Add remaining code blocks, and add colours

This commit is contained in:
Veronica Berglyd Olsen
2024-10-18 21:18:59 +02:00
parent 4b07dbadb2
commit 717aac9dc8
3 changed files with 123 additions and 41 deletions
+113 -30
View File
@@ -1,9 +1,9 @@
""" """
novelWriter DOCX Text Converter novelWriter DocX Text Converter
================================= =================================
File History: File History:
Created: 2024-10-15 [2.6b1] ToRaw Created: 2024-10-18 [2.6b1] ToDocX
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182024, Veronica Berglyd Olsen Copyright 20182024, Veronica Berglyd Olsen
@@ -32,7 +32,7 @@ from zipfile import ZipFile
from novelwriter import __version__ from novelwriter import __version__
from novelwriter.common import xmlIndent from novelwriter.common import xmlIndent
from novelwriter.constants import nwHeadFmt, 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 T_Formats, Tokenizer
@@ -110,6 +110,15 @@ M_DLG = ~X_DLG
M_DLA = ~X_DLA M_DLA = ~X_DLA
# Colours
COL_HEAD_L12 = "2a6099"
COL_HEAD_L34 = "444444"
COL_DIALOG_M = "2a6099"
COL_DIALOG_A = "813709"
COL_META_TXT = "813709"
COL_MARK_TXT = "ffffa6"
class ToDocX(Tokenizer): class ToDocX(Tokenizer):
"""Core: DocX Document Writer """Core: DocX Document Writer
@@ -243,27 +252,27 @@ class ToDocX(Tokenizer):
tHead = tText.replace(nwHeadFmt.BR, "\n") tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addFragments(par, "Heading4", tHead, tFormat) self._addFragments(par, "Heading4", tHead, tFormat)
# elif tType == self.T_SEP: elif tType == self.T_SEP:
# self._addTextPar(xText, S_SEP, oStyle, tText) self._addFragments(par, "Separator", tText)
# elif tType == self.T_SKIP: elif tType == self.T_SKIP:
# self._addTextPar(xText, S_TEXT, oStyle, "") self._addFragments(par, "Normal", "")
# elif tType == self.T_SYNOPSIS and self._doSynopsis: elif tType == self.T_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._addFragments(par, "TextMeta", tTemp, tFmt)
# elif tType == self.T_SHORT and self._doSynopsis: elif tType == self.T_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._addFragments(par, "TextMeta", tTemp, tFmt)
# elif tType == self.T_COMMENT and self._doComments: elif tType == self.T_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._addFragments(par, "TextMeta", tTemp, tFmt)
# elif tType == self.T_KEYWORD and self._doKeywords: elif tType == self.T_KEYWORD and self._doKeywords:
# tTemp, tFmt = self._formatKeywords(tText) tTemp, tFmt = self._formatKeywords(tText)
# self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt) self._addFragments(par, "TextMeta", tTemp, tFmt)
par.finalise(self._xBody) par.finalise(self._xBody)
@@ -370,12 +379,48 @@ class ToDocX(Tokenizer):
# Internal Functions # Internal Functions
## ##
def _addFragments(self, par: DocXParagraph, pStyle: str, text: str, tFmt: T_Formats) -> None: def _formatSynopsis(self, text: str, fmt: T_Formats, synopsis: bool) -> tuple[str, T_Formats]:
"""Apply formatting to synopsis lines."""
name = self._localLookup("Synopsis" if synopsis else "Short Description")
shift = len(name) + 2
rTxt = f"{name}: {text}"
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(name) + 1, self.FMT_B_E, "")]
rFmt.extend((p + shift, f, d) for p, f, d in fmt)
return rTxt, rFmt
def _formatComments(self, text: str, fmt: T_Formats) -> tuple[str, T_Formats]:
"""Apply formatting to comments."""
name = self._localLookup("Comment")
shift = len(name) + 2
rTxt = f"{name}: {text}"
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(name) + 1, self.FMT_B_E, "")]
rFmt.extend((p + shift, f, d) for p, f, d in fmt)
return rTxt, rFmt
def _formatKeywords(self, text: str) -> tuple[str, T_Formats]:
"""Apply formatting to keywords."""
valid, bits, _ = self._project.index.scanThis("@"+text)
if not valid or not bits or bits[0] not in nwLabels.KEY_NAME:
return "", []
rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
rFmt: T_Formats = [(0, self.FMT_B_B, ""), (len(rTxt) - 1, self.FMT_B_E, "")]
if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY:
rTxt += bits[1]
else:
rTxt += ", ".join(bits[1:])
return rTxt, rFmt
def _addFragments(
self, par: DocXParagraph, pStyle: str, text: str, tFmt: T_Formats | None = None
) -> None:
"""Apply formatting tags to text.""" """Apply formatting tags to text."""
par.setStyle(pStyle) par.setStyle(pStyle)
xFmt = 0x00 xFmt = 0x00
fStart = 0 fStart = 0
for fPos, fFmt, fData in tFmt: for fPos, fFmt, fData in tFmt or []:
run = DocXRun(text[fStart:fPos], xFmt) run = DocXRun(text[fStart:fPos], xFmt)
par.addRun(run) par.addRun(run)
@@ -456,12 +501,12 @@ class ToDocX(Tokenizer):
def _useableStyles(self) -> None: def _useableStyles(self) -> None:
"""Set the usable styles.""" """Set the usable styles."""
hScale = self._scaleHeads hScale = self._scaleHeads
hColor = self._colorHeads
# Add Normal Style # Add Normal Style
self._addParStyle( self._addParStyle(
name="Normal", name="Normal",
styleId="Normal", styleId="Normal",
size=1.0,
default=True, default=True,
margins=self._marginText, margins=self._marginText,
) )
@@ -486,6 +531,7 @@ class ToDocX(Tokenizer):
nextStyle="Normal", nextStyle="Normal",
margins=self._marginHead1, margins=self._marginHead1,
level=0, level=0,
color=COL_HEAD_L12 if hColor else None,
) )
# Add Heading 2 # Add Heading 2
@@ -497,6 +543,7 @@ class ToDocX(Tokenizer):
nextStyle="Normal", nextStyle="Normal",
margins=self._marginHead2, margins=self._marginHead2,
level=1, level=1,
color=COL_HEAD_L12 if hColor else None,
) )
# Add Heading 3 # Add Heading 3
@@ -508,6 +555,7 @@ class ToDocX(Tokenizer):
nextStyle="Normal", nextStyle="Normal",
margins=self._marginHead3, margins=self._marginHead3,
level=1, level=1,
color=COL_HEAD_L34 if hColor else None,
) )
# Add Heading 4 # Add Heading 4
@@ -519,6 +567,27 @@ class ToDocX(Tokenizer):
nextStyle="Normal", nextStyle="Normal",
margins=self._marginHead4, margins=self._marginHead4,
level=1, level=1,
color=COL_HEAD_L34 if hColor else None,
)
# Add Separator
self._addParStyle(
name="Separator",
styleId="Separator",
basedOn="Normal",
nextStyle="Normal",
margins=self._marginSep,
align="center",
)
# Add Text Meta Style
self._addParStyle(
name="Text Meta",
styleId="TextMeta",
basedOn="Normal",
nextStyle="Normal",
margins=self._marginMeta,
color=COL_META_TXT,
) )
return return
@@ -527,12 +596,14 @@ class ToDocX(Tokenizer):
self, *, self, *,
name: str, name: str,
styleId: str, styleId: str,
size: float, size: float = 1.0,
basedOn: str | None = None, basedOn: str | None = None,
nextStyle: str | None = None, nextStyle: str | None = None,
margins: tuple[float, float] | None = None, margins: tuple[float, float] | None = None,
align: str | None = None,
default: bool = False, default: bool = False,
level: int | None = None, level: int | None = None,
color: str | None = None,
) -> None: ) -> None:
"""Add a paragraph style.""" """Add a paragraph style."""
sAttr = {} sAttr = {}
@@ -553,17 +624,21 @@ class ToDocX(Tokenizer):
if level is not None: if level is not None:
ET.SubElement(xStyl, _wTag("outlineLvl"), attrib={_wTag("val"): str(level)}) ET.SubElement(xStyl, _wTag("outlineLvl"), attrib={_wTag("val"): str(level)})
xPPr = ET.SubElement(xStyl, _wTag("pPr")) pPr = ET.SubElement(xStyl, _wTag("pPr"))
if margins: if margins:
ET.SubElement(xPPr, _wTag("spacing"), attrib={ ET.SubElement(pPr, _wTag("spacing"), attrib={
_wTag("before"): str(int(20.0 * margins[0] * self._fontSize)), _wTag("before"): str(int(20.0 * margins[0] * self._fontSize)),
_wTag("after"): str(int(20.0 * margins[1] * self._fontSize)), _wTag("after"): str(int(20.0 * margins[1] * self._fontSize)),
_wTag("line"): ln, _wTag("line"): ln,
}) })
if align:
ET.SubElement(pPr, _wTag("jc"), attrib={_wTag("val"): align})
xRPr = ET.SubElement(xStyl, _wTag("rPr")) rPr = ET.SubElement(xStyl, _wTag("rPr"))
ET.SubElement(xRPr, _wTag("sz"), attrib={_wTag("val"): sz}) ET.SubElement(rPr, _wTag("sz"), attrib={_wTag("val"): sz})
ET.SubElement(xRPr, _wTag("szCs"), attrib={_wTag("val"): sz}) ET.SubElement(rPr, _wTag("szCs"), attrib={_wTag("val"): sz})
if color:
ET.SubElement(rPr, _wTag("color"), attrib={_wTag("val"): color})
return return
@@ -630,9 +705,9 @@ class DocXParagraph:
# Values # Values
spacing = {} spacing = {}
if self._topMargin: if self._topMargin is not None:
spacing["before"] = str(self._topMargin) spacing["before"] = str(self._topMargin)
if self._bottomMargin: if self._bottomMargin is not None:
spacing["after"] = str(self._bottomMargin) spacing["after"] = str(self._bottomMargin)
# Paragraph # Paragraph
@@ -672,12 +747,20 @@ class DocXRun:
ET.SubElement(rPr, _wTag("i")) ET.SubElement(rPr, _wTag("i"))
if fmt & X_UND == X_UND: if fmt & X_UND == X_UND:
ET.SubElement(rPr, _wTag("u"), attrib={_wTag("val"): "single"}) ET.SubElement(rPr, _wTag("u"), attrib={_wTag("val"): "single"})
if fmt & X_MRK == X_MRK:
ET.SubElement(rPr, _wTag("shd"), attrib={
_wTag("fill"): COL_MARK_TXT, _wTag("val"): "clear",
})
if fmt & X_DEL == X_DEL: if fmt & X_DEL == X_DEL:
ET.SubElement(rPr, _wTag("strike")) ET.SubElement(rPr, _wTag("strike"))
if fmt & X_SUP == X_SUP: if fmt & X_SUP == X_SUP:
ET.SubElement(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "superscript"}) ET.SubElement(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "superscript"})
if fmt & X_SUB == X_SUB: if fmt & X_SUB == X_SUB:
ET.SubElement(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "subscript"}) ET.SubElement(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "subscript"})
if fmt & X_DLG == X_DLG:
ET.SubElement(rPr, _wTag("color"), attrib={_wTag("val"): COL_DIALOG_M})
if fmt & X_DLA == X_DLA:
ET.SubElement(rPr, _wTag("color"), attrib={_wTag("val"): COL_DIALOG_A})
temp = text temp = text
while (parts := temp.partition("\n"))[0]: while (parts := temp.partition("\n"))[0]:
-2
View File
@@ -205,7 +205,6 @@ class Tokenizer(ABC):
self._hFormatter = HeadingFormatter(self._project) self._hFormatter = HeadingFormatter(self._project)
self._noSep = True # Flag to indicate that we don't want a scene separator self._noSep = True # Flag to indicate that we don't want a scene separator
self._noIndent = False # Flag to disable text indent on next paragraph self._noIndent = False # Flag to disable text indent on next paragraph
self._showDialog = False # Flag for dialogue highlighting
# This File # This File
self._isNovel = False # Document is a novel document self._isNovel = False # Document is a novel document
@@ -380,7 +379,6 @@ class Tokenizer(ABC):
def setDialogueHighlight(self, state: bool) -> None: def setDialogueHighlight(self, state: bool) -> None:
"""Enable or disable dialogue highlighting.""" """Enable or disable dialogue highlighting."""
self._rxDialogue = [] self._rxDialogue = []
self._showDialog = state
if state: if state:
if CONFIG.dialogStyle > 0: if CONFIG.dialogStyle > 0:
self._rxDialogue.append(( self._rxDialogue.append((
+10 -9
View File
@@ -237,8 +237,8 @@ class ToOdt(Tokenizer):
self._opaHead12 = None self._opaHead12 = None
self._colHead34 = None self._colHead34 = None
self._opaHead34 = None self._opaHead34 = None
self._colDialogM = None self._colDialogM = "#2a6099"
self._colDialogA = None self._colDialogA = "#813709"
self._colMetaTx = "#813709" self._colMetaTx = "#813709"
self._opaMetaTx = "100%" self._opaMetaTx = "100%"
self._markText = "#ffffa6" self._markText = "#ffffa6"
@@ -337,10 +337,6 @@ class ToOdt(Tokenizer):
self._colHead34 = "#444444" self._colHead34 = "#444444"
self._opaHead34 = "100%" self._opaHead34 = "100%"
if self._showDialog:
self._colDialogM = "#2a6099"
self._colDialogA = "#813709"
self._fLineHeight = f"{round(100 * self._lineHeight):d}%" self._fLineHeight = f"{round(100 * self._lineHeight):d}%"
self._fBlockIndent = self._emToCm(self._blockIndent) self._fBlockIndent = self._emToCm(self._blockIndent)
self._fTextIndent = self._emToCm(self._firstWidth) self._fTextIndent = self._emToCm(self._firstWidth)
@@ -625,8 +621,13 @@ class ToOdt(Tokenizer):
return rTxt, rFmt return rTxt, rFmt
def _addTextPar( def _addTextPar(
self, xParent: ET.Element, styleName: str, oStyle: ODTParagraphStyle, tText: str, self,
tFmt: Sequence[tuple[int, int, str]] = [], isHead: bool = False, oLevel: str | None = None xParent: ET.Element,
styleName: str, oStyle: ODTParagraphStyle,
tText: str,
tFmt: Sequence[tuple[int, int, str]] | None = None,
isHead: bool = False,
oLevel: str | None = None,
) -> None: ) -> None:
"""Add a text paragraph to the text XML element.""" """Add a text paragraph to the text XML element."""
tAttr = {_mkTag("text", "style-name"): self._paraStyle(styleName, oStyle)} tAttr = {_mkTag("text", "style-name"): self._paraStyle(styleName, oStyle)}
@@ -653,7 +654,7 @@ class ToOdt(Tokenizer):
tFrag = "" tFrag = ""
fLast = 0 fLast = 0
xNode = None xNode = None
for fPos, fFmt, fData in tFmt: for fPos, fFmt, fData in tFmt or []:
# Add any extra nodes # Add any extra nodes
if xNode is not None: if xNode is not None: