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
##