Fix DocX tabs in text, and page break before issues
This commit is contained in:
@@ -193,6 +193,8 @@ class NWBuildDocument:
|
||||
|
||||
yield from self._iterBuild(makeObj, filtered)
|
||||
|
||||
makeObj.closeDocument()
|
||||
|
||||
elif bFormat == nwBuildFmt.PDF:
|
||||
makeObj = ToQTextDocument(self._project)
|
||||
filtered = self._setupBuild(makeObj)
|
||||
|
||||
@@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from datetime import datetime
|
||||
@@ -40,6 +41,9 @@ from novelwriter.formats.tokenizer import T_Formats, Tokenizer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# RegEx
|
||||
RX_TEXT = re.compile(r"([\n\t])", re.UNICODE)
|
||||
|
||||
# Types and Relationships
|
||||
WORD_BASE = "application/vnd.openxmlformats-officedocument"
|
||||
RELS_TYPE = "application/vnd.openxmlformats-package.relationships+xml"
|
||||
@@ -158,8 +162,9 @@ class ToDocX(Tokenizer):
|
||||
self._fontSize = 12.0
|
||||
self._dLanguage = "en-GB"
|
||||
|
||||
# Maps
|
||||
# Data Variables
|
||||
self._styles: dict[str, DocXParStyle] = {}
|
||||
self._pars: list[DocXParagraph] = []
|
||||
|
||||
return
|
||||
|
||||
@@ -215,8 +220,11 @@ class ToDocX(Tokenizer):
|
||||
# xText = self._xText
|
||||
for tType, _, tText, tFormat, tStyle in self._tokens:
|
||||
|
||||
# Styles
|
||||
# Create Paragraph
|
||||
par = DocXParagraph()
|
||||
self._pars.append(par)
|
||||
|
||||
# Styles
|
||||
if tStyle is not None:
|
||||
if tStyle & self.A_LEFT:
|
||||
par.setAlignment("left")
|
||||
@@ -292,7 +300,29 @@ class ToDocX(Tokenizer):
|
||||
tTemp, tFmt = self._formatKeywords(tText)
|
||||
self._processFragments(par, S_META, tTemp, tFmt)
|
||||
|
||||
par.finalise(self._xBody)
|
||||
return
|
||||
|
||||
def closeDocument(self) -> None:
|
||||
"""Finalise document."""
|
||||
# Map all Page Break Before to After where possible
|
||||
pars: list[DocXParagraph] = []
|
||||
for i, par in enumerate(self._pars):
|
||||
if i > 0 and par.pageBreakBefore:
|
||||
prev = self._pars[i-1]
|
||||
if prev.pageBreakAfter:
|
||||
# We already have a break, so we inject a new paragraph instead
|
||||
empty = DocXParagraph()
|
||||
empty.setStyle(self._styles.get(S_NORM))
|
||||
empty.setPageBreakAfter(True)
|
||||
pars.append(empty)
|
||||
else:
|
||||
par.setPageBreakBefore(False)
|
||||
prev.setPageBreakAfter(True)
|
||||
|
||||
pars.append(par)
|
||||
|
||||
for par in pars:
|
||||
par.toXml(self._xBody)
|
||||
|
||||
return
|
||||
|
||||
@@ -486,8 +516,8 @@ class ToDocX(Tokenizer):
|
||||
# Move pos for next pass
|
||||
fStart = fPos
|
||||
|
||||
if rest := text[fStart:]:
|
||||
par.addContent(self._textRunToXml(rest, xFmt))
|
||||
if temp := text[fStart:]:
|
||||
par.addContent(self._textRunToXml(temp, xFmt))
|
||||
|
||||
return
|
||||
|
||||
@@ -516,16 +546,15 @@ class ToDocX(Tokenizer):
|
||||
if fmt & X_DLA == X_DLA:
|
||||
xmlSubElem(rPr, _wTag("color"), attrib={_wTag("val"): COL_DIALOG_A})
|
||||
|
||||
remaining = text
|
||||
while (parts := remaining.partition("\n"))[0]:
|
||||
segment = parts[0]
|
||||
attr = {}
|
||||
if len(segment) != len(segment.strip()):
|
||||
attr[_mkTag("xml", "space")] = "preserve"
|
||||
xmlSubElem(run, _wTag("t"), segment, attrib=attr)
|
||||
if parts[1]:
|
||||
for segment in RX_TEXT.split(text):
|
||||
if segment == "\n":
|
||||
xmlSubElem(run, _wTag("br"))
|
||||
remaining = parts[2]
|
||||
elif segment == "\t":
|
||||
xmlSubElem(run, _wTag("tab"))
|
||||
elif len(segment) != len(segment.strip()):
|
||||
xmlSubElem(run, _wTag("t"), segment, attrib={_mkTag("xml", "space"): "preserve"})
|
||||
elif segment:
|
||||
xmlSubElem(run, _wTag("t"), segment)
|
||||
|
||||
return run
|
||||
|
||||
@@ -716,6 +745,11 @@ class ToDocX(Tokenizer):
|
||||
|
||||
class DocXParagraph:
|
||||
|
||||
__slots__ = (
|
||||
"_content", "_style", "_textAlign", "_topMargin", "_bottomMargin",
|
||||
"_breakBefore", "_breakAfter",
|
||||
)
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._content: list[ET.Element] = []
|
||||
self._style: DocXParStyle | None = None
|
||||
@@ -726,6 +760,20 @@ class DocXParagraph:
|
||||
self._breakAfter = False
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def pageBreakBefore(self) -> bool:
|
||||
"""Has page break before."""
|
||||
return self._breakBefore
|
||||
|
||||
@property
|
||||
def pageBreakAfter(self) -> bool:
|
||||
"""Has page break after."""
|
||||
return self._breakAfter
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
@@ -770,18 +818,11 @@ class DocXParagraph:
|
||||
self._content.append(run)
|
||||
return
|
||||
|
||||
def finalise(self, body: ET.Element) -> None:
|
||||
def toXml(self, body: ET.Element) -> None:
|
||||
"""Called after all content is set."""
|
||||
if style := self._style:
|
||||
par = xmlSubElem(body, _wTag("p"))
|
||||
|
||||
# Values
|
||||
spacing = {}
|
||||
if self._topMargin is not None:
|
||||
spacing["before"] = str(self._topMargin)
|
||||
if self._bottomMargin is not None:
|
||||
spacing["after"] = str(self._bottomMargin)
|
||||
|
||||
# Paragraph
|
||||
pPr = xmlSubElem(par, _wTag("pPr"))
|
||||
xmlSubElem(pPr, _wTag("pStyle"), attrib={_wTag("val"): style.styleId})
|
||||
|
||||
Reference in New Issue
Block a user