From ae1ed10f25e1b6e90702976afcbff914795aa7d3 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 20 Oct 2023 00:05:17 +0200 Subject: [PATCH] Add converter support for the new formats --- novelwriter/core/tohtml.py | 9 ++++ novelwriter/core/tomd.py | 28 +++++++++---- novelwriter/core/toodt.py | 86 ++++++++++++++++++++++++++++++++++---- 3 files changed, 107 insertions(+), 16 deletions(-) diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index a4d4bb3f..93b300ee 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -131,6 +131,8 @@ class ToHtml(Tokenizer): self.FMT_I_E: "", self.FMT_D_B: "", self.FMT_D_E: "", + self.FMT_U_B: "", + self.FMT_U_E: "", } else: htmlTags = { # HTML5 (for export) @@ -140,8 +142,15 @@ class ToHtml(Tokenizer): self.FMT_I_E: "", self.FMT_D_B: "", self.FMT_D_E: "", + self.FMT_U_B: "", + self.FMT_U_E: "", } + htmlTags[self.FMT_SUP_B] = "" + htmlTags[self.FMT_SUP_E] = "" + htmlTags[self.FMT_SUB_B] = "" + htmlTags[self.FMT_SUB_E] = "" + if self._isNovel and self._genMode != self.M_PREVIEW: # For story files, we bump the titles one level up h1Cl = " class='title'" diff --git a/novelwriter/core/tomd.py b/novelwriter/core/tomd.py index ec1646ad..29dc00e5 100644 --- a/novelwriter/core/tomd.py +++ b/novelwriter/core/tomd.py @@ -38,12 +38,12 @@ class ToMarkdown(Tokenizer): """Core: Markdown Document Writer Extend the Tokenizer class to writer Markdown output. It supports - both Standard Markdown and GitHub Flavour Markdown (Extended). The - class also supports concatenating novelWriter markup files. + both Standard Markdown and Extended Markdown. The class also + supports concatenating novelWriter markup files. """ M_STD = 0 # Standard Markdown - M_GH = 1 # GitHub Markdown + M_EXT = 1 # Extended Markdown def __init__(self, project: NWProject) -> None: super().__init__(project) @@ -69,9 +69,9 @@ class ToMarkdown(Tokenizer): self._genMode = self.M_STD return - def setGitHubMarkdown(self) -> None: - """Set the converter to use GitHub Markdown formatting.""" - self._genMode = self.M_GH + def setExtendedMarkdown(self) -> None: + """Set the converter to use Extended Markdown formatting.""" + self._genMode = self.M_EXT return ## @@ -85,7 +85,7 @@ class ToMarkdown(Tokenizer): def doConvert(self) -> None: """Convert the list of text tokens into a Markdown document.""" if self._genMode == self.M_STD: - # Standard + # Standard Markdown mdTags = { self.FMT_B_B: "**", self.FMT_B_E: "**", @@ -93,9 +93,15 @@ class ToMarkdown(Tokenizer): self.FMT_I_E: "_", self.FMT_D_B: "", self.FMT_D_E: "", + self.FMT_U_B: "", + self.FMT_U_E: "", + self.FMT_SUP_B: "", + self.FMT_SUP_E: "", + self.FMT_SUB_B: "", + self.FMT_SUB_E: "", } else: - # GitHub + # Extended Markdown mdTags = { self.FMT_B_B: "**", self.FMT_B_E: "**", @@ -103,6 +109,12 @@ class ToMarkdown(Tokenizer): self.FMT_I_E: "_", self.FMT_D_B: "~~", self.FMT_D_E: "~~", + self.FMT_U_B: "", + self.FMT_U_E: "", + self.FMT_SUP_B: "^", + self.FMT_SUP_E: "^", + self.FMT_SUB_B: "~", + self.FMT_SUB_E: "~", } self._result = "" diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index c4f6b19d..1aeefb80 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -82,11 +82,17 @@ TAG_STNM = _mkTag("text", "style-name") X_BLD = 0x01 # Bold format X_ITA = 0x02 # Italic format X_DEL = 0x04 # Strikethrough format +X_UND = 0x08 # Underline format +X_SUP = 0x10 # Superscript +X_SUB = 0x20 # Subscript # Formatting Masks M_BLD = ~X_BLD M_ITA = ~X_ITA M_DEL = ~X_DEL +M_UND = ~X_UND +M_SUP = ~X_SUP +M_SUB = ~X_SUB class ToOdt(Tokenizer): @@ -386,12 +392,18 @@ class ToOdt(Tokenizer): self._result = "" # Not used, but cleared just in case odtTags = { - self.FMT_B_B: "_B", # Bold open format - self.FMT_B_E: "b_", # Bold close format - self.FMT_I_B: "I", # Italic open format - self.FMT_I_E: "i", # Italic close format - self.FMT_D_B: "_S", # Strikethrough open format - self.FMT_D_E: "s_", # Strikethrough close format + self.FMT_B_B: "B", # Bold open format + self.FMT_B_E: "b", # Bold close format + self.FMT_I_B: "I", # Italic open format + self.FMT_I_E: "i", # Italic close format + self.FMT_D_B: "S", # Strikethrough open format + self.FMT_D_E: "s", # Strikethrough close format + self.FMT_U_B: "U", # Underline open format + self.FMT_U_E: "u", # Underline close format + self.FMT_SUP_B: "P", # Superscript open format + self.FMT_SUP_E: "p", # Superscript close format + self.FMT_SUB_B: "D", # Subscript open format + self.FMT_SUB_E: "d", # Subscript close format } fmt = [] @@ -480,7 +492,10 @@ class ToOdt(Tokenizer): tFmt = " "*len(tText) for xPos, xLen, xFmt in tFormat: - tFmt = tFmt[:xPos] + odtTags[xFmt] + tFmt[xPos+xLen:] + if xFmt%2 == 0: # Even number: End + tFmt = tFmt[:xPos] + odtTags[xFmt].ljust(xLen, "_") + tFmt[xPos+xLen:] + else: # Odd number: Begin + tFmt = tFmt[:xPos] + odtTags[xFmt].rjust(xLen, "_") + tFmt[xPos+xLen:] tTxt = tText.rstrip() tFmt = tFmt[:len(tTxt)] @@ -650,6 +665,18 @@ class ToOdt(Tokenizer): xFmt |= X_DEL elif tFmt[i] == "s": xFmt &= M_DEL + elif tFmt[i] == "U": + xFmt |= X_UND + elif tFmt[i] == "u": + xFmt &= M_UND + elif tFmt[i] == "P": + xFmt |= X_SUP + elif tFmt[i] == "p": + xFmt &= M_SUP + elif tFmt[i] == "D": + xFmt |= X_SUB + elif tFmt[i] == "d": + xFmt &= M_SUB else: pErr += 1 @@ -713,6 +740,14 @@ class ToOdt(Tokenizer): if hFmt & X_DEL: newStyle.setStrikeStyle("solid") newStyle.setStrikeType("single") + if hFmt & X_UND: + newStyle.setUnderlineStyle("solid") + newStyle.setUnderlineWidth("auto") + newStyle.setUnderlineColour("font-color") + if hFmt & X_SUP: + newStyle.setTextPosition("super") + if hFmt & X_SUB: + newStyle.setTextPosition("sub") self._autoText[hFmt] = (newName, newStyle) @@ -1256,16 +1291,23 @@ class ODTTextStyle: """ VALID_WEIGHT = ["normal", "inherit", "bold"] VALID_STYLE = ["normal", "inherit", "italic"] + VALID_POS = ["super", "sub"] VALID_LSTYLE = ["none", "solid"] - VALID_LTYPE = ["none", "single", "double"] + VALID_LTYPE = ["single", "double"] + VALID_LWIDTH = ["auto"] + VALID_LCOL = ["font-color"] def __init__(self) -> None: # Text Attributes self._tAttr = { "font-weight": ["fo", None], "font-style": ["fo", None], + "text-position": ["style", None], "text-line-through-style": ["style", None], "text-line-through-type": ["style", None], + "text-underline-style": ["style", None], + "text-underline-width": ["style", None], + "text-underline-color": ["style", None], } return @@ -1287,6 +1329,13 @@ class ODTTextStyle: self._tAttr["font-style"][1] = None return + def setTextPosition(self, value: str | None) -> None: + if value in self.VALID_POS: + self._tAttr["text-position"][1] = f"{value} 58%" + else: + self._tAttr["text-position"][1] = None + return + def setStrikeStyle(self, value: str | None) -> None: if value in self.VALID_LSTYLE: self._tAttr["text-line-through-style"][1] = value @@ -1301,6 +1350,27 @@ class ODTTextStyle: self._tAttr["text-line-through-type"][1] = None return + def setUnderlineStyle(self, value: str | None) -> None: + if value in self.VALID_LSTYLE: + self._tAttr["text-underline-style"][1] = value + else: + self._tAttr["text-underline-style"][1] = None + return + + def setUnderlineWidth(self, value: str | None) -> None: + if value in self.VALID_LWIDTH: + self._tAttr["text-underline-width"][1] = value + else: + self._tAttr["text-underline-width"][1] = None + return + + def setUnderlineColour(self, value: str | None) -> None: + if value in self.VALID_LCOL: + self._tAttr["text-underline-color"][1] = value + else: + self._tAttr["text-underline-color"][1] = None + return + ## # Methods ##