Move heading formatting into a separate class

This commit is contained in:
Veronica Berglyd Olsen
2023-05-24 00:27:09 +02:00
parent fda647da54
commit e3744d31aa
2 changed files with 84 additions and 55 deletions
+8 -7
View File
@@ -25,19 +25,20 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations from __future__ import annotations
import logging import logging
from pathlib import Path
from typing import Iterable from typing import Iterable
from pathlib import Path
from PyQt5.QtGui import QFont, QFontInfo from PyQt5.QtGui import QFont, QFontInfo
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwConst
from novelwriter.core.tokenizer import Tokenizer
from novelwriter.error import formatException from novelwriter.error import formatException
from novelwriter.constants import nwConst
from novelwriter.core.tomd import ToMarkdown from novelwriter.core.tomd import ToMarkdown
from novelwriter.core.toodt import ToOdt from novelwriter.core.toodt import ToOdt
from novelwriter.core.tohtml import ToHtml from novelwriter.core.tohtml import ToHtml
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import Tokenizer
from novelwriter.core.buildsettings import BuildSettings from novelwriter.core.buildsettings import BuildSettings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -80,7 +81,7 @@ class NWBuildDocument:
"""Queue all document as defined by the build setup. """Queue all document as defined by the build setup.
""" """
filtered = self._build.buildItemFilter(self._project) filtered = self._build.buildItemFilter(self._project)
noteTitles = self._build.getValue("text.addNoteHeadings") noteTitles = self._build.getBool("text.addNoteHeadings")
for item in self._project.tree: for item in self._project.tree:
if filtered.get(item.itemHandle, False): if filtered.get(item.itemHandle, False):
self._queue.append(item.itemHandle) self._queue.append(item.itemHandle)
@@ -117,7 +118,7 @@ class NWBuildDocument:
makeHtml = ToHtml(self._project) makeHtml = ToHtml(self._project)
self._setupBuild(makeHtml) self._setupBuild(makeHtml)
if self._build.getValue("format.replaceTabs"): if self._build.getBool("format.replaceTabs"):
makeHtml.replaceTabs() makeHtml.replaceTabs()
for i, tHandle in enumerate(self._queue): for i, tHandle in enumerate(self._queue):
@@ -142,7 +143,7 @@ class NWBuildDocument:
else: else:
makeMd.setStandardMarkdown() makeMd.setStandardMarkdown()
if self._build.getValue("format.replaceTabs"): if self._build.getBool("format.replaceTabs"):
makeMd.replaceTabs(nSpaces=4, spaceChar=" ") makeMd.replaceTabs(nSpaces=4, spaceChar=" ")
for i, tHandle in enumerate(self._queue): for i, tHandle in enumerate(self._queue):
@@ -196,7 +197,7 @@ class NWBuildDocument:
if not textFont: if not textFont:
textFont = nwConst.SYSTEM_FONT textFont = nwConst.SYSTEM_FONT
bldFont = QFont(family=textFont, pointSize=textSize) bldFont = QFont(textFont, textSize)
fontInfo = QFontInfo(bldFont) fontInfo = QFontInfo(bldFont)
textFixed = fontInfo.fixedPitch() textFixed = fontInfo.fixedPitch()
+76 -48
View File
@@ -22,6 +22,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations
import re import re
import logging import logging
@@ -34,13 +35,14 @@ from PyQt5.QtCore import QCoreApplication, QRegularExpression
from novelwriter.enum import nwItemLayout, nwItemType from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.common import numberToRoman, checkInt 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__) logger = logging.getLogger(__name__)
def stripEscape(text): def stripEscape(text):
"""Helper function to strip escaped markdown characters from """Helper function to strip escaped Markdown characters from
paragraph text. paragraph text.
""" """
if "\\" in text: if "\\" in text:
@@ -136,10 +138,8 @@ class Tokenizer(ABC):
self._linkHeaders = False # Add an anchor before headers self._linkHeaders = False # Add an anchor before headers
# Instance Variables # Instance Variables
self._numChapter = 0 # Counter for chapter numbers self._hFormatter = HeadingFormatter(self.theProject)
self._numChScene = 0 # Counter for scene number within chapter self._firstScene = False # Flag to indicate that the first scene of the chapter
self._numAbsScene = 0 # Counter for scene number within novel
self._firstScene = False # Flag to indicate that the first scene of the chapter
# This File # This File
self._isNone = False # Document has unknown layout self._isNone = False # Document has unknown layout
@@ -179,26 +179,26 @@ class Tokenizer(ABC):
# Setters # Setters
## ##
def setTitleFormat(self, fmtTitle): def setTitleFormat(self, hFormat):
self._fmtTitle = fmtTitle.strip() self._fmtTitle = hFormat.strip()
return return
def setChapterFormat(self, fmtChapter): def setChapterFormat(self, hFormat):
self._fmtChapter = fmtChapter.strip() self._fmtChapter = hFormat.strip()
return return
def setUnNumberedFormat(self, fmtUnNum): def setUnNumberedFormat(self, hFormat):
self._fmtUnNum = fmtUnNum.strip() self._fmtUnNum = hFormat.strip()
return return
def setSceneFormat(self, fmtScene, hideScene): def setSceneFormat(self, hFormat, hide):
self._fmtScene = fmtScene.strip() self._fmtScene = hFormat.strip()
self._hideScene = hideScene self._hideScene = hide
return return
def setSectionFormat(self, fmtSection, hideSection): def setSectionFormat(self, hFormat, hide):
self._fmtSection = fmtSection.strip() self._fmtSection = hFormat.strip()
self._hideSection = hideSection self._hideSection = hide
return return
def setFont(self, textFont, textSize, textFixed=False): def setFont(self, textFont, textSize, textFixed=False):
@@ -627,7 +627,7 @@ class Tokenizer(ABC):
elif tToken[0] == self.T_HEAD1: elif tToken[0] == self.T_HEAD1:
# Partition # Partition
tTemp = self._formatHeading(self._fmtTitle, tToken[2]) tTemp = self._hFormatter.apply(self._fmtTitle, tToken[2])
self._theTokens[n] = ( self._theTokens[n] = (
tToken[0], tToken[1], tTemp, None, tToken[4] tToken[0], tToken[1], tTemp, None, tToken[4]
) )
@@ -637,10 +637,10 @@ class Tokenizer(ABC):
# Numbered or Unnumbered # Numbered or Unnumbered
if tToken[0] == self.T_UNNUM: if tToken[0] == self.T_UNNUM:
tTemp = self._formatHeading(self._fmtUnNum, tToken[2]) tTemp = self._hFormatter.apply(self._fmtUnNum, tToken[2])
else: else:
self._numChapter += 1 self._hFormatter.incChapter()
tTemp = self._formatHeading(self._fmtChapter, tToken[2]) tTemp = self._hFormatter.apply(self._fmtChapter, tToken[2])
# Format the chapter header # Format the chapter header
self._theTokens[n] = ( self._theTokens[n] = (
@@ -649,15 +649,14 @@ class Tokenizer(ABC):
# Set scene variables # Set scene variables
self._firstScene = True self._firstScene = True
self._numChScene = 0 self._hFormatter.resetScene()
elif tToken[0] == self.T_HEAD3: elif tToken[0] == self.T_HEAD3:
# Scene # Scene
self._numChScene += 1 self._hFormatter.incScene()
self._numAbsScene += 1
tTemp = self._formatHeading(self._fmtScene, tToken[2]) tTemp = self._hFormatter.apply(self._fmtScene, tToken[2])
if tTemp == "" and self._hideScene: if tTemp == "" and self._hideScene:
self._theTokens[n] = ( self._theTokens[n] = (
self.T_EMPTY, tToken[1], "", None, self.A_NONE self.T_EMPTY, tToken[1], "", None, self.A_NONE
@@ -685,13 +684,12 @@ class Tokenizer(ABC):
tToken[0], tToken[1], tTemp, None, tToken[4] tToken[0], tToken[1], tTemp, None, tToken[4]
) )
# Definitely no longer the first scene
self._firstScene = False self._firstScene = False
elif tToken[0] == self.T_HEAD4: elif tToken[0] == self.T_HEAD4:
# Section # Section
tTemp = self._formatHeading(self._fmtSection, tToken[2]) tTemp = self._hFormatter.apply(self._fmtSection, tToken[2])
if tTemp == "" and self._hideSection: if tTemp == "" and self._hideSection:
self._theTokens[n] = ( self._theTokens[n] = (
self.T_EMPTY, tToken[1], "", None, self.A_NONE self.T_EMPTY, tToken[1], "", None, self.A_NONE
@@ -719,24 +717,54 @@ class Tokenizer(ABC):
outFile.write(nwdPage) outFile.write(nwdPage)
return 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 # 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