Add remaining code blocks, and add colours
This commit is contained in:
+113
-30
@@ -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 2018–2024, Veronica Berglyd Olsen
|
Copyright 2018–2024, 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]:
|
||||||
|
|||||||
@@ -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((
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user