diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index a2417e8f..0e91b15b 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -132,7 +132,7 @@ class NWBuildDocument: yield from self._iterBuild(makeObj, filtered) - makeObj.appendFootnotes() + makeObj.closeDocument() self._error = None self._cache = makeObj @@ -164,7 +164,7 @@ class NWBuildDocument: yield from self._iterBuild(makeObj, filtered) - makeObj.appendFootnotes() + makeObj.closeDocument() if not self._build.getBool("html.preserveTabs"): makeObj.replaceTabs() @@ -174,7 +174,7 @@ class NWBuildDocument: yield from self._iterBuild(makeObj, filtered) - makeObj.appendFootnotes() + makeObj.closeDocument() if self._build.getBool("format.replaceTabs"): makeObj.replaceTabs(nSpaces=4, spaceChar=" ") @@ -184,6 +184,7 @@ class NWBuildDocument: yield from self._iterBuild(makeObj, filtered) + makeObj.closeDocument() if self._build.getBool("format.replaceTabs"): makeObj.replaceTabs(nSpaces=4, spaceChar=" ") @@ -203,7 +204,7 @@ class NWBuildDocument: yield from self._iterBuild(makeObj, filtered) - makeObj.appendFootnotes() + makeObj.closeDocument() else: logger.error("Unsupported document format") diff --git a/novelwriter/formats/tohtml.py b/novelwriter/formats/tohtml.py index 484a48ee..f7634a61 100644 --- a/novelwriter/formats/tohtml.py +++ b/novelwriter/formats/tohtml.py @@ -249,7 +249,7 @@ class ToHtml(Tokenizer): return - def appendFootnotes(self) -> None: + def closeDocument(self) -> None: """Append the footnotes in the buffer.""" if self._usedNotes: footnotes = self._localLookup("Footnotes") diff --git a/novelwriter/formats/tokenizer.py b/novelwriter/formats/tokenizer.py index feb77ffd..64017e7e 100644 --- a/novelwriter/formats/tokenizer.py +++ b/novelwriter/formats/tokenizer.py @@ -428,6 +428,10 @@ class Tokenizer(ABC): def doConvert(self) -> None: raise NotImplementedError + @abstractmethod + def closeDocument(self) -> None: + raise NotImplementedError + @abstractmethod def saveDocument(self, path: Path) -> None: raise NotImplementedError diff --git a/novelwriter/formats/tomarkdown.py b/novelwriter/formats/tomarkdown.py index 61111091..ff1aa48e 100644 --- a/novelwriter/formats/tomarkdown.py +++ b/novelwriter/formats/tomarkdown.py @@ -149,7 +149,7 @@ class ToMarkdown(Tokenizer): return - def appendFootnotes(self) -> None: + def closeDocument(self) -> None: """Append the footnotes in the buffer.""" if self._usedNotes: tags = EXT_MD if self._extended else STD_MD diff --git a/novelwriter/formats/toqdoc.py b/novelwriter/formats/toqdoc.py index 84745c58..6ef06d7d 100644 --- a/novelwriter/formats/toqdoc.py +++ b/novelwriter/formats/toqdoc.py @@ -40,8 +40,8 @@ from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt from novelwriter.formats.tokenizer import HEADINGS, Tokenizer from novelwriter.types import ( QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight, - QtPageBreakAfter, QtPageBreakBefore, QtTransparent, QtVAlignNormal, - QtVAlignSub, QtVAlignSuper + QtKeepAnchor, QtMoveAnchor, QtPageBreakAfter, QtPageBreakBefore, + QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper ) logger = logging.getLogger(__name__) @@ -70,6 +70,7 @@ class ToQTextDocument(Tokenizer): self._document.setDocumentMargin(0) self._usedNotes: dict[str, int] = {} + self._usedFields: list[tuple[int, str]] = [] self._init = False self._bold = QFont.Weight.Bold @@ -246,11 +247,21 @@ class ToQTextDocument(Tokenizer): return - def appendFootnotes(self) -> None: - """Append the footnotes in the buffer.""" - if self._usedNotes: - self._document.blockSignals(True) + def closeDocument(self) -> None: + """Run close document tasks.""" + self._document.blockSignals(True) + # Replace fields if there are stats available + if self._usedFields and self._counts: + cursor = QTextCursor(self._document) + for pos, field in reversed(self._usedFields): + if (value := self._counts.get(field)) is not None: + cursor.setPosition(pos, QtMoveAnchor) + cursor.setPosition(pos + 1, QtKeepAnchor) + cursor.insertText(f"{value:n}") + + # Add footnotes + if self._usedNotes: cursor = QTextCursor(self._document) cursor.movePosition(QTextCursor.MoveOperation.End) @@ -268,7 +279,7 @@ class ToQTextDocument(Tokenizer): cursor.insertText(f"{index}. ", cFmt) self._insertFragments(*content, cursor, self._charFmt) - self._document.blockSignals(False) + self._document.blockSignals(False) return @@ -361,6 +372,11 @@ class ToQTextDocument(Tokenizer): cursor.insertText(f"[{index}]", xFmt) else: cursor.insertText("[ERR]", cFmt) + elif fmt == TextFmt.FIELD: + if field := data.partition(":")[2]: + self._usedFields.append((cursor.position(), field)) + cursor.insertText("0", cFmt) + pass # Move pos for next pass start = pos diff --git a/novelwriter/formats/toraw.py b/novelwriter/formats/toraw.py index 8e1ed47e..9a410f2f 100644 --- a/novelwriter/formats/toraw.py +++ b/novelwriter/formats/toraw.py @@ -52,6 +52,10 @@ class ToRaw(Tokenizer): """No conversion to perform.""" return + def closeDocument(self) -> None: + """Nothing to close.""" + return + def saveDocument(self, path: Path) -> None: """Save the raw text to a plain text file.""" if path.suffix.lower() == ".json": diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index 13d641fa..a304b12e 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -229,7 +229,7 @@ class GuiDocViewer(QTextBrowser): qDoc.doPreProcessing() qDoc.tokenizeText() qDoc.doConvert() - qDoc.appendFootnotes() + qDoc.closeDocument() except Exception: logger.error("Failed to generate preview for document with handle '%s'", tHandle) logException() diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index af8f5a3b..dfcb4d3d 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -955,7 +955,7 @@ class _StatsWidget(QWidget): # Maximal self.maxTotalWords.setText("{0:n}".format(data.get(nwStats.WORDS_ALL, 0))) self.maxHeadWords.setText("{0:n}".format(data.get(nwStats.WORDS_TITLE, 0))) - self.maxTextWords.setText("{0:n}".format(data.get(nwStats.WORDS_TITLE, 0))) + self.maxTextWords.setText("{0:n}".format(data.get(nwStats.WORDS_TEXT, 0))) self.maxTitleCount.setText("{0:n}".format(data.get(nwStats.TITLES, 0))) self.maxParCount.setText("{0:n}".format(data.get(nwStats.PARAGRAPHS, 0))) diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 1308d3b1..aaca21b8 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,6 +1,6 @@ - - + + Sample Project Jane Smith @@ -9,8 +9,8 @@ en_GB None - 53b69b83cdafc - 53b69b83cdafc + 636b6aa9b697b + 636b6aa9b697b 7031beac91f75 7031beac91f75 diff --git a/tests/test_formats/test_fmt_tohtml.py b/tests/test_formats/test_fmt_tohtml.py index ac5abbec..d9257738 100644 --- a/tests/test_formats/test_fmt_tohtml.py +++ b/tests/test_formats/test_fmt_tohtml.py @@ -315,7 +315,7 @@ def testFmtToHtml_ConvertParagraphs(mockGUI): "or twoERR footnotes.

