diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 0e91b15b..ba3e469d 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -127,16 +127,11 @@ class NWBuildDocument: makeObj = ToQTextDocument(self._project) filtered = self._setupBuild(makeObj) makeObj.initDocument() - self._outline = True - yield from self._iterBuild(makeObj, filtered) - makeObj.closeDocument() - self._error = None self._cache = makeObj - return def iterBuildDocument(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]: @@ -152,18 +147,14 @@ class NWBuildDocument: makeObj = ToOdt(self._project, bFormat == nwBuildFmt.FODT) filtered = self._setupBuild(makeObj) makeObj.initDocument() - yield from self._iterBuild(makeObj, filtered) - makeObj.closeDocument() elif bFormat in (nwBuildFmt.HTML, nwBuildFmt.J_HTML): makeObj = ToHtml(self._project) filtered = self._setupBuild(makeObj) makeObj.initDocument() - yield from self._iterBuild(makeObj, filtered) - makeObj.closeDocument() if not self._build.getBool("html.preserveTabs"): makeObj.replaceTabs() @@ -171,9 +162,7 @@ class NWBuildDocument: elif bFormat in (nwBuildFmt.STD_MD, nwBuildFmt.EXT_MD): makeObj = ToMarkdown(self._project, bFormat == nwBuildFmt.EXT_MD) filtered = self._setupBuild(makeObj) - yield from self._iterBuild(makeObj, filtered) - makeObj.closeDocument() if self._build.getBool("format.replaceTabs"): makeObj.replaceTabs(nSpaces=4, spaceChar=" ") @@ -181,9 +170,7 @@ class NWBuildDocument: elif bFormat in (nwBuildFmt.NWD, nwBuildFmt.J_NWD): makeObj = ToRaw(self._project) filtered = self._setupBuild(makeObj) - yield from self._iterBuild(makeObj, filtered) - makeObj.closeDocument() if self._build.getBool("format.replaceTabs"): makeObj.replaceTabs(nSpaces=4, spaceChar=" ") @@ -192,18 +179,14 @@ class NWBuildDocument: makeObj = ToDocX(self._project) filtered = self._setupBuild(makeObj) makeObj.initDocument() - yield from self._iterBuild(makeObj, filtered) - makeObj.closeDocument() elif bFormat == nwBuildFmt.PDF: makeObj = ToQTextDocument(self._project) filtered = self._setupBuild(makeObj) makeObj.initDocument() - yield from self._iterBuild(makeObj, filtered) - makeObj.closeDocument() else: diff --git a/novelwriter/formats/todocx.py b/novelwriter/formats/todocx.py index 2f37d5cd..4f426453 100644 --- a/novelwriter/formats/todocx.py +++ b/novelwriter/formats/todocx.py @@ -192,6 +192,7 @@ class ToDocX(Tokenizer): self._files: dict[str, DocXXmlFile] = {} self._styles: dict[str, DocXParStyle] = {} self._usedNotes: dict[str, int] = {} + self._usedFields: list[tuple[ET.Element, str]] = [] return @@ -440,6 +441,8 @@ class ToDocX(Tokenizer): fLink = "" elif fFmt == TextFmt.FNOTE: xNode = self._generateFootnote(fData) + elif fFmt == TextFmt.FIELD: + xNode = self._generateField(fData) elif fFmt == TextFmt.STRIP: pass @@ -510,6 +513,15 @@ class ToDocX(Tokenizer): return xR return None + def _generateField(self, key: str) -> ET.Element | None: + """Generate a data field XML object.""" + if key and (field := key.partition(":")[2]): + xR = ET.Element(_wTag("r")) + xT = xmlSubElem(xR, _wTag("t"), "0") + self._usedFields.append((xT, field)) + return xR + return None + def _generateStyles(self) -> None: """Generate usable styles.""" styles: list[DocXParStyle] = [] @@ -920,6 +932,12 @@ class ToDocX(Tokenizer): pars.append(par) + # Replace fields if there are stats available + if self._usedFields and self._counts: + for xField, field in self._usedFields: + if (value := self._counts.get(field)) is not None: + xField.text = f"{value:n}" + # Write Paragraphs for par in pars: par.toXml(xBody) diff --git a/novelwriter/formats/tohtml.py b/novelwriter/formats/tohtml.py index f7634a61..e27a96dc 100644 --- a/novelwriter/formats/tohtml.py +++ b/novelwriter/formats/tohtml.py @@ -84,6 +84,7 @@ class ToHtml(Tokenizer): self._trMap = {} self._cssStyles = True self._usedNotes: dict[str, int] = {} + self._usedFields: list[tuple[int, str]] = [] self.setReplaceUnicode(False) return @@ -250,7 +251,15 @@ class ToHtml(Tokenizer): return def closeDocument(self) -> None: - """Append the footnotes in the buffer.""" + """Run close document tasks.""" + # Replace fields if there are stats available + if self._usedFields and self._counts: + pages = len(self._pages) + for doc, field in self._usedFields: + if doc >= 0 and doc < pages and (value := self._counts.get(field)) is not None: + self._pages[doc] = self._pages[doc].replace(f"{{{{{field}}}}}", f"{value:n}") + + # Add footnotes if self._usedNotes: footnotes = self._localLookup("Footnotes") @@ -416,6 +425,10 @@ class ToHtml(Tokenizer): tags.append((pos, f"{index}")) else: tags.append((pos, "ERR")) + elif fmt == TextFmt.FIELD: + if field := data.partition(":")[2]: + self._usedFields.append((len(self._pages), field)) + tags.append((pos, f"{{{{{field}}}}}")) # Check all format types and close any tag that is still open. This # ensures that unclosed tags don't spill over to the next paragraph. diff --git a/novelwriter/formats/tomarkdown.py b/novelwriter/formats/tomarkdown.py index ff1aa48e..1db36152 100644 --- a/novelwriter/formats/tomarkdown.py +++ b/novelwriter/formats/tomarkdown.py @@ -86,6 +86,7 @@ class ToMarkdown(Tokenizer): super().__init__(project) self._extended = extended self._usedNotes: dict[str, int] = {} + self._usedFields: list[tuple[int, str]] = [] return ## @@ -150,7 +151,15 @@ class ToMarkdown(Tokenizer): return def closeDocument(self) -> None: - """Append the footnotes in the buffer.""" + """Run close document tasks.""" + # Replace fields if there are stats available + if self._usedFields and self._counts: + pages = len(self._pages) + for doc, field in self._usedFields: + if doc >= 0 and doc < pages and (value := self._counts.get(field)) is not None: + self._pages[doc] = self._pages[doc].replace(f"{{{{{field}}}}}", f"{value:n}") + + # Add footnotes if self._usedNotes: tags = EXT_MD if self._extended else STD_MD footnotes = self._localLookup("Footnotes") @@ -196,6 +205,10 @@ class ToMarkdown(Tokenizer): md = f"[{index}]" else: md = "[ERR]" + elif fmt == TextFmt.FIELD: + if field := data.partition(":")[2]: + self._usedFields.append((len(self._pages), field)) + md = f"{{{{{field}}}}}" else: md = tags.get(fmt, "") temp = f"{temp[:pos]}{md}{temp[pos:]}" diff --git a/novelwriter/formats/toodt.py b/novelwriter/formats/toodt.py index afb15203..0845b14a 100644 --- a/novelwriter/formats/toodt.py +++ b/novelwriter/formats/toodt.py @@ -630,6 +630,8 @@ class ToOdt(Tokenizer): fLink = "" elif fFmt == TextFmt.FNOTE: xNode = self._generateFootnote(fData) + elif fFmt == TextFmt.FIELD: + xNode = self._generateField(fData) elif fFmt == TextFmt.STRIP: pass @@ -725,6 +727,16 @@ class ToOdt(Tokenizer): return xNote return None + def _generateField(self, key: str) -> ET.Element | None: + """Generate a data field XML object.""" + if key and (field := key.partition(":")[2]): + xField = ET.Element(_mkTag("text", "user-field-get"), attrib={ + _mkTag("text", "name"): f"Manuscript{field[:1].upper()}{field[1:]}", + }) + xField.text = "0" + return xField + return None + def _emToCm(self, value: float) -> str: """Converts an em value to centimetres.""" return f"{value*2.54/72*self._fontSize:.3f}cm"