Convert format codes to enums

This commit is contained in:
Veronica Berglyd Olsen
2024-10-21 23:37:27 +02:00
parent f91006631d
commit 38ca447c8a
12 changed files with 817 additions and 789 deletions
+47 -47
View File
@@ -39,7 +39,7 @@ from novelwriter import __version__
from novelwriter.common import firstFloat, xmlSubElem from novelwriter.common import firstFloat, xmlSubElem
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import T_Formats, Tokenizer from novelwriter.formats.tokenizer import BlockFmt, BlockTyp, T_Formats, TextFmt, Tokenizer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -227,75 +227,75 @@ class ToDocX(Tokenizer):
# Styles # Styles
if tStyle is not None: if tStyle is not None:
if tStyle & self.A_LEFT: if tStyle & BlockFmt.LEFT:
par.setAlignment("left") par.setAlignment("left")
elif tStyle & self.A_RIGHT: elif tStyle & BlockFmt.RIGHT:
par.setAlignment("right") par.setAlignment("right")
elif tStyle & self.A_CENTRE: elif tStyle & BlockFmt.CENTRE:
par.setAlignment("center") par.setAlignment("center")
elif tStyle & self.A_JUSTIFY: elif tStyle & BlockFmt.JUSTIFY:
par.setAlignment("both") par.setAlignment("both")
if tStyle & self.A_PBB: if tStyle & BlockFmt.PBB:
par.setPageBreakBefore(True) par.setPageBreakBefore(True)
if tStyle & self.A_PBA: if tStyle & BlockFmt.PBA:
par.setPageBreakAfter(True) par.setPageBreakAfter(True)
if tStyle & self.A_Z_BTMMRG: if tStyle & BlockFmt.Z_BTMMRG:
par.setMarginBottom(0.0) par.setMarginBottom(0.0)
if tStyle & self.A_Z_TOPMRG: if tStyle & BlockFmt.Z_TOPMRG:
par.setMarginTop(0.0) par.setMarginTop(0.0)
if tStyle & self.A_IND_T: if tStyle & BlockFmt.IND_T:
par.setIndentFirst(True) par.setIndentFirst(True)
if tStyle & self.A_IND_L: if tStyle & BlockFmt.IND_L:
par.setMarginLeft(bIndent) par.setMarginLeft(bIndent)
if tStyle & self.A_IND_R: if tStyle & BlockFmt.IND_R:
par.setMarginRight(bIndent) par.setMarginRight(bIndent)
# Process Text Types # Process Text Types
if tType == self.T_TEXT: if tType == BlockTyp.TEXT:
self._processFragments(par, S_NORM, tText, tFormat) self._processFragments(par, S_NORM, tText, tFormat)
elif tType == self.T_TITLE: elif tType == BlockTyp.TITLE:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._processFragments(par, S_TITLE, tHead, tFormat) self._processFragments(par, S_TITLE, tHead, tFormat)
elif tType == self.T_HEAD1: elif tType == BlockTyp.HEAD1:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._processFragments(par, S_HEAD1, tHead, tFormat) self._processFragments(par, S_HEAD1, tHead, tFormat)
elif tType == self.T_HEAD2: elif tType == BlockTyp.HEAD2:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._processFragments(par, S_HEAD2, tHead, tFormat) self._processFragments(par, S_HEAD2, tHead, tFormat)
elif tType == self.T_HEAD3: elif tType == BlockTyp.HEAD3:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._processFragments(par, S_HEAD3, tHead, tFormat) self._processFragments(par, S_HEAD3, tHead, tFormat)
elif tType == self.T_HEAD4: elif tType == BlockTyp.HEAD4:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._processFragments(par, S_HEAD4, tHead, tFormat) self._processFragments(par, S_HEAD4, tHead, tFormat)
elif tType == self.T_SEP: elif tType == BlockTyp.SEP:
self._processFragments(par, S_SEP, tText) self._processFragments(par, S_SEP, tText)
elif tType == self.T_SKIP: elif tType == BlockTyp.SKIP:
self._processFragments(par, S_NORM, "") self._processFragments(par, S_NORM, "")
elif tType == self.T_SYNOPSIS and self._doSynopsis: elif tType == BlockTyp.SYNOPSIS and self._doSynopsis:
tTemp, tFmt = self._formatSynopsis(tText, tFormat, True) tTemp, tFmt = self._formatSynopsis(tText, tFormat, True)
self._processFragments(par, S_META, tTemp, tFmt) self._processFragments(par, S_META, tTemp, tFmt)
elif tType == self.T_SHORT and self._doSynopsis: elif tType == BlockTyp.SHORT and self._doSynopsis:
tTemp, tFmt = self._formatSynopsis(tText, tFormat, False) tTemp, tFmt = self._formatSynopsis(tText, tFormat, False)
self._processFragments(par, S_META, tTemp, tFmt) self._processFragments(par, S_META, tTemp, tFmt)
elif tType == self.T_COMMENT and self._doComments: elif tType == BlockTyp.COMMENT and self._doComments:
tTemp, tFmt = self._formatComments(tText, tFormat) tTemp, tFmt = self._formatComments(tText, tFormat)
self._processFragments(par, S_META, tTemp, tFmt) self._processFragments(par, S_META, tTemp, tFmt)
elif tType == self.T_KEYWORD and self._doKeywords: elif tType == BlockTyp.KEYWORD and self._doKeywords:
tTemp, tFmt = self._formatKeywords(tText) tTemp, tFmt = self._formatKeywords(tText)
self._processFragments(par, S_META, tTemp, tFmt) self._processFragments(par, S_META, tTemp, tFmt)
@@ -378,7 +378,7 @@ class ToDocX(Tokenizer):
name = self._localLookup("Synopsis" if synopsis else "Short Description") name = self._localLookup("Synopsis" if synopsis else "Short Description")
shift = len(name) + 2 shift = len(name) + 2
rTxt = f"{name}: {text}" rTxt = f"{name}: {text}"
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(name) + 1, self.FMT_B_E, "")] rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(name) + 1, TextFmt.B_E, "")]
rFmt.extend((p + shift, f, d) for p, f, d in fmt) rFmt.extend((p + shift, f, d) for p, f, d in fmt)
return rTxt, rFmt return rTxt, rFmt
@@ -387,7 +387,7 @@ class ToDocX(Tokenizer):
name = self._localLookup("Comment") name = self._localLookup("Comment")
shift = len(name) + 2 shift = len(name) + 2
rTxt = f"{name}: {text}" rTxt = f"{name}: {text}"
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(name) + 1, self.FMT_B_E, "")] rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(name) + 1, TextFmt.B_E, "")]
rFmt.extend((p + shift, f, d) for p, f, d in fmt) rFmt.extend((p + shift, f, d) for p, f, d in fmt)
return rTxt, rFmt return rTxt, rFmt
@@ -398,7 +398,7 @@ class ToDocX(Tokenizer):
return "", [] return "", []
rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: " rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(rTxt) - 1, self.FMT_B_E, "")] rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(rTxt) - 1, TextFmt.B_E, "")]
if len(bits) > 1: if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY: if bits[0] == nwKeyWords.TAG_KEY:
rTxt += bits[1] rTxt += bits[1]
@@ -424,45 +424,45 @@ class ToDocX(Tokenizer):
if temp := text[fStart:fPos]: if temp := text[fStart:fPos]:
par.addContent(self._textRunToXml(temp, xFmt)) par.addContent(self._textRunToXml(temp, xFmt))
if fFmt == self.FMT_B_B: if fFmt == TextFmt.B_B:
xFmt |= X_BLD xFmt |= X_BLD
elif fFmt == self.FMT_B_E: elif fFmt == TextFmt.B_E:
xFmt &= M_BLD xFmt &= M_BLD
elif fFmt == self.FMT_I_B: elif fFmt == TextFmt.I_B:
xFmt |= X_ITA xFmt |= X_ITA
elif fFmt == self.FMT_I_E: elif fFmt == TextFmt.I_E:
xFmt &= M_ITA xFmt &= M_ITA
elif fFmt == self.FMT_D_B: elif fFmt == TextFmt.D_B:
xFmt |= X_DEL xFmt |= X_DEL
elif fFmt == self.FMT_D_E: elif fFmt == TextFmt.D_E:
xFmt &= M_DEL xFmt &= M_DEL
elif fFmt == self.FMT_U_B: elif fFmt == TextFmt.U_B:
xFmt |= X_UND xFmt |= X_UND
elif fFmt == self.FMT_U_E: elif fFmt == TextFmt.U_E:
xFmt &= M_UND xFmt &= M_UND
elif fFmt == self.FMT_M_B: elif fFmt == TextFmt.M_B:
xFmt |= X_MRK xFmt |= X_MRK
elif fFmt == self.FMT_M_E: elif fFmt == TextFmt.M_E:
xFmt &= M_MRK xFmt &= M_MRK
elif fFmt == self.FMT_SUP_B: elif fFmt == TextFmt.SUP_B:
xFmt |= X_SUP xFmt |= X_SUP
elif fFmt == self.FMT_SUP_E: elif fFmt == TextFmt.SUP_E:
xFmt &= M_SUP xFmt &= M_SUP
elif fFmt == self.FMT_SUB_B: elif fFmt == TextFmt.SUB_B:
xFmt |= X_SUB xFmt |= X_SUB
elif fFmt == self.FMT_SUB_E: elif fFmt == TextFmt.SUB_E:
xFmt &= M_SUB xFmt &= M_SUB
elif fFmt == self.FMT_DL_B: elif fFmt == TextFmt.DL_B:
xFmt |= X_DLG xFmt |= X_DLG
elif fFmt == self.FMT_DL_E: elif fFmt == TextFmt.DL_E:
xFmt &= M_DLG xFmt &= M_DLG
elif fFmt == self.FMT_ADL_B: elif fFmt == TextFmt.ADL_B:
xFmt |= X_DLA xFmt |= X_DLA
elif fFmt == self.FMT_ADL_E: elif fFmt == TextFmt.ADL_E:
xFmt &= M_DLA xFmt &= M_DLA
elif fFmt == self.FMT_FNOTE: elif fFmt == TextFmt.FNOTE:
xNode = self._generateFootnote(fData) xNode = self._generateFootnote(fData)
elif fFmt == self.FMT_STRIP: elif fFmt == TextFmt.STRIP:
pass pass
# Move pos for next pass # Move pos for next pass
+45 -43
View File
@@ -32,35 +32,37 @@ from time import time
from novelwriter.common import formatTimeStamp from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import T_Formats, Tokenizer, stripEscape from novelwriter.formats.tokenizer import (
BlockFmt, BlockTyp, T_Formats, TextFmt, Tokenizer, stripEscape
)
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS from novelwriter.types import FONT_STYLE, FONT_WEIGHTS
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Each opener tag, with the id of its corresponding closer and tag format # Each opener tag, with the id of its corresponding closer and tag format
HTML_OPENER: dict[int, tuple[int, str]] = { HTML_OPENER: dict[int, tuple[int, str]] = {
Tokenizer.FMT_B_B: (Tokenizer.FMT_B_E, "<strong>"), TextFmt.B_B: (TextFmt.B_E, "<strong>"),
Tokenizer.FMT_I_B: (Tokenizer.FMT_I_E, "<em>"), TextFmt.I_B: (TextFmt.I_E, "<em>"),
Tokenizer.FMT_D_B: (Tokenizer.FMT_D_E, "<del>"), TextFmt.D_B: (TextFmt.D_E, "<del>"),
Tokenizer.FMT_U_B: (Tokenizer.FMT_U_E, "<span style='text-decoration: underline;'>"), TextFmt.U_B: (TextFmt.U_E, "<span style='text-decoration: underline;'>"),
Tokenizer.FMT_M_B: (Tokenizer.FMT_M_E, "<mark>"), TextFmt.M_B: (TextFmt.M_E, "<mark>"),
Tokenizer.FMT_SUP_B: (Tokenizer.FMT_SUP_E, "<sup>"), TextFmt.SUP_B: (TextFmt.SUP_E, "<sup>"),
Tokenizer.FMT_SUB_B: (Tokenizer.FMT_SUB_E, "<sub>"), TextFmt.SUB_B: (TextFmt.SUB_E, "<sub>"),
Tokenizer.FMT_DL_B: (Tokenizer.FMT_DL_E, "<span class='dialog'>"), TextFmt.DL_B: (TextFmt.DL_E, "<span class='dialog'>"),
Tokenizer.FMT_ADL_B: (Tokenizer.FMT_ADL_E, "<span class='altdialog'>"), TextFmt.ADL_B: (TextFmt.ADL_E, "<span class='altdialog'>"),
} }
# Each closer tag, with the id of its corresponding opener and tag format # Each closer tag, with the id of its corresponding opener and tag format
HTML_CLOSER: dict[int, tuple[int, str]] = { HTML_CLOSER: dict[int, tuple[int, str]] = {
Tokenizer.FMT_B_E: (Tokenizer.FMT_B_B, "</strong>"), TextFmt.B_E: (TextFmt.B_B, "</strong>"),
Tokenizer.FMT_I_E: (Tokenizer.FMT_I_B, "</em>"), TextFmt.I_E: (TextFmt.I_B, "</em>"),
Tokenizer.FMT_D_E: (Tokenizer.FMT_D_B, "</del>"), TextFmt.D_E: (TextFmt.D_B, "</del>"),
Tokenizer.FMT_U_E: (Tokenizer.FMT_U_B, "</span>"), TextFmt.U_E: (TextFmt.U_B, "</span>"),
Tokenizer.FMT_M_E: (Tokenizer.FMT_M_B, "</mark>"), TextFmt.M_E: (TextFmt.M_B, "</mark>"),
Tokenizer.FMT_SUP_E: (Tokenizer.FMT_SUP_B, "</sup>"), TextFmt.SUP_E: (TextFmt.SUP_B, "</sup>"),
Tokenizer.FMT_SUB_E: (Tokenizer.FMT_SUB_B, "</sub>"), TextFmt.SUB_E: (TextFmt.SUB_B, "</sub>"),
Tokenizer.FMT_DL_E: (Tokenizer.FMT_DL_B, "</span>"), TextFmt.DL_E: (TextFmt.DL_B, "</span>"),
Tokenizer.FMT_ADL_E: (Tokenizer.FMT_ADL_B, "</span>"), TextFmt.ADL_E: (TextFmt.ADL_B, "</span>"),
} }
# Empty HTML tag record # Empty HTML tag record
@@ -183,30 +185,30 @@ class ToHtml(Tokenizer):
# Styles # Styles
aStyle = [] aStyle = []
if tStyle is not None and self._cssStyles: if tStyle is not None and self._cssStyles:
if tStyle & self.A_LEFT: if tStyle & BlockFmt.LEFT:
aStyle.append("text-align: left;") aStyle.append("text-align: left;")
elif tStyle & self.A_RIGHT: elif tStyle & BlockFmt.RIGHT:
aStyle.append("text-align: right;") aStyle.append("text-align: right;")
elif tStyle & self.A_CENTRE: elif tStyle & BlockFmt.CENTRE:
aStyle.append("text-align: center;") aStyle.append("text-align: center;")
elif tStyle & self.A_JUSTIFY: elif tStyle & BlockFmt.JUSTIFY:
aStyle.append("text-align: justify;") aStyle.append("text-align: justify;")
if tStyle & self.A_PBB: if tStyle & BlockFmt.PBB:
aStyle.append("page-break-before: always;") aStyle.append("page-break-before: always;")
if tStyle & self.A_PBA: if tStyle & BlockFmt.PBA:
aStyle.append("page-break-after: always;") aStyle.append("page-break-after: always;")
if tStyle & self.A_Z_BTMMRG: if tStyle & BlockFmt.Z_BTMMRG:
aStyle.append("margin-bottom: 0;") aStyle.append("margin-bottom: 0;")
if tStyle & self.A_Z_TOPMRG: if tStyle & BlockFmt.Z_TOPMRG:
aStyle.append("margin-top: 0;") aStyle.append("margin-top: 0;")
if tStyle & self.A_IND_L: if tStyle & BlockFmt.IND_L:
aStyle.append(f"margin-left: {self._blockIndent:.2f}em;") aStyle.append(f"margin-left: {self._blockIndent:.2f}em;")
if tStyle & self.A_IND_R: if tStyle & BlockFmt.IND_R:
aStyle.append(f"margin-right: {self._blockIndent:.2f}em;") aStyle.append(f"margin-right: {self._blockIndent:.2f}em;")
if tStyle & self.A_IND_T: if tStyle & BlockFmt.IND_T:
aStyle.append(f"text-indent: {self._firstWidth:.2f}em;") aStyle.append(f"text-indent: {self._firstWidth:.2f}em;")
if aStyle: if aStyle:
@@ -221,45 +223,45 @@ class ToHtml(Tokenizer):
aNm = "" aNm = ""
# Process Text Type # Process Text Type
if tType == self.T_TEXT: if tType == BlockTyp.TEXT:
lines.append(f"<p{hStyle}>{self._formatText(tText, tFormat)}</p>\n") lines.append(f"<p{hStyle}>{self._formatText(tText, tFormat)}</p>\n")
elif tType == self.T_TITLE: elif tType == BlockTyp.TITLE:
tHead = tText.replace(nwHeadFmt.BR, "<br>") tHead = tText.replace(nwHeadFmt.BR, "<br>")
lines.append(f"<h1 class='title'{hStyle}>{aNm}{tHead}</h1>\n") lines.append(f"<h1 class='title'{hStyle}>{aNm}{tHead}</h1>\n")
elif tType == self.T_HEAD1: elif tType == BlockTyp.HEAD1:
tHead = tText.replace(nwHeadFmt.BR, "<br>") tHead = tText.replace(nwHeadFmt.BR, "<br>")
lines.append(f"<{h1}{h1Cl}{hStyle}>{aNm}{tHead}</{h1}>\n") lines.append(f"<{h1}{h1Cl}{hStyle}>{aNm}{tHead}</{h1}>\n")
elif tType == self.T_HEAD2: elif tType == BlockTyp.HEAD2:
tHead = tText.replace(nwHeadFmt.BR, "<br>") tHead = tText.replace(nwHeadFmt.BR, "<br>")
lines.append(f"<{h2}{hStyle}>{aNm}{tHead}</{h2}>\n") lines.append(f"<{h2}{hStyle}>{aNm}{tHead}</{h2}>\n")
elif tType == self.T_HEAD3: elif tType == BlockTyp.HEAD3:
tHead = tText.replace(nwHeadFmt.BR, "<br>") tHead = tText.replace(nwHeadFmt.BR, "<br>")
lines.append(f"<{h3}{hStyle}>{aNm}{tHead}</{h3}>\n") lines.append(f"<{h3}{hStyle}>{aNm}{tHead}</{h3}>\n")
elif tType == self.T_HEAD4: elif tType == BlockTyp.HEAD4:
tHead = tText.replace(nwHeadFmt.BR, "<br>") tHead = tText.replace(nwHeadFmt.BR, "<br>")
lines.append(f"<{h4}{hStyle}>{aNm}{tHead}</{h4}>\n") lines.append(f"<{h4}{hStyle}>{aNm}{tHead}</{h4}>\n")
elif tType == self.T_SEP: elif tType == BlockTyp.SEP:
lines.append(f"<p class='sep'{hStyle}>{tText}</p>\n") lines.append(f"<p class='sep'{hStyle}>{tText}</p>\n")
elif tType == self.T_SKIP: elif tType == BlockTyp.SKIP:
lines.append(f"<p class='skip'{hStyle}>&nbsp;</p>\n") lines.append(f"<p class='skip'{hStyle}>&nbsp;</p>\n")
elif tType == self.T_SYNOPSIS and self._doSynopsis: elif tType == BlockTyp.SYNOPSIS and self._doSynopsis:
lines.append(self._formatSynopsis(self._formatText(tText, tFormat), True)) lines.append(self._formatSynopsis(self._formatText(tText, tFormat), True))
elif tType == self.T_SHORT and self._doSynopsis: elif tType == BlockTyp.SHORT and self._doSynopsis:
lines.append(self._formatSynopsis(self._formatText(tText, tFormat), False)) lines.append(self._formatSynopsis(self._formatText(tText, tFormat), False))
elif tType == self.T_COMMENT and self._doComments: elif tType == BlockTyp.COMMENT and self._doComments:
lines.append(self._formatComments(self._formatText(tText, tFormat))) lines.append(self._formatComments(self._formatText(tText, tFormat)))
elif tType == self.T_KEYWORD and self._doKeywords: elif tType == BlockTyp.KEYWORD and self._doKeywords:
tag, text = self._formatKeywords(tText) tag, text = self._formatKeywords(tText)
kClass = f" class='meta meta-{tag}'" if tag else "" kClass = f" class='meta meta-{tag}'" if tag else ""
tTemp = f"<p{kClass}{hStyle}>{text}</p>\n" tTemp = f"<p{kClass}{hStyle}>{text}</p>\n"
@@ -469,7 +471,7 @@ class ToHtml(Tokenizer):
if state.get(m[0], False): if state.get(m[0], False):
tags.append((pos, m[1])) tags.append((pos, m[1]))
state[m[0]] = False state[m[0]] = False
elif fmt == self.FMT_FNOTE: elif fmt == TextFmt.FNOTE:
if data in self._footnotes: if data in self._footnotes:
index = len(self._usedNotes) + 1 index = len(self._usedNotes) + 1
self._usedNotes[data] = index self._usedNotes[data] = index
+170 -153
View File
@@ -29,6 +29,7 @@ import logging
import re import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from enum import Flag, IntEnum
from functools import partial from functools import partial
from pathlib import Path from pathlib import Path
from time import time from time import time
@@ -51,10 +52,6 @@ logger = logging.getLogger(__name__)
ESCAPES = {r"\*": "*", r"\~": "~", r"\_": "_", r"\[": "[", r"\]": "]", r"\ ": ""} ESCAPES = {r"\*": "*", r"\~": "~", r"\_": "_", r"\[": "[", r"\]": "]", r"\ ": ""}
RX_ESC = re.compile("|".join([re.escape(k) for k in ESCAPES.keys()]), flags=re.DOTALL) RX_ESC = re.compile("|".join([re.escape(k) for k in ESCAPES.keys()]), flags=re.DOTALL)
T_Formats = list[tuple[int, int, str]]
T_Comment = tuple[str, T_Formats]
T_Token = tuple[int, int, str, T_Formats, int]
def stripEscape(text: str) -> str: def stripEscape(text: str) -> str:
"""Strip escaped Markdown characters from paragraph text.""" """Strip escaped Markdown characters from paragraph text."""
@@ -63,6 +60,68 @@ def stripEscape(text: str) -> str:
return text return text
class TextFmt(IntEnum):
B_B = 1 # Begin bold
B_E = 2 # End bold
I_B = 3 # Begin italics
I_E = 4 # End italics
D_B = 5 # Begin strikeout
D_E = 6 # End strikeout
U_B = 7 # Begin underline
U_E = 8 # End underline
M_B = 9 # Begin mark
M_E = 10 # End mark
SUP_B = 11 # Begin superscript
SUP_E = 12 # End superscript
SUB_B = 13 # Begin subscript
SUB_E = 14 # End subscript
DL_B = 15 # Begin dialogue
DL_E = 16 # End dialogue
ADL_B = 17 # Begin alt dialogue
ADL_E = 18 # End alt dialogue
FNOTE = 19 # Footnote marker
STRIP = 20 # Strip the format code
class BlockTyp(IntEnum):
EMPTY = 1 # Empty line (new paragraph)
SYNOPSIS = 2 # Synopsis comment
SHORT = 3 # Short description comment
COMMENT = 4 # Comment line
KEYWORD = 5 # Command line
TITLE = 6 # Title
HEAD1 = 7 # Heading 1
HEAD2 = 8 # Heading 2
HEAD3 = 9 # Heading 3
HEAD4 = 10 # Heading 4
TEXT = 11 # Text line
SEP = 12 # Scene separator
SKIP = 13 # Paragraph break
class BlockFmt(Flag):
NONE = 0x0000 # No special style
LEFT = 0x0001 # Left aligned
RIGHT = 0x0002 # Right aligned
CENTRE = 0x0004 # Centred
JUSTIFY = 0x0008 # Justified
PBB = 0x0010 # Page break before
PBA = 0x0020 # Page break after
Z_TOPMRG = 0x0040 # Zero top margin
Z_BTMMRG = 0x0080 # Zero bottom margin
IND_L = 0x0100 # Left indentation
IND_R = 0x0200 # Right indentation
IND_T = 0x0400 # Text indentation
T_Formats = list[tuple[int, TextFmt, str]]
T_Comment = tuple[str, T_Formats]
T_Token = tuple[BlockTyp, int, str, T_Formats, BlockFmt]
class Tokenizer(ABC): class Tokenizer(ABC):
"""Core: Text Tokenizer Abstract Base Class """Core: Text Tokenizer Abstract Base Class
@@ -72,64 +131,18 @@ class Tokenizer(ABC):
subclasses. subclasses.
""" """
# In-Text Format
FMT_B_B = 1 # Begin bold
FMT_B_E = 2 # End bold
FMT_I_B = 3 # Begin italics
FMT_I_E = 4 # End italics
FMT_D_B = 5 # Begin strikeout
FMT_D_E = 6 # End strikeout
FMT_U_B = 7 # Begin underline
FMT_U_E = 8 # End underline
FMT_M_B = 9 # Begin mark
FMT_M_E = 10 # End mark
FMT_SUP_B = 11 # Begin superscript
FMT_SUP_E = 12 # End superscript
FMT_SUB_B = 13 # Begin subscript
FMT_SUB_E = 14 # End subscript
FMT_DL_B = 15 # Begin dialogue
FMT_DL_E = 16 # End dialogue
FMT_ADL_B = 17 # Begin alt dialogue
FMT_ADL_E = 18 # End alt dialogue
FMT_FNOTE = 19 # Footnote marker
FMT_STRIP = 20 # Strip the format code
# Block Type
T_EMPTY = 1 # Empty line (new paragraph)
T_SYNOPSIS = 2 # Synopsis comment
T_SHORT = 3 # Short description comment
T_COMMENT = 4 # Comment line
T_KEYWORD = 5 # Command line
T_TITLE = 6 # Title
T_HEAD1 = 7 # Heading 1
T_HEAD2 = 8 # Heading 2
T_HEAD3 = 9 # Heading 3
T_HEAD4 = 10 # Heading 4
T_TEXT = 11 # Text line
T_SEP = 12 # Scene separator
T_SKIP = 13 # Paragraph break
# Block Style
A_NONE = 0x0000 # No special style
A_LEFT = 0x0001 # Left aligned
A_RIGHT = 0x0002 # Right aligned
A_CENTRE = 0x0004 # Centred
A_JUSTIFY = 0x0008 # Justified
A_PBB = 0x0010 # Page break before
A_PBA = 0x0020 # Page break after
A_Z_TOPMRG = 0x0040 # Zero top margin
A_Z_BTMMRG = 0x0080 # Zero bottom margin
A_IND_L = 0x0100 # Left indentation
A_IND_R = 0x0200 # Right indentation
A_IND_T = 0x0400 # Text indentation
# Masks # Masks
M_ALIGNED = A_LEFT | A_RIGHT | A_CENTRE | A_JUSTIFY M_ALIGNED = BlockFmt.LEFT | BlockFmt.RIGHT | BlockFmt.CENTRE | BlockFmt.JUSTIFY
# Lookups # Lookups
L_HEADINGS = [T_TITLE, T_HEAD1, T_HEAD2, T_HEAD3, T_HEAD4] L_HEADINGS = [
L_SKIP_INDENT = [T_TITLE, T_HEAD1, T_HEAD2, T_HEAD2, T_HEAD3, T_HEAD4, T_SEP, T_SKIP] BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4,
L_SUMMARY = [T_SYNOPSIS, T_SHORT] ]
L_SKIP_INDENT = [
BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD2, BlockTyp.HEAD3,
BlockTyp.HEAD4, BlockTyp.SEP, BlockTyp.SKIP,
]
L_SUMMARY = [BlockTyp.SYNOPSIS, BlockTyp.SHORT]
def __init__(self, project: NWProject) -> None: def __init__(self, project: NWProject) -> None:
@@ -197,10 +210,10 @@ class Tokenizer(ABC):
self._linkHeadings = False # Add an anchor before headings self._linkHeadings = False # Add an anchor before headings
self._titleStyle = self.A_CENTRE | self.A_PBB self._titleStyle = BlockFmt.CENTRE | BlockFmt.PBB
self._partStyle = self.A_CENTRE | self.A_PBB self._partStyle = BlockFmt.CENTRE | BlockFmt.PBB
self._chapterStyle = self.A_PBB self._chapterStyle = BlockFmt.PBB
self._sceneStyle = self.A_NONE self._sceneStyle = BlockFmt.NONE
# Instance Variables # Instance Variables
self._hFormatter = HeadingFormatter(self._project) self._hFormatter = HeadingFormatter(self._project)
@@ -220,24 +233,24 @@ class Tokenizer(ABC):
# Format RegEx # Format RegEx
self._rxMarkdown = [ self._rxMarkdown = [
(REGEX_PATTERNS.markdownItalic, [0, self.FMT_I_B, 0, self.FMT_I_E]), (REGEX_PATTERNS.markdownItalic, [0, TextFmt.I_B, 0, TextFmt.I_E]),
(REGEX_PATTERNS.markdownBold, [0, self.FMT_B_B, 0, self.FMT_B_E]), (REGEX_PATTERNS.markdownBold, [0, TextFmt.B_B, 0, TextFmt.B_E]),
(REGEX_PATTERNS.markdownStrike, [0, self.FMT_D_B, 0, self.FMT_D_E]), (REGEX_PATTERNS.markdownStrike, [0, TextFmt.D_B, 0, TextFmt.D_E]),
] ]
self._rxShortCodes = REGEX_PATTERNS.shortcodePlain self._rxShortCodes = REGEX_PATTERNS.shortcodePlain
self._rxShortCodeVals = REGEX_PATTERNS.shortcodeValue self._rxShortCodeVals = REGEX_PATTERNS.shortcodeValue
self._shortCodeFmt = { self._shortCodeFmt = {
nwShortcode.ITALIC_O: self.FMT_I_B, nwShortcode.ITALIC_C: self.FMT_I_E, nwShortcode.ITALIC_O: TextFmt.I_B, nwShortcode.ITALIC_C: TextFmt.I_E,
nwShortcode.BOLD_O: self.FMT_B_B, nwShortcode.BOLD_C: self.FMT_B_E, nwShortcode.BOLD_O: TextFmt.B_B, nwShortcode.BOLD_C: TextFmt.B_E,
nwShortcode.STRIKE_O: self.FMT_D_B, nwShortcode.STRIKE_C: self.FMT_D_E, nwShortcode.STRIKE_O: TextFmt.D_B, nwShortcode.STRIKE_C: TextFmt.D_E,
nwShortcode.ULINE_O: self.FMT_U_B, nwShortcode.ULINE_C: self.FMT_U_E, nwShortcode.ULINE_O: TextFmt.U_B, nwShortcode.ULINE_C: TextFmt.U_E,
nwShortcode.MARK_O: self.FMT_M_B, nwShortcode.MARK_C: self.FMT_M_E, nwShortcode.MARK_O: TextFmt.M_B, nwShortcode.MARK_C: TextFmt.M_E,
nwShortcode.SUP_O: self.FMT_SUP_B, nwShortcode.SUP_C: self.FMT_SUP_E, nwShortcode.SUP_O: TextFmt.SUP_B, nwShortcode.SUP_C: TextFmt.SUP_E,
nwShortcode.SUB_O: self.FMT_SUB_B, nwShortcode.SUB_C: self.FMT_SUB_E, nwShortcode.SUB_O: TextFmt.SUB_B, nwShortcode.SUB_C: TextFmt.SUB_E,
} }
self._shortCodeVals = { self._shortCodeVals = {
nwShortcode.FOOTNOTE_B: self.FMT_FNOTE, nwShortcode.FOOTNOTE_B: TextFmt.FNOTE,
} }
self._rxDialogue: list[tuple[re.Pattern, int, int]] = [] self._rxDialogue: list[tuple[re.Pattern, int, int]] = []
@@ -316,28 +329,32 @@ class Tokenizer(ABC):
def setTitleStyle(self, center: bool, pageBreak: bool) -> None: def setTitleStyle(self, center: bool, pageBreak: bool) -> None:
"""Set the title heading style.""" """Set the title heading style."""
self._titleStyle = ( self._titleStyle = (
(self.A_CENTRE if center else self.A_NONE) | (self.A_PBB if pageBreak else self.A_NONE) (BlockFmt.CENTRE if center else BlockFmt.NONE)
| (BlockFmt.PBB if pageBreak else BlockFmt.NONE)
) )
return return
def setPartitionStyle(self, center: bool, pageBreak: bool) -> None: def setPartitionStyle(self, center: bool, pageBreak: bool) -> None:
"""Set the partition heading style.""" """Set the partition heading style."""
self._partStyle = ( self._partStyle = (
(self.A_CENTRE if center else self.A_NONE) | (self.A_PBB if pageBreak else self.A_NONE) (BlockFmt.CENTRE if center else BlockFmt.NONE)
| (BlockFmt.PBB if pageBreak else BlockFmt.NONE)
) )
return return
def setChapterStyle(self, center: bool, pageBreak: bool) -> None: def setChapterStyle(self, center: bool, pageBreak: bool) -> None:
"""Set the chapter heading style.""" """Set the chapter heading style."""
self._chapterStyle = ( self._chapterStyle = (
(self.A_CENTRE if center else self.A_NONE) | (self.A_PBB if pageBreak else self.A_NONE) (BlockFmt.CENTRE if center else BlockFmt.NONE)
| (BlockFmt.PBB if pageBreak else BlockFmt.NONE)
) )
return return
def setSceneStyle(self, center: bool, pageBreak: bool) -> None: def setSceneStyle(self, center: bool, pageBreak: bool) -> None:
"""Set the scene heading style.""" """Set the scene heading style."""
self._sceneStyle = ( self._sceneStyle = (
(self.A_CENTRE if center else self.A_NONE) | (self.A_PBB if pageBreak else self.A_NONE) (BlockFmt.CENTRE if center else BlockFmt.NONE)
| (BlockFmt.PBB if pageBreak else BlockFmt.NONE)
) )
return return
@@ -383,19 +400,19 @@ class Tokenizer(ABC):
if state: if state:
if CONFIG.dialogStyle > 0: if CONFIG.dialogStyle > 0:
self._rxDialogue.append(( self._rxDialogue.append((
REGEX_PATTERNS.dialogStyle, self.FMT_DL_B, self.FMT_DL_E REGEX_PATTERNS.dialogStyle, TextFmt.DL_B, TextFmt.DL_E
)) ))
if CONFIG.dialogLine: if CONFIG.dialogLine:
self._rxDialogue.append(( self._rxDialogue.append((
REGEX_PATTERNS.dialogLine, self.FMT_DL_B, self.FMT_DL_E REGEX_PATTERNS.dialogLine, TextFmt.DL_B, TextFmt.DL_E
)) ))
if CONFIG.narratorBreak: if CONFIG.narratorBreak:
self._rxDialogue.append(( self._rxDialogue.append((
REGEX_PATTERNS.narratorBreak, self.FMT_DL_E, self.FMT_DL_B REGEX_PATTERNS.narratorBreak, TextFmt.DL_E, TextFmt.DL_B
)) ))
if CONFIG.altDialogOpen and CONFIG.altDialogClose: if CONFIG.altDialogOpen and CONFIG.altDialogClose:
self._rxDialogue.append(( self._rxDialogue.append((
REGEX_PATTERNS.altDialogStyle, self.FMT_ADL_B, self.FMT_ADL_E REGEX_PATTERNS.altDialogStyle, TextFmt.ADL_B, TextFmt.ADL_E
)) ))
return return
@@ -494,16 +511,16 @@ class Tokenizer(ABC):
if (tItem := self._project.tree[tHandle]) and tItem.isRootType(): if (tItem := self._project.tree[tHandle]) and tItem.isRootType():
self._handle = tHandle self._handle = tHandle
if self._isFirst: if self._isFirst:
textAlign = self.A_CENTRE textAlign = BlockFmt.CENTRE
self._isFirst = False self._isFirst = False
else: else:
textAlign = self.A_PBB | self.A_CENTRE textAlign = BlockFmt.PBB | BlockFmt.CENTRE
trNotes = self._localLookup("Notes") trNotes = self._localLookup("Notes")
title = f"{trNotes}: {tItem.itemName}" title = f"{trNotes}: {tItem.itemName}"
self._tokens = [] self._tokens = []
self._tokens.append(( self._tokens.append((
self.T_TITLE, 1, title, [], textAlign BlockTyp.TITLE, 1, title, [], textAlign
)) ))
if self._keepRaw: if self._keepRaw:
self._markdown.append(f"#! {title}\n\n") self._markdown.append(f"#! {title}\n\n")
@@ -548,11 +565,11 @@ class Tokenizer(ABC):
The format of the token list is an entry with a five-tuple for The format of the token list is an entry with a five-tuple for
each line in the file. The tuple is as follows: each line in the file. The tuple is as follows:
1: The type of the block, self.T_* 1: The type of the block, BlockType.*
2: The heading number under which the text is placed 2: The heading number under which the text is placed
3: The text content of the block, without leading tags 3: The text content of the block, without leading tags
4: The internal formatting map of the text, self.FMT_* 4: The internal formatting map of the text, TxtFmt.*
5: The style of the block, self.A_* 5: The style of the block, BlockFmt.*
""" """
if self._isNovel: if self._isNovel:
self._hFormatter.setHandle(self._handle) self._hFormatter.setHandle(self._handle)
@@ -568,7 +585,7 @@ class Tokenizer(ABC):
# Check for blank lines # Check for blank lines
if len(sLine) == 0: if len(sLine) == 0:
tokens.append(( tokens.append((
self.T_EMPTY, nHead, "", [], self.A_NONE BlockTyp.EMPTY, nHead, "", [], BlockFmt.NONE
)) ))
if self._keepRaw: if self._keepRaw:
tmpMarkdown.append("\n") tmpMarkdown.append("\n")
@@ -576,10 +593,10 @@ class Tokenizer(ABC):
continue continue
if breakNext: if breakNext:
sAlign = self.A_PBB sAlign = BlockFmt.PBB
breakNext = False breakNext = False
else: else:
sAlign = self.A_NONE sAlign = BlockFmt.NONE
# Check Line Format # Check Line Format
# ================= # =================
@@ -597,7 +614,7 @@ class Tokenizer(ABC):
elif sLine == "[vspace]": elif sLine == "[vspace]":
tokens.append( tokens.append(
(self.T_SKIP, nHead, "", [], sAlign) (BlockTyp.SKIP, nHead, "", [], sAlign)
) )
continue continue
@@ -605,11 +622,11 @@ class Tokenizer(ABC):
nSkip = checkInt(sLine[8:-1], 0) nSkip = checkInt(sLine[8:-1], 0)
if nSkip >= 1: if nSkip >= 1:
tokens.append( tokens.append(
(self.T_SKIP, nHead, "", [], sAlign) (BlockTyp.SKIP, nHead, "", [], sAlign)
) )
if nSkip > 1: if nSkip > 1:
tokens += (nSkip - 1) * [ tokens += (nSkip - 1) * [
(self.T_SKIP, nHead, "", [], self.A_NONE) (BlockTyp.SKIP, nHead, "", [], BlockFmt.NONE)
] ]
continue continue
@@ -623,32 +640,32 @@ class Tokenizer(ABC):
continue continue
if self._doJustify and not sAlign & self.M_ALIGNED: if self._doJustify and not sAlign & self.M_ALIGNED:
sAlign |= self.A_JUSTIFY sAlign |= BlockFmt.JUSTIFY
cStyle, cKey, cText, _, _ = processComment(aLine) cStyle, cKey, cText, _, _ = processComment(aLine)
if cStyle == nwComment.SYNOPSIS: if cStyle == nwComment.SYNOPSIS:
tLine, tFmt = self._extractFormats(cText) tLine, tFmt = self._extractFormats(cText)
tokens.append(( tokens.append((
self.T_SYNOPSIS, nHead, tLine, tFmt, sAlign BlockTyp.SYNOPSIS, nHead, tLine, tFmt, sAlign
)) ))
if self._doSynopsis and self._keepRaw: if self._doSynopsis and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif cStyle == nwComment.SHORT: elif cStyle == nwComment.SHORT:
tLine, tFmt = self._extractFormats(cText) tLine, tFmt = self._extractFormats(cText)
tokens.append(( tokens.append((
self.T_SHORT, nHead, tLine, tFmt, sAlign BlockTyp.SHORT, nHead, tLine, tFmt, sAlign
)) ))
if self._doSynopsis and self._keepRaw: if self._doSynopsis and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif cStyle == nwComment.FOOTNOTE: elif cStyle == nwComment.FOOTNOTE:
tLine, tFmt = self._extractFormats(cText, skip=self.FMT_FNOTE) tLine, tFmt = self._extractFormats(cText, skip=TextFmt.FNOTE)
self._footnotes[f"{tHandle}:{cKey}"] = (tLine, tFmt) self._footnotes[f"{tHandle}:{cKey}"] = (tLine, tFmt)
if self._keepRaw: if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
else: else:
tLine, tFmt = self._extractFormats(cText) tLine, tFmt = self._extractFormats(cText)
tokens.append(( tokens.append((
self.T_COMMENT, nHead, tLine, tFmt, sAlign BlockTyp.COMMENT, nHead, tLine, tFmt, sAlign
)) ))
if self._doComments and self._keepRaw: if self._doComments and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
@@ -665,7 +682,7 @@ class Tokenizer(ABC):
and bits[0] not in self._skipKeywords and bits[0] not in self._skipKeywords
): ):
tokens.append(( tokens.append((
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign BlockTyp.KEYWORD, nHead, aLine[1:].strip(), [], sAlign
)) ))
if self._doKeywords and self._keepRaw: if self._doKeywords and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
@@ -683,14 +700,14 @@ class Tokenizer(ABC):
nHead += 1 nHead += 1
tText = aLine[2:].strip() tText = aLine[2:].strip()
tType = self.T_HEAD1 if isPlain else self.T_TITLE tType = BlockTyp.HEAD1 if isPlain else BlockTyp.TITLE
tStyle = self.A_NONE if isPlain else self._titleStyle tStyle = BlockFmt.NONE if isPlain else self._titleStyle
sHide = self._hidePart if isPlain else False sHide = self._hidePart if isPlain else False
if self._isNovel: if self._isNovel:
if sHide: if sHide:
tText = "" tText = ""
tType = self.T_EMPTY tType = BlockTyp.EMPTY
tStyle = self.A_NONE tStyle = BlockFmt.NONE
elif isPlain: elif isPlain:
tText = self._hFormatter.apply(self._fmtPart, tText, nHead) tText = self._hFormatter.apply(self._fmtPart, tText, nHead)
tStyle = self._partStyle tStyle = self._partStyle
@@ -719,8 +736,8 @@ class Tokenizer(ABC):
nHead += 1 nHead += 1
tText = aLine[3:].strip() tText = aLine[3:].strip()
tType = self.T_HEAD2 tType = BlockTyp.HEAD2
tStyle = self.A_NONE tStyle = BlockFmt.NONE
sHide = self._hideChapter if isPlain else self._hideUnNum sHide = self._hideChapter if isPlain else self._hideUnNum
tFormat = self._fmtChapter if isPlain else self._fmtUnNum tFormat = self._fmtChapter if isPlain else self._fmtUnNum
if self._isNovel: if self._isNovel:
@@ -728,7 +745,7 @@ class Tokenizer(ABC):
self._hFormatter.incChapter() self._hFormatter.incChapter()
if sHide: if sHide:
tText = "" tText = ""
tType = self.T_EMPTY tType = BlockTyp.EMPTY
else: else:
tText = self._hFormatter.apply(tFormat, tText, nHead) tText = self._hFormatter.apply(tFormat, tText, nHead)
tStyle = self._chapterStyle tStyle = self._chapterStyle
@@ -756,24 +773,24 @@ class Tokenizer(ABC):
nHead += 1 nHead += 1
tText = aLine[4:].strip() tText = aLine[4:].strip()
tType = self.T_HEAD3 tType = BlockTyp.HEAD3
tStyle = self.A_NONE tStyle = BlockFmt.NONE
sHide = self._hideScene if isPlain else self._hideHScene sHide = self._hideScene if isPlain else self._hideHScene
tFormat = self._fmtScene if isPlain else self._fmtHScene tFormat = self._fmtScene if isPlain else self._fmtHScene
if self._isNovel: if self._isNovel:
self._hFormatter.incScene() self._hFormatter.incScene()
if sHide: if sHide:
tText = "" tText = ""
tType = self.T_EMPTY tType = BlockTyp.EMPTY
else: else:
tText = self._hFormatter.apply(tFormat, tText, nHead) tText = self._hFormatter.apply(tFormat, tText, nHead)
tStyle = self._sceneStyle tStyle = self._sceneStyle
if tText == "": # Empty Format if tText == "": # Empty Format
tType = self.T_EMPTY if self._noSep else self.T_SKIP tType = BlockTyp.EMPTY if self._noSep else BlockTyp.SKIP
elif tText == tFormat: # Static Format elif tText == tFormat: # Static Format
tText = "" if self._noSep else tText tText = "" if self._noSep else tText
tType = self.T_EMPTY if self._noSep else self.T_SEP tType = BlockTyp.EMPTY if self._noSep else BlockTyp.SEP
tStyle = self.A_NONE if self._noSep else self.A_CENTRE tStyle = BlockFmt.NONE if self._noSep else BlockFmt.CENTRE
self._noSep = False self._noSep = False
tokens.append(( tokens.append((
@@ -792,19 +809,19 @@ class Tokenizer(ABC):
nHead += 1 nHead += 1
tText = aLine[5:].strip() tText = aLine[5:].strip()
tType = self.T_HEAD4 tType = BlockTyp.HEAD4
tStyle = self.A_NONE tStyle = BlockFmt.NONE
if self._isNovel: if self._isNovel:
if self._hideSection: if self._hideSection:
tText = "" tText = ""
tType = self.T_EMPTY tType = BlockTyp.EMPTY
else: else:
tText = self._hFormatter.apply(self._fmtSection, tText, nHead) tText = self._hFormatter.apply(self._fmtSection, tText, nHead)
if tText == "": # Empty Format if tText == "": # Empty Format
tType = self.T_SKIP tType = BlockTyp.SKIP
elif tText == self._fmtSection: # Static Format elif tText == self._fmtSection: # Static Format
tType = self.T_SEP tType = BlockTyp.SEP
tStyle = self.A_CENTRE tStyle = BlockFmt.CENTRE
tokens.append(( tokens.append((
tType, nHead, tText, [], tStyle tType, nHead, tText, [], tStyle
@@ -841,21 +858,21 @@ class Tokenizer(ABC):
aLine = aLine[:-1].rstrip(" ") aLine = aLine[:-1].rstrip(" ")
if alnLeft and alnRight: if alnLeft and alnRight:
sAlign |= self.A_CENTRE sAlign |= BlockFmt.CENTRE
elif alnLeft: elif alnLeft:
sAlign |= self.A_LEFT sAlign |= BlockFmt.LEFT
elif alnRight: elif alnRight:
sAlign |= self.A_RIGHT sAlign |= BlockFmt.RIGHT
if indLeft: if indLeft:
sAlign |= self.A_IND_L sAlign |= BlockFmt.IND_L
if indRight: if indRight:
sAlign |= self.A_IND_R sAlign |= BlockFmt.IND_R
# Process formats # Process formats
tLine, tFmt = self._extractFormats(aLine, hDialog=self._isNovel) tLine, tFmt = self._extractFormats(aLine, hDialog=self._isNovel)
tokens.append(( tokens.append((
self.T_TEXT, nHead, tLine, tFmt, sAlign BlockTyp.TEXT, nHead, tLine, tFmt, sAlign
)) ))
if self._keepRaw: if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
@@ -866,15 +883,15 @@ class Tokenizer(ABC):
# Make sure the token array doesn't start with a page break # Make sure the token array doesn't start with a page break
# on the very first page, adding a blank first page. # on the very first page, adding a blank first page.
if tokens[0][4] & self.A_PBB: if tokens[0][4] & BlockFmt.PBB:
cToken = tokens[0] cToken = tokens[0]
tokens[0] = ( tokens[0] = (
cToken[0], cToken[1], cToken[2], cToken[3], cToken[4] & ~self.A_PBB cToken[0], cToken[1], cToken[2], cToken[3], cToken[4] & ~BlockFmt.PBB
) )
# Always add an empty line at the end of the file # Always add an empty line at the end of the file
tokens.append(( tokens.append((
self.T_EMPTY, nHead, "", [], self.A_NONE BlockTyp.EMPTY, nHead, "", [], BlockFmt.NONE
)) ))
if self._keepRaw: if self._keepRaw:
tmpMarkdown.append("\n") tmpMarkdown.append("\n")
@@ -888,8 +905,8 @@ class Tokenizer(ABC):
# meta data lines for formats that has spacing. # meta data lines for formats that has spacing.
self._tokens = [] self._tokens = []
pToken: T_Token = (self.T_EMPTY, 0, "", [], self.A_NONE) pToken: T_Token = (BlockTyp.EMPTY, 0, "", [], BlockFmt.NONE)
nToken: T_Token = (self.T_EMPTY, 0, "", [], self.A_NONE) nToken: T_Token = (BlockTyp.EMPTY, 0, "", [], BlockFmt.NONE)
lineSep = "\n" if self._keepBreaks else " " lineSep = "\n" if self._keepBreaks else " "
pLines: list[T_Token] = [] pLines: list[T_Token] = []
@@ -908,26 +925,26 @@ class Tokenizer(ABC):
# specific type # specific type
self._noIndent = True self._noIndent = True
if cToken[0] == self.T_EMPTY: if cToken[0] == BlockTyp.EMPTY:
# We don't need to keep the empty lines after this pass # We don't need to keep the empty lines after this pass
pass pass
elif cToken[0] == self.T_KEYWORD: elif cToken[0] == BlockTyp.KEYWORD:
# Adjust margins for lines in a list of keyword lines # Adjust margins for lines in a list of keyword lines
aStyle = cToken[4] aStyle = cToken[4]
if pToken[0] == self.T_KEYWORD: if pToken[0] == BlockTyp.KEYWORD:
aStyle |= self.A_Z_TOPMRG aStyle |= BlockFmt.Z_TOPMRG
if nToken[0] == self.T_KEYWORD: if nToken[0] == BlockTyp.KEYWORD:
aStyle |= self.A_Z_BTMMRG aStyle |= BlockFmt.Z_BTMMRG
self._tokens.append(( self._tokens.append((
cToken[0], cToken[1], cToken[2], cToken[3], aStyle cToken[0], cToken[1], cToken[2], cToken[3], aStyle
)) ))
elif cToken[0] == self.T_TEXT: elif cToken[0] == BlockTyp.TEXT:
# Combine lines from the same paragraph # Combine lines from the same paragraph
pLines.append(cToken) pLines.append(cToken)
if nToken[0] != self.T_TEXT: if nToken[0] != BlockTyp.TEXT:
# Next token is not text, so we add the buffer to tokens # Next token is not text, so we add the buffer to tokens
nLines = len(pLines) nLines = len(pLines)
cStyle = pLines[0][4] cStyle = pLines[0][4]
@@ -935,16 +952,16 @@ class Tokenizer(ABC):
# If paragraph indentation is enabled, not temporarily # If paragraph indentation is enabled, not temporarily
# turned off, and the block is not aligned, we add the # turned off, and the block is not aligned, we add the
# text indentation flag # text indentation flag
cStyle |= self.A_IND_T cStyle |= BlockFmt.IND_T
if nLines == 1: if nLines == 1:
# The paragraph contains a single line, so we just save # The paragraph contains a single line, so we just save
# that directly to the token list. If justify is # that directly to the token list. If justify is
# enabled, and there is no alignment, we apply it. # enabled, and there is no alignment, we apply it.
if self._doJustify and not cStyle & self.M_ALIGNED: if self._doJustify and not cStyle & self.M_ALIGNED:
cStyle |= self.A_JUSTIFY cStyle |= BlockFmt.JUSTIFY
self._tokens.append(( self._tokens.append((
self.T_TEXT, pLines[0][1], pLines[0][2], pLines[0][3], cStyle BlockTyp.TEXT, pLines[0][1], pLines[0][2], pLines[0][3], cStyle
)) ))
elif nLines > 1: elif nLines > 1:
# The paragraph contains multiple lines, so we need to # The paragraph contains multiple lines, so we need to
@@ -957,7 +974,7 @@ class Tokenizer(ABC):
tTxt += f"{aToken[2]}{lineSep}" tTxt += f"{aToken[2]}{lineSep}"
tFmt.extend((p+tLen, fmt, key) for p, fmt, key in aToken[3]) tFmt.extend((p+tLen, fmt, key) for p, fmt, key in aToken[3])
self._tokens.append(( self._tokens.append((
self.T_TEXT, pLines[0][1], tTxt[:-1], tFmt, cStyle BlockTyp.TEXT, pLines[0][1], tTxt[:-1], tFmt, cStyle
)) ))
# Reset buffer and make sure text indent is on for next pass # Reset buffer and make sure text indent is on for next pass
@@ -974,13 +991,13 @@ class Tokenizer(ABC):
tHandle = self._handle or "" tHandle = self._handle or ""
isNovel = self._isNovel isNovel = self._isNovel
for tType, nHead, tText, _, _ in self._tokens: for tType, nHead, tText, _, _ in self._tokens:
if tType == self.T_TITLE: if tType == BlockTyp.TITLE:
prefix = "TT" prefix = "TT"
elif tType == self.T_HEAD1: elif tType == BlockTyp.HEAD1:
prefix = "PT" if isNovel else "H1" prefix = "PT" if isNovel else "H1"
elif tType == self.T_HEAD2: elif tType == BlockTyp.HEAD2:
prefix = "CH" if isNovel else "H2" prefix = "CH" if isNovel else "H2"
elif tType == self.T_HEAD3: elif tType == BlockTyp.HEAD3:
prefix = "SC" if isNovel else "H3" prefix = "SC" if isNovel else "H3"
else: else:
continue continue
@@ -1017,7 +1034,7 @@ class Tokenizer(ABC):
nChars = len(tText) nChars = len(tText)
nWChars = len("".join(tWords)) nWChars = len("".join(tWords))
if tType == self.T_TEXT: if tType == BlockTyp.TEXT:
tPWords = tText.split() tPWords = tText.split()
nPWords = len(tPWords) nPWords = len(tPWords)
nPChars = len(tText) nPChars = len(tText)
@@ -1040,33 +1057,33 @@ class Tokenizer(ABC):
titleChars += nChars titleChars += nChars
titleWordChars += nWChars titleWordChars += nWChars
elif tType == self.T_SEP: elif tType == BlockTyp.SEP:
allWords += nWords allWords += nWords
allChars += nChars allChars += nChars
allWordChars += nWChars allWordChars += nWChars
elif tType == self.T_SYNOPSIS and self._doSynopsis: elif tType == BlockTyp.SYNOPSIS and self._doSynopsis:
text = "{0}: {1}".format(self._localLookup("Synopsis"), tText) text = "{0}: {1}".format(self._localLookup("Synopsis"), tText)
words = text.split() words = text.split()
allWords += len(words) allWords += len(words)
allChars += len(text) allChars += len(text)
allWordChars += len("".join(words)) allWordChars += len("".join(words))
elif tType == self.T_SHORT and self._doSynopsis: elif tType == BlockTyp.SHORT and self._doSynopsis:
text = "{0}: {1}".format(self._localLookup("Short Description"), tText) text = "{0}: {1}".format(self._localLookup("Short Description"), tText)
words = text.split() words = text.split()
allWords += len(words) allWords += len(words)
allChars += len(text) allChars += len(text)
allWordChars += len("".join(words)) allWordChars += len("".join(words))
elif tType == self.T_COMMENT and self._doComments: elif tType == BlockTyp.COMMENT and self._doComments:
text = "{0}: {1}".format(self._localLookup("Comment"), tText) text = "{0}: {1}".format(self._localLookup("Comment"), tText)
words = text.split() words = text.split()
allWords += len(words) allWords += len(words)
allChars += len(text) allChars += len(text)
allWordChars += len("".join(words)) allWordChars += len("".join(words))
elif tType == self.T_KEYWORD and self._doKeywords: elif tType == BlockTyp.KEYWORD and self._doKeywords:
valid, bits, _ = self._project.index.scanThis("@"+tText) valid, bits, _ = self._project.index.scanThis("@"+tText)
if valid and bits: if valid and bits:
key = self._localLookup(nwLabels.KEY_NAME[bits[0]]) key = self._localLookup(nwLabels.KEY_NAME[bits[0]])
@@ -1155,7 +1172,7 @@ class Tokenizer(ABC):
kind = self._shortCodeVals.get(res.group(1).lower(), 0) kind = self._shortCodeVals.get(res.group(1).lower(), 0)
temp.append(( temp.append((
res.start(0), res.end(0), res.start(0), res.end(0),
self.FMT_STRIP if kind == skip else kind, TextFmt.STRIP if kind == skip else kind,
f"{tHandle}:{res.group(2)}", f"{tHandle}:{res.group(2)}",
)) ))
+47 -47
View File
@@ -29,47 +29,47 @@ from pathlib import Path
from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import T_Formats, Tokenizer from novelwriter.formats.tokenizer import BlockFmt, BlockTyp, T_Formats, TextFmt, Tokenizer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Standard Markdown # Standard Markdown
STD_MD = { STD_MD = {
Tokenizer.FMT_B_B: "**", TextFmt.B_B: "**",
Tokenizer.FMT_B_E: "**", TextFmt.B_E: "**",
Tokenizer.FMT_I_B: "_", TextFmt.I_B: "_",
Tokenizer.FMT_I_E: "_", TextFmt.I_E: "_",
Tokenizer.FMT_D_B: "", TextFmt.D_B: "",
Tokenizer.FMT_D_E: "", TextFmt.D_E: "",
Tokenizer.FMT_U_B: "", TextFmt.U_B: "",
Tokenizer.FMT_U_E: "", TextFmt.U_E: "",
Tokenizer.FMT_M_B: "", TextFmt.M_B: "",
Tokenizer.FMT_M_E: "", TextFmt.M_E: "",
Tokenizer.FMT_SUP_B: "", TextFmt.SUP_B: "",
Tokenizer.FMT_SUP_E: "", TextFmt.SUP_E: "",
Tokenizer.FMT_SUB_B: "", TextFmt.SUB_B: "",
Tokenizer.FMT_SUB_E: "", TextFmt.SUB_E: "",
Tokenizer.FMT_STRIP: "", TextFmt.STRIP: "",
} }
# Extended Markdown # Extended Markdown
EXT_MD = { EXT_MD = {
Tokenizer.FMT_B_B: "**", TextFmt.B_B: "**",
Tokenizer.FMT_B_E: "**", TextFmt.B_E: "**",
Tokenizer.FMT_I_B: "_", TextFmt.I_B: "_",
Tokenizer.FMT_I_E: "_", TextFmt.I_E: "_",
Tokenizer.FMT_D_B: "~~", TextFmt.D_B: "~~",
Tokenizer.FMT_D_E: "~~", TextFmt.D_E: "~~",
Tokenizer.FMT_U_B: "", TextFmt.U_B: "",
Tokenizer.FMT_U_E: "", TextFmt.U_E: "",
Tokenizer.FMT_M_B: "==", TextFmt.M_B: "==",
Tokenizer.FMT_M_E: "==", TextFmt.M_E: "==",
Tokenizer.FMT_SUP_B: "^", TextFmt.SUP_B: "^",
Tokenizer.FMT_SUP_E: "^", TextFmt.SUP_E: "^",
Tokenizer.FMT_SUB_B: "~", TextFmt.SUB_B: "~",
Tokenizer.FMT_SUB_E: "~", TextFmt.SUB_E: "~",
Tokenizer.FMT_STRIP: "", TextFmt.STRIP: "",
} }
@@ -119,49 +119,49 @@ class ToMarkdown(Tokenizer):
lines = [] lines = []
for tType, _, tText, tFormat, tStyle in self._tokens: for tType, _, tText, tFormat, tStyle in self._tokens:
if tType == self.T_TEXT: if tType == BlockTyp.TEXT:
tTemp = self._formatText(tText, tFormat, mTags).replace("\n", " \n") tTemp = self._formatText(tText, tFormat, mTags).replace("\n", " \n")
lines.append(f"{tTemp}\n\n") lines.append(f"{tTemp}\n\n")
elif tType == self.T_TITLE: elif tType == BlockTyp.TITLE:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
lines.append(f"# {tHead}\n\n") lines.append(f"# {tHead}\n\n")
elif tType == self.T_HEAD1: elif tType == BlockTyp.HEAD1:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
lines.append(f"# {tHead}\n\n") lines.append(f"# {tHead}\n\n")
elif tType == self.T_HEAD2: elif tType == BlockTyp.HEAD2:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
lines.append(f"## {tHead}\n\n") lines.append(f"## {tHead}\n\n")
elif tType == self.T_HEAD3: elif tType == BlockTyp.HEAD3:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
lines.append(f"### {tHead}\n\n") lines.append(f"### {tHead}\n\n")
elif tType == self.T_HEAD4: elif tType == BlockTyp.HEAD4:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
lines.append(f"#### {tHead}\n\n") lines.append(f"#### {tHead}\n\n")
elif tType == self.T_SEP: elif tType == BlockTyp.SEP:
lines.append(f"{tText}\n\n") lines.append(f"{tText}\n\n")
elif tType == self.T_SKIP: elif tType == BlockTyp.SKIP:
lines.append(f"{cSkip}\n\n") lines.append(f"{cSkip}\n\n")
elif tType == self.T_SYNOPSIS and self._doSynopsis: elif tType == BlockTyp.SYNOPSIS and self._doSynopsis:
label = self._localLookup("Synopsis") label = self._localLookup("Synopsis")
lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n") lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
elif tType == self.T_SHORT and self._doSynopsis: elif tType == BlockTyp.SHORT and self._doSynopsis:
label = self._localLookup("Short Description") label = self._localLookup("Short Description")
lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n") lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
elif tType == self.T_COMMENT and self._doComments: elif tType == BlockTyp.COMMENT and self._doComments:
label = self._localLookup("Comment") label = self._localLookup("Comment")
lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n") lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
elif tType == self.T_KEYWORD and self._doKeywords: elif tType == BlockTyp.KEYWORD and self._doKeywords:
lines.append(self._formatKeywords(tText, tStyle)) lines.append(self._formatKeywords(tText, tStyle))
self._result = "".join(lines) self._result = "".join(lines)
@@ -207,12 +207,12 @@ class ToMarkdown(Tokenizer):
# Internal Functions # Internal Functions
## ##
def _formatText(self, text: str, tFmt: T_Formats, tags: dict[int, str]) -> str: def _formatText(self, text: str, tFmt: T_Formats, tags: dict[TextFmt, str]) -> str:
"""Apply formatting tags to text.""" """Apply formatting tags to text."""
temp = text temp = text
for pos, fmt, data in reversed(tFmt): for pos, fmt, data in reversed(tFmt):
md = "" md = ""
if fmt == self.FMT_FNOTE: if fmt == TextFmt.FNOTE:
if data in self._footnotes: if data in self._footnotes:
index = len(self._usedNotes) + 1 index = len(self._usedNotes) + 1
self._usedNotes[data] = index self._usedNotes[data] = index
@@ -224,7 +224,7 @@ class ToMarkdown(Tokenizer):
temp = f"{temp[:pos]}{md}{temp[pos:]}" temp = f"{temp[:pos]}{md}{temp[pos:]}"
return temp return temp
def _formatKeywords(self, text: str, style: int) -> str: def _formatKeywords(self, text: str, style: BlockFmt) -> str:
"""Apply Markdown formatting to keywords.""" """Apply Markdown formatting to keywords."""
valid, bits, _ = self._project.index.scanThis("@"+text) valid, bits, _ = self._project.index.scanThis("@"+text)
if not valid or not bits: if not valid or not bits:
@@ -236,6 +236,6 @@ class ToMarkdown(Tokenizer):
if len(bits) > 1: if len(bits) > 1:
result += ", ".join(bits[1:]) result += ", ".join(bits[1:])
result += " \n" if style & self.A_Z_BTMMRG else "\n\n" result += " \n" if style & BlockFmt.Z_BTMMRG else "\n\n"
return result return result
+49 -47
View File
@@ -41,7 +41,9 @@ from novelwriter import __version__
from novelwriter.common import xmlIndent, xmlSubElem from novelwriter.common import xmlIndent, xmlSubElem
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import T_Formats, Tokenizer, stripEscape from novelwriter.formats.tokenizer import (
BlockFmt, BlockTyp, T_Formats, TextFmt, Tokenizer, stripEscape
)
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS from novelwriter.types import FONT_STYLE, FONT_WEIGHTS
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -429,79 +431,79 @@ class ToOdt(Tokenizer):
# Styles # Styles
oStyle = ODTParagraphStyle("New") oStyle = ODTParagraphStyle("New")
if tStyle is not None: if tStyle is not None:
if tStyle & self.A_LEFT: if tStyle & BlockFmt.LEFT:
oStyle.setTextAlign("left") oStyle.setTextAlign("left")
elif tStyle & self.A_RIGHT: elif tStyle & BlockFmt.RIGHT:
oStyle.setTextAlign("right") oStyle.setTextAlign("right")
elif tStyle & self.A_CENTRE: elif tStyle & BlockFmt.CENTRE:
oStyle.setTextAlign("center") oStyle.setTextAlign("center")
elif tStyle & self.A_JUSTIFY: elif tStyle & BlockFmt.JUSTIFY:
oStyle.setTextAlign("justify") oStyle.setTextAlign("justify")
if tStyle & self.A_PBB: if tStyle & BlockFmt.PBB:
oStyle.setBreakBefore("page") oStyle.setBreakBefore("page")
if tStyle & self.A_PBA: if tStyle & BlockFmt.PBA:
oStyle.setBreakAfter("page") oStyle.setBreakAfter("page")
if tStyle & self.A_Z_BTMMRG: if tStyle & BlockFmt.Z_BTMMRG:
oStyle.setMarginBottom("0.000cm") oStyle.setMarginBottom("0.000cm")
if tStyle & self.A_Z_TOPMRG: if tStyle & BlockFmt.Z_TOPMRG:
oStyle.setMarginTop("0.000cm") oStyle.setMarginTop("0.000cm")
if tStyle & self.A_IND_L: if tStyle & BlockFmt.IND_L:
oStyle.setMarginLeft(self._fBlockIndent) oStyle.setMarginLeft(self._fBlockIndent)
if tStyle & self.A_IND_R: if tStyle & BlockFmt.IND_R:
oStyle.setMarginRight(self._fBlockIndent) oStyle.setMarginRight(self._fBlockIndent)
# Process Text Types # Process Text Types
if tType == self.T_TEXT: if tType == BlockTyp.TEXT:
# Text indentation is processed here because there is a # Text indentation is processed here because there is a
# dedicated pre-defined style for it # dedicated pre-defined style for it
if tStyle & self.A_IND_T: if tStyle & BlockFmt.IND_T:
self._addTextPar(xText, S_FIND, oStyle, tText, tFmt=tFormat) self._addTextPar(xText, S_FIND, oStyle, tText, tFmt=tFormat)
else: else:
self._addTextPar(xText, S_TEXT, oStyle, tText, tFmt=tFormat) self._addTextPar(xText, S_TEXT, oStyle, tText, tFmt=tFormat)
elif tType == self.T_TITLE: elif tType == BlockTyp.TITLE:
# Title must be text:p # Title must be text:p
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addTextPar(xText, S_TITLE, oStyle, tHead, isHead=False) self._addTextPar(xText, S_TITLE, oStyle, tHead, isHead=False)
elif tType == self.T_HEAD1: elif tType == BlockTyp.HEAD1:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addTextPar(xText, S_HEAD1, oStyle, tHead, isHead=True, oLevel="1") self._addTextPar(xText, S_HEAD1, oStyle, tHead, isHead=True, oLevel="1")
elif tType == self.T_HEAD2: elif tType == BlockTyp.HEAD2:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addTextPar(xText, S_HEAD2, oStyle, tHead, isHead=True, oLevel="2") self._addTextPar(xText, S_HEAD2, oStyle, tHead, isHead=True, oLevel="2")
elif tType == self.T_HEAD3: elif tType == BlockTyp.HEAD3:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addTextPar(xText, S_HEAD3, oStyle, tHead, isHead=True, oLevel="3") self._addTextPar(xText, S_HEAD3, oStyle, tHead, isHead=True, oLevel="3")
elif tType == self.T_HEAD4: elif tType == BlockTyp.HEAD4:
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addTextPar(xText, S_HEAD4, oStyle, tHead, isHead=True, oLevel="4") self._addTextPar(xText, S_HEAD4, oStyle, tHead, isHead=True, oLevel="4")
elif tType == self.T_SEP: elif tType == BlockTyp.SEP:
self._addTextPar(xText, S_SEP, oStyle, tText) self._addTextPar(xText, S_SEP, oStyle, tText)
elif tType == self.T_SKIP: elif tType == BlockTyp.SKIP:
self._addTextPar(xText, S_TEXT, oStyle, "") self._addTextPar(xText, S_TEXT, oStyle, "")
elif tType == self.T_SYNOPSIS and self._doSynopsis: elif tType == BlockTyp.SYNOPSIS and self._doSynopsis:
tTemp, tFmt = self._formatSynopsis(tText, tFormat, True) tTemp, tFmt = self._formatSynopsis(tText, tFormat, True)
self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt) self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
elif tType == self.T_SHORT and self._doSynopsis: elif tType == BlockTyp.SHORT and self._doSynopsis:
tTemp, tFmt = self._formatSynopsis(tText, tFormat, False) tTemp, tFmt = self._formatSynopsis(tText, tFormat, False)
self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt) self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
elif tType == self.T_COMMENT and self._doComments: elif tType == BlockTyp.COMMENT and self._doComments:
tTemp, tFmt = self._formatComments(tText, tFormat) tTemp, tFmt = self._formatComments(tText, tFormat)
self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt) self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
elif tType == self.T_KEYWORD and self._doKeywords: elif tType == BlockTyp.KEYWORD and self._doKeywords:
tTemp, tFmt = self._formatKeywords(tText) tTemp, tFmt = self._formatKeywords(tText)
self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt) self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
@@ -576,7 +578,7 @@ class ToOdt(Tokenizer):
name = self._localLookup("Synopsis" if synopsis else "Short Description") name = self._localLookup("Synopsis" if synopsis else "Short Description")
shift = len(name) + 2 shift = len(name) + 2
rTxt = f"{name}: {text}" rTxt = f"{name}: {text}"
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(name) + 1, self.FMT_B_E, "")] rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(name) + 1, TextFmt.B_E, "")]
rFmt.extend((p + shift, f, d) for p, f, d in fmt) rFmt.extend((p + shift, f, d) for p, f, d in fmt)
return rTxt, rFmt return rTxt, rFmt
@@ -585,7 +587,7 @@ class ToOdt(Tokenizer):
name = self._localLookup("Comment") name = self._localLookup("Comment")
shift = len(name) + 2 shift = len(name) + 2
rTxt = f"{name}: {text}" rTxt = f"{name}: {text}"
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(name) + 1, self.FMT_B_E, "")] rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(name) + 1, TextFmt.B_E, "")]
rFmt.extend((p + shift, f, d) for p, f, d in fmt) rFmt.extend((p + shift, f, d) for p, f, d in fmt)
return rTxt, rFmt return rTxt, rFmt
@@ -596,7 +598,7 @@ class ToOdt(Tokenizer):
return "", [] return "", []
rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: " rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(rTxt) - 1, self.FMT_B_E, "")] rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(rTxt) - 1, TextFmt.B_E, "")]
if len(bits) > 1: if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY: if bits[0] == nwKeyWords.TAG_KEY:
rTxt += bits[1] rTxt += bits[1]
@@ -654,45 +656,45 @@ class ToOdt(Tokenizer):
parProc.appendSpan(tFrag, self._textStyle(xFmt)) parProc.appendSpan(tFrag, self._textStyle(xFmt))
# Calculate the change of format # Calculate the change of format
if fFmt == self.FMT_B_B: if fFmt == TextFmt.B_B:
xFmt |= X_BLD xFmt |= X_BLD
elif fFmt == self.FMT_B_E: elif fFmt == TextFmt.B_E:
xFmt &= M_BLD xFmt &= M_BLD
elif fFmt == self.FMT_I_B: elif fFmt == TextFmt.I_B:
xFmt |= X_ITA xFmt |= X_ITA
elif fFmt == self.FMT_I_E: elif fFmt == TextFmt.I_E:
xFmt &= M_ITA xFmt &= M_ITA
elif fFmt == self.FMT_D_B: elif fFmt == TextFmt.D_B:
xFmt |= X_DEL xFmt |= X_DEL
elif fFmt == self.FMT_D_E: elif fFmt == TextFmt.D_E:
xFmt &= M_DEL xFmt &= M_DEL
elif fFmt == self.FMT_U_B: elif fFmt == TextFmt.U_B:
xFmt |= X_UND xFmt |= X_UND
elif fFmt == self.FMT_U_E: elif fFmt == TextFmt.U_E:
xFmt &= M_UND xFmt &= M_UND
elif fFmt == self.FMT_M_B: elif fFmt == TextFmt.M_B:
xFmt |= X_MRK xFmt |= X_MRK
elif fFmt == self.FMT_M_E: elif fFmt == TextFmt.M_E:
xFmt &= M_MRK xFmt &= M_MRK
elif fFmt == self.FMT_SUP_B: elif fFmt == TextFmt.SUP_B:
xFmt |= X_SUP xFmt |= X_SUP
elif fFmt == self.FMT_SUP_E: elif fFmt == TextFmt.SUP_E:
xFmt &= M_SUP xFmt &= M_SUP
elif fFmt == self.FMT_SUB_B: elif fFmt == TextFmt.SUB_B:
xFmt |= X_SUB xFmt |= X_SUB
elif fFmt == self.FMT_SUB_E: elif fFmt == TextFmt.SUB_E:
xFmt &= M_SUB xFmt &= M_SUB
elif fFmt == self.FMT_DL_B: elif fFmt == TextFmt.DL_B:
xFmt |= X_DLG xFmt |= X_DLG
elif fFmt == self.FMT_DL_E: elif fFmt == TextFmt.DL_E:
xFmt &= M_DLG xFmt &= M_DLG
elif fFmt == self.FMT_ADL_B: elif fFmt == TextFmt.ADL_B:
xFmt |= X_DLA xFmt |= X_DLA
elif fFmt == self.FMT_ADL_E: elif fFmt == TextFmt.ADL_E:
xFmt &= M_DLA xFmt &= M_DLA
elif fFmt == self.FMT_FNOTE: elif fFmt == TextFmt.FNOTE:
xNode = self._generateFootnote(fData) xNode = self._generateFootnote(fData)
elif fFmt == self.FMT_STRIP: elif fFmt == TextFmt.STRIP:
pass pass
else: else:
pErr += 1 pErr += 1
+50 -50
View File
@@ -36,7 +36,7 @@ from PyQt5.QtPrintSupport import QPrinter
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles, nwUnicode from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles, nwUnicode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import T_Formats, Tokenizer from novelwriter.formats.tokenizer import BlockFmt, BlockTyp, T_Formats, TextFmt, Tokenizer
from novelwriter.types import ( from novelwriter.types import (
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight, QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
QtPageBreakAfter, QtPageBreakBefore, QtTransparent, QtVAlignNormal, QtPageBreakAfter, QtPageBreakBefore, QtTransparent, QtVAlignNormal,
@@ -141,20 +141,20 @@ class ToQTextDocument(Tokenizer):
# ============ # ============
self._mHead = { self._mHead = {
self.T_TITLE: (mPx * self._marginTitle[0], mPx * self._marginTitle[1]), BlockTyp.TITLE: (mPx * self._marginTitle[0], mPx * self._marginTitle[1]),
self.T_HEAD1: (mPx * self._marginHead1[0], mPx * self._marginHead1[1]), BlockTyp.HEAD1: (mPx * self._marginHead1[0], mPx * self._marginHead1[1]),
self.T_HEAD2: (mPx * self._marginHead2[0], mPx * self._marginHead2[1]), BlockTyp.HEAD2: (mPx * self._marginHead2[0], mPx * self._marginHead2[1]),
self.T_HEAD3: (mPx * self._marginHead3[0], mPx * self._marginHead3[1]), BlockTyp.HEAD3: (mPx * self._marginHead3[0], mPx * self._marginHead3[1]),
self.T_HEAD4: (mPx * self._marginHead4[0], mPx * self._marginHead4[1]), BlockTyp.HEAD4: (mPx * self._marginHead4[0], mPx * self._marginHead4[1]),
} }
hScale = self._scaleHeads hScale = self._scaleHeads
self._sHead = { self._sHead = {
self.T_TITLE: (nwStyles.H_SIZES.get(0, 1.0) * fPt) if hScale else fPt, BlockTyp.TITLE: (nwStyles.H_SIZES.get(0, 1.0) * fPt) if hScale else fPt,
self.T_HEAD1: (nwStyles.H_SIZES.get(1, 1.0) * fPt) if hScale else fPt, BlockTyp.HEAD1: (nwStyles.H_SIZES.get(1, 1.0) * fPt) if hScale else fPt,
self.T_HEAD2: (nwStyles.H_SIZES.get(2, 1.0) * fPt) if hScale else fPt, BlockTyp.HEAD2: (nwStyles.H_SIZES.get(2, 1.0) * fPt) if hScale else fPt,
self.T_HEAD3: (nwStyles.H_SIZES.get(3, 1.0) * fPt) if hScale else fPt, BlockTyp.HEAD3: (nwStyles.H_SIZES.get(3, 1.0) * fPt) if hScale else fPt,
self.T_HEAD4: (nwStyles.H_SIZES.get(4, 1.0) * fPt) if hScale else fPt, BlockTyp.HEAD4: (nwStyles.H_SIZES.get(4, 1.0) * fPt) if hScale else fPt,
} }
self._mText = (mPx * self._marginText[0], mPx * self._marginText[1]) self._mText = (mPx * self._marginText[0], mPx * self._marginText[1])
@@ -226,33 +226,33 @@ class ToQTextDocument(Tokenizer):
# Styles # Styles
bFmt = QTextBlockFormat(self._blockFmt) bFmt = QTextBlockFormat(self._blockFmt)
if tStyle is not None: if tStyle is not None:
if tStyle & self.A_LEFT: if tStyle & BlockFmt.LEFT:
bFmt.setAlignment(QtAlignLeft) bFmt.setAlignment(QtAlignLeft)
elif tStyle & self.A_RIGHT: elif tStyle & BlockFmt.RIGHT:
bFmt.setAlignment(QtAlignRight) bFmt.setAlignment(QtAlignRight)
elif tStyle & self.A_CENTRE: elif tStyle & BlockFmt.CENTRE:
bFmt.setAlignment(QtAlignCenter) bFmt.setAlignment(QtAlignCenter)
elif tStyle & self.A_JUSTIFY: elif tStyle & BlockFmt.JUSTIFY:
bFmt.setAlignment(QtAlignJustify) bFmt.setAlignment(QtAlignJustify)
if tStyle & self.A_PBB: if tStyle & BlockFmt.PBB:
bFmt.setPageBreakPolicy(QtPageBreakBefore) bFmt.setPageBreakPolicy(QtPageBreakBefore)
if tStyle & self.A_PBA: if tStyle & BlockFmt.PBA:
bFmt.setPageBreakPolicy(QtPageBreakAfter) bFmt.setPageBreakPolicy(QtPageBreakAfter)
if tStyle & self.A_Z_BTMMRG: if tStyle & BlockFmt.Z_BTMMRG:
bFmt.setBottomMargin(0.0) bFmt.setBottomMargin(0.0)
if tStyle & self.A_Z_TOPMRG: if tStyle & BlockFmt.Z_TOPMRG:
bFmt.setTopMargin(0.0) bFmt.setTopMargin(0.0)
if tStyle & self.A_IND_L: if tStyle & BlockFmt.IND_L:
bFmt.setLeftMargin(self._mIndent) bFmt.setLeftMargin(self._mIndent)
if tStyle & self.A_IND_R: if tStyle & BlockFmt.IND_R:
bFmt.setRightMargin(self._mIndent) bFmt.setRightMargin(self._mIndent)
if tStyle & self.A_IND_T: if tStyle & BlockFmt.IND_T:
bFmt.setTextIndent(self._tIndent) bFmt.setTextIndent(self._tIndent)
if tType == self.T_TEXT: if tType == BlockTyp.TEXT:
newBlock(cursor, bFmt) newBlock(cursor, bFmt)
self._insertFragments(tText, tFormat, cursor, self._cText) self._insertFragments(tText, tFormat, cursor, self._cText)
@@ -261,32 +261,32 @@ class ToQTextDocument(Tokenizer):
newBlock(cursor, bFmt) newBlock(cursor, bFmt)
cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt) cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt)
elif tType == self.T_SEP: elif tType == BlockTyp.SEP:
sFmt = QTextBlockFormat(bFmt) sFmt = QTextBlockFormat(bFmt)
sFmt.setTopMargin(self._mSep[0]) sFmt.setTopMargin(self._mSep[0])
sFmt.setBottomMargin(self._mSep[1]) sFmt.setBottomMargin(self._mSep[1])
newBlock(cursor, sFmt) newBlock(cursor, sFmt)
cursor.insertText(tText, self._cText) cursor.insertText(tText, self._cText)
elif tType == self.T_SKIP: elif tType == BlockTyp.SKIP:
newBlock(cursor, bFmt) newBlock(cursor, bFmt)
cursor.insertText(nwUnicode.U_NBSP, self._cText) cursor.insertText(nwUnicode.U_NBSP, self._cText)
elif tType in self.L_SUMMARY and self._doSynopsis: elif tType in self.L_SUMMARY and self._doSynopsis:
newBlock(cursor, bFmt) newBlock(cursor, bFmt)
modifier = self._localLookup( modifier = self._localLookup(
"Short Description" if tType == self.T_SHORT else "Synopsis" "Short Description" if tType == BlockTyp.SHORT else "Synopsis"
) )
cursor.insertText(f"{modifier}: ", self._cModifier) cursor.insertText(f"{modifier}: ", self._cModifier)
self._insertFragments(tText, tFormat, cursor, self._cNote) self._insertFragments(tText, tFormat, cursor, self._cNote)
elif tType == self.T_COMMENT and self._doComments: elif tType == BlockTyp.COMMENT and self._doComments:
newBlock(cursor, bFmt) newBlock(cursor, bFmt)
modifier = self._localLookup("Comment") modifier = self._localLookup("Comment")
cursor.insertText(f"{modifier}: ", self._cCommentMod) cursor.insertText(f"{modifier}: ", self._cCommentMod)
self._insertFragments(tText, tFormat, cursor, self._cComment) self._insertFragments(tText, tFormat, cursor, self._cComment)
elif tType == self.T_KEYWORD and self._doKeywords: elif tType == BlockTyp.KEYWORD and self._doKeywords:
newBlock(cursor, bFmt) newBlock(cursor, bFmt)
self._insertKeywords(tText, cursor) self._insertKeywords(tText, cursor)
@@ -317,7 +317,7 @@ class ToQTextDocument(Tokenizer):
cursor = QTextCursor(self._document) cursor = QTextCursor(self._document)
cursor.movePosition(QTextCursor.MoveOperation.End) cursor.movePosition(QTextCursor.MoveOperation.End)
bFmt, cFmt = self._genHeadStyle(self.T_HEAD4, -1, self._blockFmt) bFmt, cFmt = self._genHeadStyle(BlockTyp.HEAD4, -1, self._blockFmt)
newBlock(cursor, bFmt) newBlock(cursor, bFmt)
cursor.insertText(self._localLookup("Footnotes"), cFmt) cursor.insertText(self._localLookup("Footnotes"), cFmt)
@@ -351,43 +351,43 @@ class ToQTextDocument(Tokenizer):
cursor.insertText(temp[start:pos], cFmt) cursor.insertText(temp[start:pos], cFmt)
# Construct next format # Construct next format
if fmt == self.FMT_B_B: if fmt == TextFmt.B_B:
cFmt.setFontWeight(self._bold) cFmt.setFontWeight(self._bold)
elif fmt == self.FMT_B_E: elif fmt == TextFmt.B_E:
cFmt.setFontWeight(self._normal) cFmt.setFontWeight(self._normal)
elif fmt == self.FMT_I_B: elif fmt == TextFmt.I_B:
cFmt.setFontItalic(True) cFmt.setFontItalic(True)
elif fmt == self.FMT_I_E: elif fmt == TextFmt.I_E:
cFmt.setFontItalic(False) cFmt.setFontItalic(False)
elif fmt == self.FMT_D_B: elif fmt == TextFmt.D_B:
cFmt.setFontStrikeOut(True) cFmt.setFontStrikeOut(True)
elif fmt == self.FMT_D_E: elif fmt == TextFmt.D_E:
cFmt.setFontStrikeOut(False) cFmt.setFontStrikeOut(False)
elif fmt == self.FMT_U_B: elif fmt == TextFmt.U_B:
cFmt.setFontUnderline(True) cFmt.setFontUnderline(True)
elif fmt == self.FMT_U_E: elif fmt == TextFmt.U_E:
cFmt.setFontUnderline(False) cFmt.setFontUnderline(False)
elif fmt == self.FMT_M_B: elif fmt == TextFmt.M_B:
cFmt.setBackground(self._theme.highlight) cFmt.setBackground(self._theme.highlight)
elif fmt == self.FMT_M_E: elif fmt == TextFmt.M_E:
cFmt.setBackground(QtTransparent) cFmt.setBackground(QtTransparent)
elif fmt == self.FMT_SUP_B: elif fmt == TextFmt.SUP_B:
cFmt.setVerticalAlignment(QtVAlignSuper) cFmt.setVerticalAlignment(QtVAlignSuper)
elif fmt == self.FMT_SUP_E: elif fmt == TextFmt.SUP_E:
cFmt.setVerticalAlignment(QtVAlignNormal) cFmt.setVerticalAlignment(QtVAlignNormal)
elif fmt == self.FMT_SUB_B: elif fmt == TextFmt.SUB_B:
cFmt.setVerticalAlignment(QtVAlignSub) cFmt.setVerticalAlignment(QtVAlignSub)
elif fmt == self.FMT_SUB_E: elif fmt == TextFmt.SUB_E:
cFmt.setVerticalAlignment(QtVAlignNormal) cFmt.setVerticalAlignment(QtVAlignNormal)
elif fmt == self.FMT_DL_B: elif fmt == TextFmt.DL_B:
cFmt.setForeground(self._theme.dialog) cFmt.setForeground(self._theme.dialog)
elif fmt == self.FMT_DL_E: elif fmt == TextFmt.DL_E:
cFmt.setForeground(self._theme.text) cFmt.setForeground(self._theme.text)
elif fmt == self.FMT_ADL_B: elif fmt == TextFmt.ADL_B:
cFmt.setForeground(self._theme.altdialog) cFmt.setForeground(self._theme.altdialog)
elif fmt == self.FMT_ADL_E: elif fmt == TextFmt.ADL_E:
cFmt.setForeground(self._theme.text) cFmt.setForeground(self._theme.text)
elif fmt == self.FMT_FNOTE: elif fmt == TextFmt.FNOTE:
xFmt = QTextCharFormat(self._cCode) xFmt = QTextCharFormat(self._cCode)
xFmt.setVerticalAlignment(QtVAlignSuper) xFmt.setVerticalAlignment(QtVAlignSuper)
if data in self._footnotes: if data in self._footnotes:
@@ -435,7 +435,7 @@ class ToQTextDocument(Tokenizer):
cursor.insertText(", ", self._cText) cursor.insertText(", ", self._cText)
return return
def _genHeadStyle(self, hType: int, nHead: int, rFmt: QTextBlockFormat) -> T_TextStyle: def _genHeadStyle(self, hType: BlockTyp, nHead: int, rFmt: QTextBlockFormat) -> T_TextStyle:
"""Generate a heading style set.""" """Generate a heading style set."""
mTop, mBottom = self._mHead.get(hType, (0.0, 0.0)) mTop, mBottom = self._mHead.get(hType, (0.0, 0.0))
@@ -446,7 +446,7 @@ class ToQTextDocument(Tokenizer):
self._cTitle = QTextCharFormat(self._cText) self._cTitle = QTextCharFormat(self._cText)
self._cTitle.setFontWeight(self._bold if self._boldHeads else self._normal) self._cTitle.setFontWeight(self._bold if self._boldHeads else self._normal)
hCol = self._colorHeads and hType != self.T_TITLE hCol = self._colorHeads and hType != BlockTyp.TITLE
cFmt = QTextCharFormat(self._cText) cFmt = QTextCharFormat(self._cText)
cFmt.setForeground(self._theme.head if hCol else self._theme.text) cFmt.setForeground(self._theme.head if hCol else self._theme.text)
cFmt.setFontWeight(self._bold if self._boldHeads else self._normal) cFmt.setFontWeight(self._bold if self._boldHeads else self._normal)
+24 -23
View File
@@ -35,6 +35,7 @@ from novelwriter.formats.todocx import (
S_FNOTE, S_HEAD1, S_HEAD2, S_HEAD3, S_HEAD4, S_META, S_NORM, S_SEP, S_FNOTE, S_HEAD1, S_HEAD2, S_HEAD3, S_HEAD4, S_META, S_NORM, S_SEP,
S_TITLE, ToDocX, _mkTag, _wTag S_TITLE, ToDocX, _mkTag, _wTag
) )
from novelwriter.formats.tokenizer import BlockFmt, BlockTyp
from tests.tools import DOCX_IGNORE, cmpFiles from tests.tools import DOCX_IGNORE, cmpFiles
@@ -70,7 +71,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Normal Text # Normal Text
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -80,7 +81,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Title # Title
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TITLE, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.TITLE, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -90,7 +91,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Heading Level 1 # Heading Level 1
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_HEAD1, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.HEAD1, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -100,7 +101,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Heading Level 2 # Heading Level 2
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_HEAD2, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.HEAD2, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -110,7 +111,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Heading Level 3 # Heading Level 3
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_HEAD3, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.HEAD3, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -120,7 +121,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Heading Level 4 # Heading Level 4
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_HEAD4, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.HEAD4, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -130,7 +131,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Separator # Separator
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_SEP, 0, "* * *", [], doc.A_NONE)] doc._tokens = [(BlockTyp.SEP, 0, "* * *", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -140,7 +141,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Empty Paragraph # Empty Paragraph
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_SKIP, 0, "* * *", [], doc.A_NONE)] doc._tokens = [(BlockTyp.SKIP, 0, "* * *", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -149,7 +150,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Synopsis # Synopsis
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_SYNOPSIS, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.SYNOPSIS, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -161,7 +162,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Short # Short
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_SHORT, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.SHORT, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -173,7 +174,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Comment # Comment
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_COMMENT, 0, "Hello World", [], doc.A_NONE)] doc._tokens = [(BlockTyp.COMMENT, 0, "Hello World", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -185,7 +186,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Tags and References (Single) # Tags and References (Single)
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_KEYWORD, 0, "tag: Stuff", [], doc.A_NONE)] doc._tokens = [(BlockTyp.KEYWORD, 0, "tag: Stuff", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -197,7 +198,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Tags and References (Multiple) # Tags and References (Multiple)
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_KEYWORD, 0, "char: Jane, John", [], doc.A_NONE)] doc._tokens = [(BlockTyp.KEYWORD, 0, "char: Jane, John", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -209,7 +210,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
# Tags and References (Invalid) # Tags and References (Invalid)
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_KEYWORD, 0, "stuff: Stuff", [], doc.A_NONE)] doc._tokens = [(BlockTyp.KEYWORD, 0, "stuff: Stuff", [], BlockFmt.NONE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -229,7 +230,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Left Align # Left Align
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_LEFT)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.LEFT)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -239,7 +240,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Right Align # Right Align
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_RIGHT)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.RIGHT)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -249,7 +250,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Center Align # Center Align
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_CENTRE)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.CENTRE)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -259,7 +260,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Justify # Justify
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_JUSTIFY)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.JUSTIFY)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -269,7 +270,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Page Break Before # Page Break Before
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_PBB)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.PBB)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -281,7 +282,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Page Break After # Page Break After
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_PBA)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.PBA)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -293,7 +294,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Zero Margins # Zero Margins
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_Z_TOPMRG | doc.A_Z_BTMMRG)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.Z_TOPMRG | BlockFmt.Z_BTMMRG)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -304,7 +305,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# Indent # Indent
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_IND_L | doc.A_IND_R)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.IND_L | BlockFmt.IND_R)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
@@ -315,7 +316,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
# First Line Indent # First Line Indent
xTest = ET.Element(_wTag("body")) xTest = ET.Element(_wTag("body"))
doc._tokens = [(doc.T_TEXT, 0, "Hello World", [], doc.A_IND_T)] doc._tokens = [(BlockTyp.TEXT, 0, "Hello World", [], BlockFmt.IND_T)]
doc.doConvert() doc.doConvert()
doc._pars[-1].toXml(xTest) doc._pars[-1].toXml(xTest)
assert xmlToText(xTest) == ( assert xmlToText(xTest) == (
+14 -13
View File
@@ -27,6 +27,7 @@ import pytest
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tohtml import ToHtml from novelwriter.formats.tohtml import ToHtml
from novelwriter.formats.tokenizer import BlockFmt, BlockTyp
@pytest.mark.core @pytest.mark.core
@@ -359,7 +360,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Title # Title
html._tokens = [ html._tokens = [
(html.T_TITLE, 1, "A Title", [], html.A_PBB | html.A_CENTRE), (BlockTyp.TITLE, 1, "A Title", [], BlockFmt.PBB | BlockFmt.CENTRE),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -369,7 +370,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Unnumbered # Unnumbered
html._tokens = [ html._tokens = [
(html.T_HEAD2, 1, "Prologue", [], html.A_PBB), (BlockTyp.HEAD2, 1, "Prologue", [], BlockFmt.PBB),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -382,14 +383,14 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Separator # Separator
html._tokens = [ html._tokens = [
(html.T_SEP, 1, "* * *", [], html.A_CENTRE), (BlockTyp.SEP, 1, "* * *", [], BlockFmt.CENTRE),
] ]
html.doConvert() html.doConvert()
assert html.result == "<p class='sep' style='text-align: center;'>* * *</p>\n" assert html.result == "<p class='sep' style='text-align: center;'>* * *</p>\n"
# Skip # Skip
html._tokens = [ html._tokens = [
(html.T_SKIP, 1, "", [], html.A_NONE), (BlockTyp.SKIP, 1, "", [], BlockFmt.NONE),
] ]
html.doConvert() html.doConvert()
assert html.result == "<p class='skip'>&nbsp;</p>\n" assert html.result == "<p class='skip'>&nbsp;</p>\n"
@@ -402,7 +403,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Align Left # Align Left
html.setStyles(False) html.setStyles(False)
html._tokens = [ html._tokens = [
(html.T_HEAD1, 1, "A Title", [], html.A_LEFT), (BlockTyp.HEAD1, 1, "A Title", [], BlockFmt.LEFT),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -413,7 +414,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Align Left # Align Left
html._tokens = [ html._tokens = [
(html.T_HEAD1, 1, "A Title", [], html.A_LEFT), (BlockTyp.HEAD1, 1, "A Title", [], BlockFmt.LEFT),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -422,7 +423,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Align Right # Align Right
html._tokens = [ html._tokens = [
(html.T_HEAD1, 1, "A Title", [], html.A_RIGHT), (BlockTyp.HEAD1, 1, "A Title", [], BlockFmt.RIGHT),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -431,7 +432,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Align Centre # Align Centre
html._tokens = [ html._tokens = [
(html.T_HEAD1, 1, "A Title", [], html.A_CENTRE), (BlockTyp.HEAD1, 1, "A Title", [], BlockFmt.CENTRE),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -440,7 +441,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Align Justify # Align Justify
html._tokens = [ html._tokens = [
(html.T_HEAD1, 1, "A Title", [], html.A_JUSTIFY), (BlockTyp.HEAD1, 1, "A Title", [], BlockFmt.JUSTIFY),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -452,7 +453,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Page Break Always # Page Break Always
html._tokens = [ html._tokens = [
(html.T_HEAD1, 1, "A Title", [], html.A_PBB | html.A_PBA), (BlockTyp.HEAD1, 1, "A Title", [], BlockFmt.PBB | BlockFmt.PBA),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -465,7 +466,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Indent Left # Indent Left
html._tokens = [ html._tokens = [
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_L), (BlockTyp.TEXT, 1, "Some text ...", [], BlockFmt.IND_L),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -474,7 +475,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Indent Right # Indent Right
html._tokens = [ html._tokens = [
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_R), (BlockTyp.TEXT, 1, "Some text ...", [], BlockFmt.IND_R),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
@@ -483,7 +484,7 @@ def testFmtToHtml_ConvertDirect(mockGUI):
# Text Indent # Text Indent
html._tokens = [ html._tokens = [
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_T), (BlockTyp.TEXT, 1, "Some text ...", [], BlockFmt.IND_T),
] ]
html.doConvert() html.doConvert()
assert html.result == ( assert html.result == (
File diff suppressed because it is too large Load Diff
+8 -7
View File
@@ -23,6 +23,7 @@ from __future__ import annotations
import pytest import pytest
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import BlockFmt, BlockTyp
from novelwriter.formats.tomarkdown import ToMarkdown from novelwriter.formats.tomarkdown import ToMarkdown
@@ -220,7 +221,7 @@ def testFmtToMarkdown_ConvertDirect(mockGUI):
# Title # Title
toMD._tokens = [ toMD._tokens = [
(toMD.T_TITLE, 1, "A Title", [], toMD.A_PBB | toMD.A_CENTRE), (BlockTyp.TITLE, 1, "A Title", [], BlockFmt.PBB | BlockFmt.CENTRE),
] ]
toMD.doConvert() toMD.doConvert()
assert toMD.result == "# A Title\n\n" assert toMD.result == "# A Title\n\n"
@@ -230,14 +231,14 @@ def testFmtToMarkdown_ConvertDirect(mockGUI):
# Separator # Separator
toMD._tokens = [ toMD._tokens = [
(toMD.T_SEP, 1, "* * *", [], toMD.A_CENTRE), (BlockTyp.SEP, 1, "* * *", [], BlockFmt.CENTRE),
] ]
toMD.doConvert() toMD.doConvert()
assert toMD.result == "* * *\n\n" assert toMD.result == "* * *\n\n"
# Skip # Skip
toMD._tokens = [ toMD._tokens = [
(toMD.T_SKIP, 1, "", [], toMD.A_NONE), (BlockTyp.SKIP, 1, "", [], BlockFmt.NONE),
] ]
toMD.doConvert() toMD.doConvert()
assert toMD.result == "\n\n" assert toMD.result == "\n\n"
@@ -300,7 +301,7 @@ def testFmtToMarkdown_Format(mockGUI):
project = NWProject() project = NWProject()
toMD = ToMarkdown(project, False) toMD = ToMarkdown(project, False)
assert toMD._formatKeywords("", toMD.A_NONE) == "" assert toMD._formatKeywords("", BlockFmt.NONE) == ""
assert toMD._formatKeywords("tag: Jane", toMD.A_NONE) == "**Tag:** Jane\n\n" assert toMD._formatKeywords("tag: Jane", BlockFmt.NONE) == "**Tag:** Jane\n\n"
assert toMD._formatKeywords("tag: Jane, John", toMD.A_NONE) == "**Tag:** Jane, John\n\n" assert toMD._formatKeywords("tag: Jane, John", BlockFmt.NONE) == "**Tag:** Jane, John\n\n"
assert toMD._formatKeywords("tag: Jane", toMD.A_Z_BTMMRG) == "**Tag:** Jane \n" assert toMD._formatKeywords("tag: Jane", BlockFmt.Z_BTMMRG) == "**Tag:** Jane \n"
+16 -15
View File
@@ -30,6 +30,7 @@ import pytest
from novelwriter.common import xmlIndent from novelwriter.common import xmlIndent
from novelwriter.constants import nwHeadFmt from novelwriter.constants import nwHeadFmt
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import BlockFmt, BlockTyp, TextFmt
from novelwriter.formats.toodt import ODTParagraphStyle, ODTTextStyle, ToOdt, XMLParagraph, _mkTag from novelwriter.formats.toodt import ODTParagraphStyle, ODTTextStyle, ToOdt, XMLParagraph, _mkTag
from tests.tools import ODT_IGNORE, cmpFiles from tests.tools import ODT_IGNORE, cmpFiles
@@ -183,7 +184,7 @@ def testFmtToOdt_TextFormatting(mockGUI):
# Formatted Text # Formatted Text
text = "A bold word" text = "A bold word"
fmt = [(2, odt.FMT_B_B, ""), (6, odt.FMT_B_E, "")] fmt = [(2, TextFmt.B_B, ""), (6, TextFmt.B_E, "")]
xTest = ET.Element(_mkTag("office", "text")) xTest = ET.Element(_mkTag("office", "text"))
odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt) odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt)
assert odt.errData == [] assert odt.errData == []
@@ -196,7 +197,7 @@ def testFmtToOdt_TextFormatting(mockGUI):
# Incorrectly Formatted Text # Incorrectly Formatted Text
text = "A few words" text = "A few words"
fmt = [(2, odt.FMT_B_B, ""), (5, odt.FMT_B_E, ""), (7, 99999, "")] fmt = [(2, TextFmt.B_B, ""), (5, TextFmt.B_E, ""), (7, 99999, "")]
xTest = ET.Element(_mkTag("office", "text")) xTest = ET.Element(_mkTag("office", "text"))
odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt) odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt)
assert odt.errData == ["Unknown format tag encountered"] assert odt.errData == ["Unknown format tag encountered"]
@@ -210,7 +211,7 @@ def testFmtToOdt_TextFormatting(mockGUI):
# Unclosed format # Unclosed format
text = "A bold word" text = "A bold word"
fmt = [(2, odt.FMT_B_B, "")] fmt = [(2, TextFmt.B_B, "")]
xTest = ET.Element(_mkTag("office", "text")) xTest = ET.Element(_mkTag("office", "text"))
odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt) odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt)
assert odt.errData == [] assert odt.errData == []
@@ -245,7 +246,7 @@ def testFmtToOdt_DialogueFormatting(mockGUI):
# Regular dialogue # Regular dialogue
text = "Text with 'dialogue in it.'" text = "Text with 'dialogue in it.'"
fmt = [(10, odt.FMT_DL_B, ""), (27, odt.FMT_DL_E, "")] fmt = [(10, TextFmt.DL_B, ""), (27, TextFmt.DL_E, "")]
xTest = ET.Element(_mkTag("office", "text")) xTest = ET.Element(_mkTag("office", "text"))
odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt) odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt)
assert odt.errData == [] assert odt.errData == []
@@ -258,7 +259,7 @@ def testFmtToOdt_DialogueFormatting(mockGUI):
# Alternative dialogue # Alternative dialogue
text = "Text with ::dialogue in it.::" text = "Text with ::dialogue in it.::"
fmt = [(10, odt.FMT_ADL_B, ""), (29, odt.FMT_ADL_E, "")] fmt = [(10, TextFmt.ADL_B, ""), (29, TextFmt.ADL_E, "")]
xTest = ET.Element(_mkTag("office", "text")) xTest = ET.Element(_mkTag("office", "text"))
odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt) odt._addTextPar(xTest, "Standard", oStyle, text, tFmt=fmt)
assert odt.errData == [] assert odt.errData == []
@@ -719,7 +720,7 @@ def testFmtToOdt_ConvertDirect(mockGUI):
# Justified # Justified
doc = ToOdt(project, isFlat=True) doc = ToOdt(project, isFlat=True)
doc._tokens = [ doc._tokens = [
(doc.T_TEXT, 1, "This is a paragraph", [], doc.A_JUSTIFY), (BlockTyp.TEXT, 1, "This is a paragraph", [], BlockFmt.JUSTIFY),
] ]
doc.initDocument() doc.initDocument()
doc.doConvert() doc.doConvert()
@@ -739,7 +740,7 @@ def testFmtToOdt_ConvertDirect(mockGUI):
# Page Break After # Page Break After
doc = ToOdt(project, isFlat=True) doc = ToOdt(project, isFlat=True)
doc._tokens = [ doc._tokens = [
(doc.T_TEXT, 1, "This is a paragraph", [], doc.A_PBA), (BlockTyp.TEXT, 1, "This is a paragraph", [], BlockFmt.PBA),
] ]
doc.initDocument() doc.initDocument()
doc.doConvert() doc.doConvert()
@@ -894,28 +895,28 @@ def testFmtToOdt_SpecialFormats(mockGUI):
project = NWProject() project = NWProject()
odt = ToOdt(project, isFlat=True) odt = ToOdt(project, isFlat=True)
assert odt._formatSynopsis("synopsis text", [(9, ToOdt.FMT_STRIP, "")], True) == ( assert odt._formatSynopsis("synopsis text", [(9, TextFmt.STRIP, "")], True) == (
"Synopsis: synopsis text", [ "Synopsis: synopsis text", [
(0, ToOdt.FMT_B_B, ""), (9, ToOdt.FMT_B_E, ""), (19, ToOdt.FMT_STRIP, "") (0, TextFmt.B_B, ""), (9, TextFmt.B_E, ""), (19, TextFmt.STRIP, "")
] ]
) )
assert odt._formatSynopsis("short text", [(6, ToOdt.FMT_STRIP, "")], False) == ( assert odt._formatSynopsis("short text", [(6, TextFmt.STRIP, "")], False) == (
"Short Description: short text", [ "Short Description: short text", [
(0, ToOdt.FMT_B_B, ""), (18, ToOdt.FMT_B_E, ""), (25, ToOdt.FMT_STRIP, "") (0, TextFmt.B_B, ""), (18, TextFmt.B_E, ""), (25, TextFmt.STRIP, "")
] ]
) )
assert odt._formatComments("comment text", [(8, ToOdt.FMT_STRIP, "")]) == ( assert odt._formatComments("comment text", [(8, TextFmt.STRIP, "")]) == (
"Comment: comment text", [ "Comment: comment text", [
(0, ToOdt.FMT_B_B, ""), (8, ToOdt.FMT_B_E, ""), (17, ToOdt.FMT_STRIP, "") (0, TextFmt.B_B, ""), (8, TextFmt.B_E, ""), (17, TextFmt.STRIP, "")
] ]
) )
assert odt._formatKeywords("") == ("", []) assert odt._formatKeywords("") == ("", [])
assert odt._formatKeywords("tag: Jane") == ( assert odt._formatKeywords("tag: Jane") == (
"Tag: Jane", [(0, ToOdt.FMT_B_B, ""), (4, ToOdt.FMT_B_E, "")] "Tag: Jane", [(0, TextFmt.B_B, ""), (4, TextFmt.B_E, "")]
) )
assert odt._formatKeywords("char: Bod, Jane") == ( assert odt._formatKeywords("char: Bod, Jane") == (
"Characters: Bod, Jane", [(0, ToOdt.FMT_B_B, ""), (11, ToOdt.FMT_B_E, "")] "Characters: Bod, Jane", [(0, TextFmt.B_B, ""), (11, TextFmt.B_E, "")]
) )
+18 -17
View File
@@ -27,6 +27,7 @@ from PyQt5.QtGui import QTextBlock, QTextCharFormat, QTextCursor
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import BlockFmt, BlockTyp
from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument
from novelwriter.types import ( from novelwriter.types import (
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight, QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
@@ -68,55 +69,55 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
block = qdoc.document.findBlockByNumber(0) block = qdoc.document.findBlockByNumber(0)
assert block.text() == "Title" assert block.text() == "Title"
bFmt = block.blockFormat() bFmt = block.blockFormat()
assert bFmt.topMargin() == qdoc._mHead[qdoc.T_TITLE][0] assert bFmt.topMargin() == qdoc._mHead[BlockTyp.TITLE][0]
assert bFmt.bottomMargin() == qdoc._mHead[qdoc.T_TITLE][1] assert bFmt.bottomMargin() == qdoc._mHead[BlockTyp.TITLE][1]
cFmt = charFmtInBlock(block, 1) cFmt = charFmtInBlock(block, 1)
assert cFmt.fontWeight() == qdoc._bold assert cFmt.fontWeight() == qdoc._bold
assert cFmt.fontPointSize() == qdoc._sHead[qdoc.T_TITLE] assert cFmt.fontPointSize() == qdoc._sHead[BlockTyp.TITLE]
assert cFmt.foreground().color() == THEME.text assert cFmt.foreground().color() == THEME.text
# Partition # Partition
block = qdoc.document.findBlockByNumber(1) block = qdoc.document.findBlockByNumber(1)
assert block.text() == "Partition" assert block.text() == "Partition"
bFmt = block.blockFormat() bFmt = block.blockFormat()
assert bFmt.topMargin() == qdoc._mHead[qdoc.T_HEAD1][0] assert bFmt.topMargin() == qdoc._mHead[BlockTyp.HEAD1][0]
assert bFmt.bottomMargin() == qdoc._mHead[qdoc.T_HEAD1][1] assert bFmt.bottomMargin() == qdoc._mHead[BlockTyp.HEAD1][1]
cFmt = charFmtInBlock(block, 1) cFmt = charFmtInBlock(block, 1)
assert cFmt.fontWeight() == qdoc._bold assert cFmt.fontWeight() == qdoc._bold
assert cFmt.fontPointSize() == qdoc._sHead[qdoc.T_HEAD1] assert cFmt.fontPointSize() == qdoc._sHead[BlockTyp.HEAD1]
assert cFmt.foreground().color() == THEME.head assert cFmt.foreground().color() == THEME.head
# Chapter # Chapter
block = qdoc.document.findBlockByNumber(2) block = qdoc.document.findBlockByNumber(2)
assert block.text() == "Chapter" assert block.text() == "Chapter"
bFmt = block.blockFormat() bFmt = block.blockFormat()
assert bFmt.topMargin() == qdoc._mHead[qdoc.T_HEAD2][0] assert bFmt.topMargin() == qdoc._mHead[BlockTyp.HEAD2][0]
assert bFmt.bottomMargin() == qdoc._mHead[qdoc.T_HEAD2][1] assert bFmt.bottomMargin() == qdoc._mHead[BlockTyp.HEAD2][1]
cFmt = charFmtInBlock(block, 1) cFmt = charFmtInBlock(block, 1)
assert cFmt.fontWeight() == qdoc._bold assert cFmt.fontWeight() == qdoc._bold
assert cFmt.fontPointSize() == qdoc._sHead[qdoc.T_HEAD2] assert cFmt.fontPointSize() == qdoc._sHead[BlockTyp.HEAD2]
assert cFmt.foreground().color() == THEME.head assert cFmt.foreground().color() == THEME.head
# Scene # Scene
block = qdoc.document.findBlockByNumber(3) block = qdoc.document.findBlockByNumber(3)
assert block.text() == "Scene" assert block.text() == "Scene"
bFmt = block.blockFormat() bFmt = block.blockFormat()
assert bFmt.topMargin() == qdoc._mHead[qdoc.T_HEAD3][0] assert bFmt.topMargin() == qdoc._mHead[BlockTyp.HEAD3][0]
assert bFmt.bottomMargin() == qdoc._mHead[qdoc.T_HEAD3][1] assert bFmt.bottomMargin() == qdoc._mHead[BlockTyp.HEAD3][1]
cFmt = charFmtInBlock(block, 1) cFmt = charFmtInBlock(block, 1)
assert cFmt.fontWeight() == qdoc._bold assert cFmt.fontWeight() == qdoc._bold
assert cFmt.fontPointSize() == qdoc._sHead[qdoc.T_HEAD3] assert cFmt.fontPointSize() == qdoc._sHead[BlockTyp.HEAD3]
assert cFmt.foreground().color() == THEME.head assert cFmt.foreground().color() == THEME.head
# Section # Section
block = qdoc.document.findBlockByNumber(4) block = qdoc.document.findBlockByNumber(4)
assert block.text() == "Section" assert block.text() == "Section"
bFmt = block.blockFormat() bFmt = block.blockFormat()
assert bFmt.topMargin() == qdoc._mHead[qdoc.T_HEAD4][0] assert bFmt.topMargin() == qdoc._mHead[BlockTyp.HEAD4][0]
assert bFmt.bottomMargin() == qdoc._mHead[qdoc.T_HEAD4][1] assert bFmt.bottomMargin() == qdoc._mHead[BlockTyp.HEAD4][1]
cFmt = charFmtInBlock(block, 1) cFmt = charFmtInBlock(block, 1)
assert cFmt.fontWeight() == qdoc._bold assert cFmt.fontWeight() == qdoc._bold
assert cFmt.fontPointSize() == qdoc._sHead[qdoc.T_HEAD4] assert cFmt.fontPointSize() == qdoc._sHead[BlockTyp.HEAD4]
assert cFmt.foreground().color() == THEME.head assert cFmt.foreground().color() == THEME.head
@@ -410,8 +411,8 @@ def testFmtToQTextDocument_TextBlockFormats(mockGUI):
qdoc.document.clear() qdoc.document.clear()
qdoc._tokens = [ qdoc._tokens = [
(qdoc.T_TEXT, 1, "This is justified", [], qdoc.A_JUSTIFY), (BlockTyp.TEXT, 1, "This is justified", [], BlockFmt.JUSTIFY),
(qdoc.T_TEXT, 1, "This has a page break", [], qdoc.A_PBA), (BlockTyp.TEXT, 1, "This has a page break", [], BlockFmt.PBA),
] ]
qdoc.doConvert() qdoc.doConvert()
assert qdoc.document.blockCount() == 2 assert qdoc.document.blockCount() == 2