\n" ) - html.appendFootnotes() + html.closeDocument() assert html._pages[-2] == ( "

Text with one1 " "or twoERR footnotes.

\n" diff --git a/tests/test_formats/test_fmt_tokenizer.py b/tests/test_formats/test_fmt_tokenizer.py index 874f3211..4b3178b8 100644 --- a/tests/test_formats/test_fmt_tokenizer.py +++ b/tests/test_formats/test_fmt_tokenizer.py @@ -44,6 +44,9 @@ class BareTokenizer(Tokenizer): def doConvert(self): super().doConvert() # type: ignore (deliberate check) + def closeDocument(self): + super().doConvert() # type: ignore (deliberate check) + def saveDocument(self, path) -> None: super().saveDocument(path) # type: ignore (deliberate check) diff --git a/tests/test_formats/test_fmt_tomarkdown.py b/tests/test_formats/test_fmt_tomarkdown.py index e94f4b72..fc53eb7e 100644 --- a/tests/test_formats/test_fmt_tomarkdown.py +++ b/tests/test_formats/test_fmt_tomarkdown.py @@ -205,7 +205,7 @@ def testFmtToMarkdown_ConvertParagraphs(mockGUI): md.doConvert() assert md._pages[-1] == "Text with one[1] or two[ERR] footnotes.\n\n" - md.appendFootnotes() + md.closeDocument() assert md._pages[-2] == ( "Text with one[1] or two[ERR] footnotes.\n\n" ) diff --git a/tests/test_formats/test_fmt_toqdoc.py b/tests/test_formats/test_fmt_toqdoc.py index 1914c82e..46ff0b85 100644 --- a/tests/test_formats/test_fmt_toqdoc.py +++ b/tests/test_formats/test_fmt_toqdoc.py @@ -594,7 +594,7 @@ def testFmtToQTextDocument_Footnotes(mockGUI): ) doc.tokenizeText() doc.doConvert() - doc.appendFootnotes() + doc.closeDocument() assert doc.document.blockCount() == 4 # 0: Scene