diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index d6a5b481..1ab1a09e 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -25,19 +25,20 @@ along with this program. If not, see . from __future__ import annotations import logging -from pathlib import Path + from typing import Iterable +from pathlib import Path from PyQt5.QtGui import QFont, QFontInfo from novelwriter import CONFIG -from novelwriter.constants import nwConst -from novelwriter.core.tokenizer import Tokenizer from novelwriter.error import formatException +from novelwriter.constants import nwConst from novelwriter.core.tomd import ToMarkdown from novelwriter.core.toodt import ToOdt from novelwriter.core.tohtml import ToHtml from novelwriter.core.project import NWProject +from novelwriter.core.tokenizer import Tokenizer from novelwriter.core.buildsettings import BuildSettings logger = logging.getLogger(__name__) @@ -80,7 +81,7 @@ class NWBuildDocument: """Queue all document as defined by the build setup. """ filtered = self._build.buildItemFilter(self._project) - noteTitles = self._build.getValue("text.addNoteHeadings") + noteTitles = self._build.getBool("text.addNoteHeadings") for item in self._project.tree: if filtered.get(item.itemHandle, False): self._queue.append(item.itemHandle) @@ -117,7 +118,7 @@ class NWBuildDocument: makeHtml = ToHtml(self._project) self._setupBuild(makeHtml) - if self._build.getValue("format.replaceTabs"): + if self._build.getBool("format.replaceTabs"): makeHtml.replaceTabs() for i, tHandle in enumerate(self._queue): @@ -142,7 +143,7 @@ class NWBuildDocument: else: makeMd.setStandardMarkdown() - if self._build.getValue("format.replaceTabs"): + if self._build.getBool("format.replaceTabs"): makeMd.replaceTabs(nSpaces=4, spaceChar=" ") for i, tHandle in enumerate(self._queue): @@ -196,7 +197,7 @@ class NWBuildDocument: if not textFont: textFont = nwConst.SYSTEM_FONT - bldFont = QFont(family=textFont, pointSize=textSize) + bldFont = QFont(textFont, textSize) fontInfo = QFontInfo(bldFont) textFixed = fontInfo.fixedPitch() diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index ffd697c4..0cc4bf4a 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -22,6 +22,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ +from __future__ import annotations import re import logging @@ -34,13 +35,14 @@ from PyQt5.QtCore import QCoreApplication, QRegularExpression from novelwriter.enum import nwItemLayout, nwItemType from novelwriter.common import numberToRoman, checkInt -from novelwriter.constants import nwConst, nwRegEx, nwUnicode +from novelwriter.constants import nwConst, nwHeadingFormats, nwRegEx, nwUnicode +from novelwriter.core.project import NWProject logger = logging.getLogger(__name__) def stripEscape(text): - """Helper function to strip escaped markdown characters from + """Helper function to strip escaped Markdown characters from paragraph text. """ if "\\" in text: @@ -136,10 +138,8 @@ class Tokenizer(ABC): self._linkHeaders = False # Add an anchor before headers # Instance Variables - self._numChapter = 0 # Counter for chapter numbers - self._numChScene = 0 # Counter for scene number within chapter - self._numAbsScene = 0 # Counter for scene number within novel - self._firstScene = False # Flag to indicate that the first scene of the chapter + self._hFormatter = HeadingFormatter(self.theProject) + self._firstScene = False # Flag to indicate that the first scene of the chapter # This File self._isNone = False # Document has unknown layout @@ -179,26 +179,26 @@ class Tokenizer(ABC): # Setters ## - def setTitleFormat(self, fmtTitle): - self._fmtTitle = fmtTitle.strip() + def setTitleFormat(self, hFormat): + self._fmtTitle = hFormat.strip() return - def setChapterFormat(self, fmtChapter): - self._fmtChapter = fmtChapter.strip() + def setChapterFormat(self, hFormat): + self._fmtChapter = hFormat.strip() return - def setUnNumberedFormat(self, fmtUnNum): - self._fmtUnNum = fmtUnNum.strip() + def setUnNumberedFormat(self, hFormat): + self._fmtUnNum = hFormat.strip() return - def setSceneFormat(self, fmtScene, hideScene): - self._fmtScene = fmtScene.strip() - self._hideScene = hideScene + def setSceneFormat(self, hFormat, hide): + self._fmtScene = hFormat.strip() + self._hideScene = hide return - def setSectionFormat(self, fmtSection, hideSection): - self._fmtSection = fmtSection.strip() - self._hideSection = hideSection + def setSectionFormat(self, hFormat, hide): + self._fmtSection = hFormat.strip() + self._hideSection = hide return def setFont(self, textFont, textSize, textFixed=False): @@ -627,7 +627,7 @@ class Tokenizer(ABC): elif tToken[0] == self.T_HEAD1: # Partition - tTemp = self._formatHeading(self._fmtTitle, tToken[2]) + tTemp = self._hFormatter.apply(self._fmtTitle, tToken[2]) self._theTokens[n] = ( tToken[0], tToken[1], tTemp, None, tToken[4] ) @@ -637,10 +637,10 @@ class Tokenizer(ABC): # Numbered or Unnumbered if tToken[0] == self.T_UNNUM: - tTemp = self._formatHeading(self._fmtUnNum, tToken[2]) + tTemp = self._hFormatter.apply(self._fmtUnNum, tToken[2]) else: - self._numChapter += 1 - tTemp = self._formatHeading(self._fmtChapter, tToken[2]) + self._hFormatter.incChapter() + tTemp = self._hFormatter.apply(self._fmtChapter, tToken[2]) # Format the chapter header self._theTokens[n] = ( @@ -649,15 +649,14 @@ class Tokenizer(ABC): # Set scene variables self._firstScene = True - self._numChScene = 0 + self._hFormatter.resetScene() elif tToken[0] == self.T_HEAD3: # Scene - self._numChScene += 1 - self._numAbsScene += 1 + self._hFormatter.incScene() - tTemp = self._formatHeading(self._fmtScene, tToken[2]) + tTemp = self._hFormatter.apply(self._fmtScene, tToken[2]) if tTemp == "" and self._hideScene: self._theTokens[n] = ( self.T_EMPTY, tToken[1], "", None, self.A_NONE @@ -685,13 +684,12 @@ class Tokenizer(ABC): tToken[0], tToken[1], tTemp, None, tToken[4] ) - # Definitely no longer the first scene self._firstScene = False elif tToken[0] == self.T_HEAD4: # Section - tTemp = self._formatHeading(self._fmtSection, tToken[2]) + tTemp = self._hFormatter.apply(self._fmtSection, tToken[2]) if tTemp == "" and self._hideSection: self._theTokens[n] = ( self.T_EMPTY, tToken[1], "", None, self.A_NONE @@ -719,24 +717,54 @@ class Tokenizer(ABC): outFile.write(nwdPage) return - ## - # Internal Functions - ## - - def _formatHeading(self, theTitle, theText): - """Replaces the %keyword% strings. - """ - theTitle = theTitle.replace(r"%title%", theText) - theTitle = theTitle.replace(r"%ch%", str(self._numChapter)) - theTitle = theTitle.replace(r"%sc%", str(self._numChScene)) - theTitle = theTitle.replace(r"%sca%", str(self._numAbsScene)) - if r"%chw%" in theTitle: - theTitle = theTitle.replace(r"%chw%", self._localLookup(self._numChapter)) - if r"%chi%" in theTitle: - theTitle = theTitle.replace(r"%chi%", numberToRoman(self._numChapter, True)) - if r"%chI%" in theTitle: - theTitle = theTitle.replace(r"%chI%", numberToRoman(self._numChapter, False)) - - return theTitle[:1].upper() + theTitle[1:] - # END Class Tokenizer + + +class HeadingFormatter: + + def __init__(self, project: NWProject): + self._project = project + self._chCount = 0 + self._scChCount = 0 + self._scAbsCount = 0 + return + + def incChapter(self): + """Increment the chapter counter. + """ + self._chCount += 1 + return + + def incScene(self): + """Increment the scene counters. + """ + self._scChCount += 1 + self._scAbsCount += 1 + return + + def resetScene(self): + """Reset the chapter scene counter. + """ + self._scChCount = 0 + return + + def apply(self, hFormat: str, text: str): + """Apply formatting to a specific heading. + """ + hFormat = hFormat.replace(nwHeadingFormats.TITLE, text) + hFormat = hFormat.replace(nwHeadingFormats.CH_NUM, str(self._chCount)) + hFormat = hFormat.replace(nwHeadingFormats.SC_NUM, str(self._scChCount)) + hFormat = hFormat.replace(nwHeadingFormats.SC_ABS, str(self._scAbsCount)) + if nwHeadingFormats.CH_WORD in hFormat: + chWord = self._project.localLookup(self._chCount) + hFormat = hFormat.replace(nwHeadingFormats.CH_WORD, chWord) + if nwHeadingFormats.CH_ROML in hFormat: + chRom = numberToRoman(self._chCount, True) + hFormat = hFormat.replace(nwHeadingFormats.CH_ROML, chRom) + if nwHeadingFormats.CH_ROMU in hFormat: + chRom = numberToRoman(self._chCount, False) + hFormat = hFormat.replace(nwHeadingFormats.CH_ROMU, chRom) + + return hFormat + +# END Class HeadingFormatter