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:
Created: 2024-10-15 [2.6b1] ToRaw
Created: 2024-10-18 [2.6b1] ToDocX
This file is a part of novelWriter
Copyright 20182024, Veronica Berglyd Olsen
@@ -32,7 +32,7 @@ from zipfile import ZipFile
from novelwriter import __version__
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.formats.tokenizer import T_Formats, Tokenizer
@@ -110,6 +110,15 @@ M_DLG = ~X_DLG
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):
"""Core: DocX Document Writer
@@ -243,27 +252,27 @@ class ToDocX(Tokenizer):
tHead = tText.replace(nwHeadFmt.BR, "\n")
self._addFragments(par, "Heading4", tHead, tFormat)
# elif tType == self.T_SEP:
# self._addTextPar(xText, S_SEP, oStyle, tText)
elif tType == self.T_SEP:
self._addFragments(par, "Separator", tText)
# elif tType == self.T_SKIP:
# self._addTextPar(xText, S_TEXT, oStyle, "")
elif tType == self.T_SKIP:
self._addFragments(par, "Normal", "")
# elif tType == self.T_SYNOPSIS and self._doSynopsis:
# tTemp, tFmt = self._formatSynopsis(tText, tFormat, True)
# self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
elif tType == self.T_SYNOPSIS and self._doSynopsis:
tTemp, tFmt = self._formatSynopsis(tText, tFormat, True)
self._addFragments(par, "TextMeta", tTemp, tFmt)
# elif tType == self.T_SHORT and self._doSynopsis:
# tTemp, tFmt = self._formatSynopsis(tText, tFormat, False)
# self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
elif tType == self.T_SHORT and self._doSynopsis:
tTemp, tFmt = self._formatSynopsis(tText, tFormat, False)
self._addFragments(par, "TextMeta", tTemp, tFmt)
# elif tType == self.T_COMMENT and self._doComments:
# tTemp, tFmt = self._formatComments(tText, tFormat)
# self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
elif tType == self.T_COMMENT and self._doComments:
tTemp, tFmt = self._formatComments(tText, tFormat)
self._addFragments(par, "TextMeta", tTemp, tFmt)
# elif tType == self.T_KEYWORD and self._doKeywords:
# tTemp, tFmt = self._formatKeywords(tText)
# self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
elif tType == self.T_KEYWORD and self._doKeywords:
tTemp, tFmt = self._formatKeywords(tText)
self._addFragments(par, "TextMeta", tTemp, tFmt)
par.finalise(self._xBody)
@@ -370,12 +379,48 @@ class ToDocX(Tokenizer):
# 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."""
par.setStyle(pStyle)
xFmt = 0x00
fStart = 0
for fPos, fFmt, fData in tFmt:
for fPos, fFmt, fData in tFmt or []:
run = DocXRun(text[fStart:fPos], xFmt)
par.addRun(run)
@@ -456,12 +501,12 @@ class ToDocX(Tokenizer):
def _useableStyles(self) -> None:
"""Set the usable styles."""
hScale = self._scaleHeads
hColor = self._colorHeads
# Add Normal Style
self._addParStyle(
name="Normal",
styleId="Normal",
size=1.0,
default=True,
margins=self._marginText,
)
@@ -486,6 +531,7 @@ class ToDocX(Tokenizer):
nextStyle="Normal",
margins=self._marginHead1,
level=0,
color=COL_HEAD_L12 if hColor else None,
)
# Add Heading 2
@@ -497,6 +543,7 @@ class ToDocX(Tokenizer):
nextStyle="Normal",
margins=self._marginHead2,
level=1,
color=COL_HEAD_L12 if hColor else None,
)
# Add Heading 3
@@ -508,6 +555,7 @@ class ToDocX(Tokenizer):
nextStyle="Normal",
margins=self._marginHead3,
level=1,
color=COL_HEAD_L34 if hColor else None,
)
# Add Heading 4
@@ -519,6 +567,27 @@ class ToDocX(Tokenizer):
nextStyle="Normal",
margins=self._marginHead4,
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
@@ -527,12 +596,14 @@ class ToDocX(Tokenizer):
self, *,
name: str,
styleId: str,
size: float,
size: float = 1.0,
basedOn: str | None = None,
nextStyle: str | None = None,
margins: tuple[float, float] | None = None,
align: str | None = None,
default: bool = False,
level: int | None = None,
color: str | None = None,
) -> None:
"""Add a paragraph style."""
sAttr = {}
@@ -553,17 +624,21 @@ class ToDocX(Tokenizer):
if level is not None:
ET.SubElement(xStyl, _wTag("outlineLvl"), attrib={_wTag("val"): str(level)})
xPPr = ET.SubElement(xStyl, _wTag("pPr"))
pPr = ET.SubElement(xStyl, _wTag("pPr"))
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("after"): str(int(20.0 * margins[1] * self._fontSize)),
_wTag("line"): ln,
})
if align:
ET.SubElement(pPr, _wTag("jc"), attrib={_wTag("val"): align})
xRPr = ET.SubElement(xStyl, _wTag("rPr"))
ET.SubElement(xRPr, _wTag("sz"), attrib={_wTag("val"): sz})
ET.SubElement(xRPr, _wTag("szCs"), attrib={_wTag("val"): sz})
rPr = ET.SubElement(xStyl, _wTag("rPr"))
ET.SubElement(rPr, _wTag("sz"), 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
@@ -630,9 +705,9 @@ class DocXParagraph:
# Values
spacing = {}
if self._topMargin:
if self._topMargin is not None:
spacing["before"] = str(self._topMargin)
if self._bottomMargin:
if self._bottomMargin is not None:
spacing["after"] = str(self._bottomMargin)
# Paragraph
@@ -672,12 +747,20 @@ class DocXRun:
ET.SubElement(rPr, _wTag("i"))
if fmt & X_UND == X_UND:
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:
ET.SubElement(rPr, _wTag("strike"))
if fmt & X_SUP == X_SUP:
ET.SubElement(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "superscript"})
if fmt & X_SUB == X_SUB:
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
while (parts := temp.partition("\n"))[0]:
-2
View File
@@ -205,7 +205,6 @@ class Tokenizer(ABC):
self._hFormatter = HeadingFormatter(self._project)
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._showDialog = False # Flag for dialogue highlighting
# This File
self._isNovel = False # Document is a novel document
@@ -380,7 +379,6 @@ class Tokenizer(ABC):
def setDialogueHighlight(self, state: bool) -> None:
"""Enable or disable dialogue highlighting."""
self._rxDialogue = []
self._showDialog = state
if state:
if CONFIG.dialogStyle > 0:
self._rxDialogue.append((
+10 -9
View File
@@ -237,8 +237,8 @@ class ToOdt(Tokenizer):
self._opaHead12 = None
self._colHead34 = None
self._opaHead34 = None
self._colDialogM = None
self._colDialogA = None
self._colDialogM = "#2a6099"
self._colDialogA = "#813709"
self._colMetaTx = "#813709"
self._opaMetaTx = "100%"
self._markText = "#ffffa6"
@@ -337,10 +337,6 @@ class ToOdt(Tokenizer):
self._colHead34 = "#444444"
self._opaHead34 = "100%"
if self._showDialog:
self._colDialogM = "#2a6099"
self._colDialogA = "#813709"
self._fLineHeight = f"{round(100 * self._lineHeight):d}%"
self._fBlockIndent = self._emToCm(self._blockIndent)
self._fTextIndent = self._emToCm(self._firstWidth)
@@ -625,8 +621,13 @@ class ToOdt(Tokenizer):
return rTxt, rFmt
def _addTextPar(
self, xParent: ET.Element, styleName: str, oStyle: ODTParagraphStyle, tText: str,
tFmt: Sequence[tuple[int, int, str]] = [], isHead: bool = False, oLevel: str | None = None
self,
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:
"""Add a text paragraph to the text XML element."""
tAttr = {_mkTag("text", "style-name"): self._paraStyle(styleName, oStyle)}
@@ -653,7 +654,7 @@ class ToOdt(Tokenizer):
tFrag = ""
fLast = 0
xNode = None
for fPos, fFmt, fData in tFmt:
for fPos, fFmt, fData in tFmt or []:
# Add any extra nodes
if xNode is not None: