Move heading formatting into a separate class
This commit is contained in:
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user