Extend heading styling options (#2045)

This commit is contained in:
Veronica Berglyd Olsen
2024-10-15 21:40:21 +02:00
committed by GitHub
25 changed files with 475 additions and 427 deletions
+25 -22
View File
@@ -45,29 +45,30 @@ logger = logging.getLogger(__name__)
# The Settings Template # The Settings Template
# ===================== # =====================
# Each entry contains a tuple on the form: # Each entry contains a tuple on the form: (type, default)
# (type, default, [min value, max value])
SETTINGS_TEMPLATE = { SETTINGS_TEMPLATE: dict[str, tuple[type, str | int | float | bool]] = {
"filter.includeNovel": (bool, True), "filter.includeNovel": (bool, True),
"filter.includeNotes": (bool, False), "filter.includeNotes": (bool, False),
"filter.includeInactive": (bool, False), "filter.includeInactive": (bool, False),
"headings.fmtTitle": (str, nwHeadFmt.TITLE), "headings.fmtPart": (str, nwHeadFmt.TITLE),
"headings.fmtChapter": (str, nwHeadFmt.TITLE), "headings.fmtChapter": (str, nwHeadFmt.TITLE),
"headings.fmtUnnumbered": (str, nwHeadFmt.TITLE), "headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
"headings.fmtScene": (str, "* * *"), "headings.fmtScene": (str, "* * *"),
"headings.fmtAltScene": (str, ""), "headings.fmtAltScene": (str, ""),
"headings.fmtSection": (str, ""), "headings.fmtSection": (str, ""),
"headings.hideTitle": (bool, False), "headings.hidePart": (bool, False),
"headings.hideChapter": (bool, False), "headings.hideChapter": (bool, False),
"headings.hideUnnumbered": (bool, False), "headings.hideUnnumbered": (bool, False),
"headings.hideScene": (bool, False), "headings.hideScene": (bool, False),
"headings.hideAltScene": (bool, False), "headings.hideAltScene": (bool, False),
"headings.hideSection": (bool, True), "headings.hideSection": (bool, True),
"headings.centerTitle": (bool, True), "headings.centerTitle": (bool, True),
"headings.centerPart": (bool, True),
"headings.centerChapter": (bool, False), "headings.centerChapter": (bool, False),
"headings.centerScene": (bool, False), "headings.centerScene": (bool, False),
"headings.breakTitle": (bool, True), "headings.breakTitle": (bool, True),
"headings.breakPart": (bool, True),
"headings.breakChapter": (bool, True), "headings.breakChapter": (bool, True),
"headings.breakScene": (bool, False), "headings.breakScene": (bool, False),
"text.includeSynopsis": (bool, False), "text.includeSynopsis": (bool, False),
@@ -77,7 +78,7 @@ SETTINGS_TEMPLATE = {
"text.ignoredKeywords": (str, ""), "text.ignoredKeywords": (str, ""),
"text.addNoteHeadings": (bool, True), "text.addNoteHeadings": (bool, True),
"format.textFont": (str, CONFIG.textFont.toString()), "format.textFont": (str, CONFIG.textFont.toString()),
"format.lineHeight": (float, 1.15, 0.75, 3.0), "format.lineHeight": (float, 1.15),
"format.justifyText": (bool, False), "format.justifyText": (bool, False),
"format.stripUnicode": (bool, False), "format.stripUnicode": (bool, False),
"format.replaceTabs": (bool, False), "format.replaceTabs": (bool, False),
@@ -94,9 +95,11 @@ SETTINGS_TEMPLATE = {
"format.bottomMargin": (float, 2.0), "format.bottomMargin": (float, 2.0),
"format.leftMargin": (float, 2.0), "format.leftMargin": (float, 2.0),
"format.rightMargin": (float, 2.0), "format.rightMargin": (float, 2.0),
"odt.addColours": (bool, True),
"odt.pageHeader": (str, nwHeadFmt.ODT_AUTO), "odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
"odt.pageCountOffset": (int, 0), "odt.pageCountOffset": (int, 0),
"odt.colorHeadings": (bool, True),
"odt.scaleHeadings": (bool, True),
"odt.boldHeadings": (bool, True),
"html.addStyles": (bool, True), "html.addStyles": (bool, True),
"html.preserveTabs": (bool, False), "html.preserveTabs": (bool, False),
} }
@@ -108,12 +111,16 @@ SETTINGS_LABELS = {
"filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"), "filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"),
"headings": QT_TRANSLATE_NOOP("Builds", "Headings"), "headings": QT_TRANSLATE_NOOP("Builds", "Headings"),
"headings.fmtTitle": QT_TRANSLATE_NOOP("Builds", "Partition Format"), "headings.fmtPart": QT_TRANSLATE_NOOP("Builds", "Partition Format"),
"headings.fmtChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Format"), "headings.fmtChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Format"),
"headings.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Format"), "headings.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Format"),
"headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Format"), "headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Format"),
"headings.fmtAltScene": QT_TRANSLATE_NOOP("Builds", "Alt. Scene Format"), "headings.fmtAltScene": QT_TRANSLATE_NOOP("Builds", "Alt. Scene Format"),
"headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Format"), "headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Format"),
"headings.styleTitle": QT_TRANSLATE_NOOP("Builds", "Title Styling"),
"headings.stylePart": QT_TRANSLATE_NOOP("Builds", "Partition Styling"),
"headings.styleChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Styling"),
"headings.styleScene": QT_TRANSLATE_NOOP("Builds", "Scene Styling"),
"text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"), "text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"),
"text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"), "text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"),
@@ -147,12 +154,14 @@ SETTINGS_LABELS = {
"format.leftMargin": QT_TRANSLATE_NOOP("Builds", "Left Margin"), "format.leftMargin": QT_TRANSLATE_NOOP("Builds", "Left Margin"),
"format.rightMargin": QT_TRANSLATE_NOOP("Builds", "Right Margin"), "format.rightMargin": QT_TRANSLATE_NOOP("Builds", "Right Margin"),
"odt": QT_TRANSLATE_NOOP("Builds", "ODT Documents"), "odt": QT_TRANSLATE_NOOP("Builds", "Document Options"),
"odt.addColours": QT_TRANSLATE_NOOP("Builds", "Add Highlight Colours"),
"odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"), "odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"), "odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
"odt.colorHeadings": QT_TRANSLATE_NOOP("Builds", "Add Colours to Headings"),
"odt.scaleHeadings": QT_TRANSLATE_NOOP("Builds", "Increase Size of Headings"),
"odt.boldHeadings": QT_TRANSLATE_NOOP("Builds", "Bold Headings"),
"html": QT_TRANSLATE_NOOP("Builds", "HTML Document"), "html": QT_TRANSLATE_NOOP("Builds", "HTML Options"),
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"), "html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
"html.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"), "html.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"),
} }
@@ -346,18 +355,12 @@ class BuildSettings:
self._changed = True self._changed = True
return return
def setValue(self, key: str, value: str | int | bool | float) -> bool: def setValue(self, key: str, value: str | int | float | bool) -> None:
"""Set a specific value for a build setting.""" """Set a specific value for a build setting."""
if key not in SETTINGS_TEMPLATE: if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
return False self._changed = value != self._settings[key]
definition = SETTINGS_TEMPLATE[key] self._settings[key] = value
if not isinstance(value, definition[0]): return
return False
if len(definition) == 4 and isinstance(value, (int, float)):
value = min(max(value, definition[2]), definition[3])
self._changed = value != self._settings[key]
self._settings[key] = value
return True
## ##
# Methods # Methods
+25 -25
View File
@@ -35,13 +35,13 @@ from novelwriter.constants import nwLabels
from novelwriter.core.buildsettings import BuildSettings from novelwriter.core.buildsettings import BuildSettings
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tohtml import ToHtml
from novelwriter.core.tokenizer import Tokenizer
from novelwriter.core.tomarkdown import ToMarkdown
from novelwriter.core.toodt import ToOdt
from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
from novelwriter.enum import nwBuildFmt from novelwriter.enum import nwBuildFmt
from novelwriter.error import formatException, logException from novelwriter.error import formatException, logException
from novelwriter.formats.tohtml import ToHtml
from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.formats.tomarkdown import ToMarkdown
from novelwriter.formats.toodt import ToOdt
from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -178,10 +178,7 @@ class NWBuildDocument:
self._cache = makeObj self._cache = makeObj
try: try:
if isFlat: makeObj.saveDocument(path)
makeObj.saveFlatXML(path)
else:
makeObj.saveOpenDocText(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
@@ -213,10 +210,7 @@ class NWBuildDocument:
if isinstance(path, Path): if isinstance(path, Path):
try: try:
if asJson: makeObj.saveDocument(path, asJson=asJson)
makeObj.saveHtmlJson(path)
else:
makeObj.saveHtml5(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
@@ -246,7 +240,7 @@ class NWBuildDocument:
self._cache = makeObj self._cache = makeObj
try: try:
makeObj.saveMarkdown(path) makeObj.saveDocument(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
@@ -276,10 +270,7 @@ class NWBuildDocument:
if isinstance(path, Path): if isinstance(path, Path):
try: try:
if asJson: makeObj.saveRawDocument(path, asJson=asJson)
makeObj.saveRawMarkdownJSON(path)
else:
makeObj.saveRawMarkdown(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
@@ -297,9 +288,9 @@ class NWBuildDocument:
textFont.fromString(self._build.getStr("format.textFont")) textFont.fromString(self._build.getStr("format.textFont"))
bldObj.setFont(textFont) bldObj.setFont(textFont)
bldObj.setTitleFormat( bldObj.setPartitionFormat(
self._build.getStr("headings.fmtTitle"), self._build.getStr("headings.fmtPart"),
self._build.getBool("headings.hideTitle") self._build.getBool("headings.hidePart")
) )
bldObj.setChapterFormat( bldObj.setChapterFormat(
self._build.getStr("headings.fmtChapter"), self._build.getStr("headings.fmtChapter"),
@@ -322,8 +313,12 @@ class NWBuildDocument:
self._build.getBool("headings.hideSection") self._build.getBool("headings.hideSection")
) )
bldObj.setTitleStyle( bldObj.setTitleStyle(
self._build.getBool("headings.centerTitle"), self._build.getBool("headings.centerPart"),
self._build.getBool("headings.breakTitle") self._build.getBool("headings.breakPart")
)
bldObj.setPartitionStyle(
self._build.getBool("headings.centerPart"),
self._build.getBool("headings.breakPart")
) )
bldObj.setChapterStyle( bldObj.setChapterStyle(
self._build.getBool("headings.centerChapter"), self._build.getBool("headings.centerChapter"),
@@ -343,6 +338,11 @@ class NWBuildDocument:
self._build.getFloat("format.firstIndentWidth"), self._build.getFloat("format.firstIndentWidth"),
self._build.getBool("format.indentFirstPar"), self._build.getBool("format.indentFirstPar"),
) )
bldObj.setHeadingStyles(
self._build.getBool("odt.colorHeadings"),
self._build.getBool("odt.scaleHeadings"),
self._build.getBool("odt.boldHeadings"),
)
bldObj.setBodyText(self._build.getBool("text.includeBodyText")) bldObj.setBodyText(self._build.getBool("text.includeBodyText"))
bldObj.setSynopsis(self._build.getBool("text.includeSynopsis")) bldObj.setSynopsis(self._build.getBool("text.includeSynopsis"))
@@ -355,10 +355,10 @@ class NWBuildDocument:
bldObj.setReplaceUnicode(self._build.getBool("format.stripUnicode")) bldObj.setReplaceUnicode(self._build.getBool("format.stripUnicode"))
if isinstance(bldObj, ToOdt): if isinstance(bldObj, ToOdt):
bldObj.setColourHeaders(self._build.getBool("odt.addColours"))
bldObj.setLanguage(self._project.data.language) bldObj.setLanguage(self._project.data.language)
bldObj.setHeaderFormat( bldObj.setHeaderFormat(
self._build.getStr("odt.pageHeader"), self._build.getInt("odt.pageCountOffset") self._build.getStr("odt.pageHeader"),
self._build.getInt("odt.pageCountOffset"),
) )
scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0) scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0)
@@ -32,7 +32,7 @@ from time import time
from novelwriter.common import formatTimeStamp from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import T_Formats, Tokenizer, stripEscape from novelwriter.formats.tokenizer import T_Formats, Tokenizer, stripEscape
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS from novelwriter.types import FONT_STYLE, FONT_WEIGHTS
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -290,51 +290,51 @@ class ToHtml(Tokenizer):
return return
def saveHtml5(self, path: str | Path) -> None: def saveDocument(self, path: str | Path, asJson: bool = False) -> None:
"""Save the data to an HTML file.""" """Save the data to an HTML file."""
with open(path, mode="w", encoding="utf-8") as fObj: if asJson:
fObj.write(( ts = time()
"<!DOCTYPE html>\n" data = {
"<html>\n" "meta": {
"<head>\n" "projectName": self._project.data.name,
"<meta charset='utf-8'>\n" "novelAuthor": self._project.data.author,
"<title>{title:s}</title>\n" "buildTime": int(ts),
"</head>\n" "buildTimeStr": formatTimeStamp(ts),
"<style>\n" },
"{style:s}\n" "text": {
"</style>\n" "css": self.getStyleSheet(),
"<body>\n" "html": [t.replace("\t", "&#09;").rstrip().split("\n") for t in self.fullHTML],
"<article>\n" }
"{body:s}\n"
"</article>\n"
"</body>\n"
"</html>\n"
).format(
title=self._project.data.name,
style="\n".join(self.getStyleSheet()),
body=("".join(self._fullHTML)).replace("\t", "&#09;").rstrip(),
))
logger.info("Wrote file: %s", path)
return
def saveHtmlJson(self, path: str | Path) -> None:
"""Save the data to a JSON file."""
timeStamp = time()
data = {
"meta": {
"projectName": self._project.data.name,
"novelAuthor": self._project.data.author,
"buildTime": int(timeStamp),
"buildTimeStr": formatTimeStamp(timeStamp),
},
"text": {
"css": self.getStyleSheet(),
"html": [t.replace("\t", "&#09;").rstrip().split("\n") for t in self.fullHTML],
} }
} with open(path, mode="w", encoding="utf-8") as fObj:
with open(path, mode="w", encoding="utf-8") as fObj: json.dump(data, fObj, indent=2)
json.dump(data, fObj, indent=2)
else:
with open(path, mode="w", encoding="utf-8") as fObj:
fObj.write((
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<meta charset='utf-8'>\n"
"<title>{title:s}</title>\n"
"</head>\n"
"<style>\n"
"{style:s}\n"
"</style>\n"
"<body>\n"
"<article>\n"
"{body:s}\n"
"</article>\n"
"</body>\n"
"</html>\n"
).format(
title=self._project.data.name,
style="\n".join(self.getStyleSheet()),
body=("".join(self._fullHTML)).replace("\t", "&#09;").rstrip(),
))
logger.info("Wrote file: %s", path) logger.info("Wrote file: %s", path)
return return
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None: def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None:
@@ -150,18 +150,21 @@ class Tokenizer(ABC):
# User Settings # User Settings
self._textFont = QFont("Serif", 11) # Output text font self._textFont = QFont("Serif", 11) # Output text font
self._lineHeight = 1.15 # Line height in units of em self._lineHeight = 1.15 # Line height in units of em
self._blockIndent = 4.00 # Block indent in units of em self._colorHeads = True # Colourise headings
self._firstIndent = False # Enable first line indent self._scaleHeads = True # Scale headings to larger font size
self._firstWidth = 1.40 # First line indent in units of em self._boldHeads = True # Bold headings
self._indentFirst = False # Indent first paragraph self._blockIndent = 4.00 # Block indent in units of em
self._doJustify = False # Justify text self._firstIndent = False # Enable first line indent
self._doBodyText = True # Include body text self._firstWidth = 1.40 # First line indent in units of em
self._doSynopsis = False # Also process synopsis comments self._indentFirst = False # Indent first paragraph
self._doComments = False # Also process comments self._doJustify = False # Justify text
self._doKeywords = False # Also process keywords like tags and references self._doBodyText = True # Include body text
self._skipKeywords = set() # Keywords to ignore self._doSynopsis = False # Also process synopsis comments
self._keepBreaks = True # Keep line breaks in paragraphs self._doComments = False # Also process comments
self._doKeywords = False # Also process keywords like tags and references
self._skipKeywords = set() # Keywords to ignore
self._keepBreaks = True # Keep line breaks in paragraphs
# Margins # Margins
self._marginTitle = (1.417, 0.500) self._marginTitle = (1.417, 0.500)
@@ -175,14 +178,14 @@ class Tokenizer(ABC):
self._marginSep = (1.168, 1.168) self._marginSep = (1.168, 1.168)
# Title Formats # Title Formats
self._fmtTitle = nwHeadFmt.TITLE # Formatting for titles self._fmtPart = nwHeadFmt.TITLE # Formatting for partitions
self._fmtChapter = nwHeadFmt.TITLE # Formatting for numbered chapters self._fmtChapter = nwHeadFmt.TITLE # Formatting for numbered chapters
self._fmtUnNum = nwHeadFmt.TITLE # Formatting for unnumbered chapters self._fmtUnNum = nwHeadFmt.TITLE # Formatting for unnumbered chapters
self._fmtScene = nwHeadFmt.TITLE # Formatting for scenes self._fmtScene = nwHeadFmt.TITLE # Formatting for scenes
self._fmtHScene = nwHeadFmt.TITLE # Formatting for hard scenes self._fmtHScene = nwHeadFmt.TITLE # Formatting for hard scenes
self._fmtSection = nwHeadFmt.TITLE # Formatting for sections self._fmtSection = nwHeadFmt.TITLE # Formatting for sections
self._hideTitle = False # Do not include title headings self._hidePart = False # Do not include partition headings
self._hideChapter = False # Do not include chapter headings self._hideChapter = False # Do not include chapter headings
self._hideUnNum = False # Do not include unnumbered headings self._hideUnNum = False # Do not include unnumbered headings
self._hideScene = False # Do not include scene headings self._hideScene = False # Do not include scene headings
@@ -192,6 +195,7 @@ class Tokenizer(ABC):
self._linkHeadings = False # Add an anchor before headings self._linkHeadings = False # Add an anchor before headings
self._titleStyle = self.A_CENTRE | self.A_PBB self._titleStyle = self.A_CENTRE | self.A_PBB
self._partStyle = self.A_CENTRE | self.A_PBB
self._chapterStyle = self.A_PBB self._chapterStyle = self.A_PBB
self._sceneStyle = self.A_NONE self._sceneStyle = self.A_NONE
@@ -271,10 +275,10 @@ class Tokenizer(ABC):
# Setters # Setters
## ##
def setTitleFormat(self, hFormat: str, hide: bool = False) -> None: def setPartitionFormat(self, hFormat: str, hide: bool = False) -> None:
"""Set the title format pattern.""" """Set the partition format pattern."""
self._fmtTitle = hFormat.strip() self._fmtPart = hFormat.strip()
self._hideTitle = hide self._hidePart = hide
return return
def setChapterFormat(self, hFormat: str, hide: bool = False) -> None: def setChapterFormat(self, hFormat: str, hide: bool = False) -> None:
@@ -314,6 +318,13 @@ class Tokenizer(ABC):
) )
return return
def setPartitionStyle(self, center: bool, pageBreak: bool) -> None:
"""Set the partition heading style."""
self._partStyle = (
(self.A_CENTRE if center else self.A_NONE) | (self.A_PBB if pageBreak else self.A_NONE)
)
return
def setChapterStyle(self, center: bool, pageBreak: bool) -> None: def setChapterStyle(self, center: bool, pageBreak: bool) -> None:
"""Set the chapter heading style.""" """Set the chapter heading style."""
self._chapterStyle = ( self._chapterStyle = (
@@ -338,6 +349,13 @@ class Tokenizer(ABC):
self._lineHeight = min(max(float(height), 0.5), 5.0) self._lineHeight = min(max(float(height), 0.5), 5.0)
return return
def setHeadingStyles(self, color: bool, scale: bool, bold: bool) -> None:
"""Set text style for headings."""
self._colorHeads = color
self._scaleHeads = scale
self._boldHeads = bold
return
def setBlockIndent(self, indent: float) -> None: def setBlockIndent(self, indent: float) -> None:
"""Set the block indent between 0.0 and 10.0.""" """Set the block indent between 0.0 and 10.0."""
self._blockIndent = min(max(float(indent), 0.0), 10.0) self._blockIndent = min(max(float(indent), 0.0), 10.0)
@@ -468,6 +486,10 @@ class Tokenizer(ABC):
def doConvert(self) -> None: def doConvert(self) -> None:
raise NotImplementedError raise NotImplementedError
@abstractmethod
def saveDocument(self, path: str | Path) -> None:
raise NotImplementedError
def addRootHeading(self, tHandle: str) -> None: def addRootHeading(self, tHandle: str) -> None:
"""Add a heading at the start of a new root folder.""" """Add a heading at the start of a new root folder."""
self._text = "" self._text = ""
@@ -663,16 +685,16 @@ class Tokenizer(ABC):
nHead += 1 nHead += 1
tText = aLine[2:].strip() tText = aLine[2:].strip()
tType = self.T_HEAD1 if isPlain else self.T_TITLE tType = self.T_HEAD1 if isPlain else self.T_TITLE
tStyle = self.A_NONE if isPlain else (self.A_PBB | self.A_CENTRE) tStyle = self.A_NONE if isPlain else self._titleStyle
sHide = self._hideTitle if isPlain else False sHide = self._hidePart if isPlain else False
if self._isNovel: if self._isNovel:
if sHide: if sHide:
tText = "" tText = ""
tType = self.T_EMPTY tType = self.T_EMPTY
tStyle = self.A_NONE tStyle = self.A_NONE
elif isPlain: elif isPlain:
tText = self._hFormatter.apply(self._fmtTitle, tText, nHead) tText = self._hFormatter.apply(self._fmtPart, tText, nHead)
tStyle = self._titleStyle tStyle = self._partStyle
if isPlain: if isPlain:
self._hFormatter.resetScene() self._hFormatter.resetScene()
else: else:
@@ -1069,29 +1091,31 @@ class Tokenizer(ABC):
return return
def saveRawMarkdown(self, path: str | Path) -> None: def saveRawDocument(self, path: str | Path, asJson: bool = False) -> None:
"""Save the raw text to a plain text file.""" """Save the raw text to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile: if asJson:
for nwdPage in self._markdown: ts = time()
outFile.write(nwdPage) data = {
return "meta": {
"projectName": self._project.data.name,
def saveRawMarkdownJSON(self, path: str | Path) -> None: "novelAuthor": self._project.data.author,
"""Save the raw text to a JSON file.""" "buildTime": int(ts),
timeStamp = time() "buildTimeStr": formatTimeStamp(ts),
data = { },
"meta": { "text": {
"projectName": self._project.data.name, "nwd": [page.rstrip("\n").split("\n") for page in self._markdown],
"novelAuthor": self._project.data.author, }
"buildTime": int(timeStamp),
"buildTimeStr": formatTimeStamp(timeStamp),
},
"text": {
"nwd": [page.rstrip("\n").split("\n") for page in self._markdown],
} }
} with open(path, mode="w", encoding="utf-8") as fObj:
with open(path, mode="w", encoding="utf-8") as fObj: json.dump(data, fObj, indent=2)
json.dump(data, fObj, indent=2)
else:
with open(path, mode="w", encoding="utf-8") as outFile:
for nwdPage in self._markdown:
outFile.write(nwdPage)
logger.info("Wrote file: %s", path)
return return
## ##
@@ -29,7 +29,7 @@ from pathlib import Path
from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import T_Formats, Tokenizer from novelwriter.formats.tokenizer import T_Formats, Tokenizer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -199,7 +199,7 @@ class ToMarkdown(Tokenizer):
return return
def saveMarkdown(self, path: str | Path) -> None: def saveDocument(self, path: str | Path) -> None:
"""Save the data to a plain text file.""" """Save the data to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile: with open(path, mode="w", encoding="utf-8") as outFile:
outFile.write("".join(self._fullMD)) outFile.write("".join(self._fullMD))
@@ -41,7 +41,7 @@ from novelwriter import __version__
from novelwriter.common import xmlIndent from novelwriter.common import xmlIndent
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import T_Formats, Tokenizer, stripEscape from novelwriter.formats.tokenizer import T_Formats, Tokenizer, stripEscape
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS from novelwriter.types import FONT_STYLE, FONT_WEIGHTS
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -161,7 +161,7 @@ class ToOdt(Tokenizer):
# Properties # Properties
self._textFont = QFont("Liberation Serif", 12) self._textFont = QFont("Liberation Serif", 12)
self._colourHead = False self._headWeight = "bold"
self._headerFormat = "" self._headerFormat = ""
self._pageOffset = 0 self._pageOffset = 0
@@ -224,10 +224,10 @@ class ToOdt(Tokenizer):
self._opaHead12 = None self._opaHead12 = None
self._colHead34 = None self._colHead34 = None
self._opaHead34 = None self._opaHead34 = None
self._colMetaTx = None
self._opaMetaTx = None
self._colDialogM = None self._colDialogM = None
self._colDialogA = None self._colDialogA = None
self._colMetaTx = "#813709"
self._opaMetaTx = "100%"
self._markText = "#ffffa6" self._markText = "#ffffa6"
return return
@@ -244,11 +244,6 @@ class ToOdt(Tokenizer):
self._dCountry = country or self._dCountry self._dCountry = country or self._dCountry
return return
def setColourHeaders(self, state: bool) -> None:
"""Enable/disable coloured headings and comments."""
self._colourHead = state
return
def setPageLayout( def setPageLayout(
self, width: int | float, height: int | float, self, width: int | float, height: int | float,
top: int | float, bottom: int | float, left: int | float, right: int | float top: int | float, bottom: int | float, left: int | float, right: int | float
@@ -287,13 +282,15 @@ class ToOdt(Tokenizer):
self._fontStyle = FONT_STYLE.get(self._textFont.style(), "normal") self._fontStyle = FONT_STYLE.get(self._textFont.style(), "normal")
self._fontPitch = "fixed" if self._textFont.fixedPitch() else "variable" self._fontPitch = "fixed" if self._textFont.fixedPitch() else "variable"
self._fontBold = FONT_WEIGHT_MAP.get(fontBold, fontBold) self._fontBold = FONT_WEIGHT_MAP.get(fontBold, fontBold)
self._headWeight = self._fontBold if self._boldHeads else None
self._fSizeTitle = f"{round(2.50 * self._fontSize):d}pt" hScale = self._scaleHeads
self._fSizeHead1 = f"{round(2.00 * self._fontSize):d}pt" self._fSizeTitle = f"{round((2.50 if hScale else 1.0) * self._fontSize):d}pt"
self._fSizeHead2 = f"{round(1.60 * self._fontSize):d}pt" self._fSizeHead1 = f"{round((2.00 if hScale else 1.0) * self._fontSize):d}pt"
self._fSizeHead3 = f"{round(1.30 * self._fontSize):d}pt" self._fSizeHead2 = f"{round((1.60 if hScale else 1.0) * self._fontSize):d}pt"
self._fSizeHead4 = f"{round(1.15 * self._fontSize):d}pt" self._fSizeHead3 = f"{round((1.30 if hScale else 1.0) * self._fontSize):d}pt"
self._fSizeHead = f"{round(1.15 * self._fontSize):d}pt" self._fSizeHead4 = f"{round((1.15 if hScale else 1.0) * self._fontSize):d}pt"
self._fSizeHead = f"{round((1.15 if hScale else 1.0) * self._fontSize):d}pt"
self._fSizeText = f"{self._fontSize:d}pt" self._fSizeText = f"{self._fontSize:d}pt"
self._fSizeFoot = f"{round(0.8*self._fontSize):d}pt" self._fSizeFoot = f"{round(0.8*self._fontSize):d}pt"
@@ -322,13 +319,11 @@ class ToOdt(Tokenizer):
self._mLeftFoot = self._emToCm(self._marginFoot[0]) self._mLeftFoot = self._emToCm(self._marginFoot[0])
self._mBotFoot = self._emToCm(self._marginFoot[1]) self._mBotFoot = self._emToCm(self._marginFoot[1])
if self._colourHead: if self._colorHeads:
self._colHead12 = "#2a6099" self._colHead12 = "#2a6099"
self._opaHead12 = "100%" self._opaHead12 = "100%"
self._colHead34 = "#444444" self._colHead34 = "#444444"
self._opaHead34 = "100%" self._opaHead34 = "100%"
self._colMetaTx = "#813709"
self._opaMetaTx = "100%"
if self._showDialog: if self._showDialog:
self._colDialogM = "#2a6099" self._colDialogM = "#2a6099"
@@ -548,46 +543,44 @@ class ToOdt(Tokenizer):
self._xText.insert(0, xFields) self._xText.insert(0, xFields)
return return
def saveFlatXML(self, path: str | Path) -> None: def saveDocument(self, path: str | Path) -> None:
"""Save the data to an .fodt file.""" """Save the data to an .fodt or .odt file."""
with open(path, mode="wb") as fObj: if self._isFlat:
xml = ET.ElementTree(self._dFlat) with open(path, mode="wb") as fObj:
xmlIndent(xml) xml = ET.ElementTree(self._dFlat)
xml.write(fObj, encoding="utf-8", xml_declaration=True) xmlIndent(xml)
logger.info("Wrote file: %s", path)
return
def saveOpenDocText(self, path: str | Path) -> None:
"""Save the data to an .odt file."""
mMani = _mkTag("manifest", "manifest")
mVers = _mkTag("manifest", "version")
mPath = _mkTag("manifest", "full-path")
mType = _mkTag("manifest", "media-type")
mFile = _mkTag("manifest", "file-entry")
xMani = ET.Element(mMani, attrib={mVers: X_VERS})
ET.SubElement(xMani, mFile, attrib={mPath: "/", mVers: X_VERS, mType: X_MIME})
ET.SubElement(xMani, mFile, attrib={mPath: "settings.xml", mType: "text/xml"})
ET.SubElement(xMani, mFile, attrib={mPath: "content.xml", mType: "text/xml"})
ET.SubElement(xMani, mFile, attrib={mPath: "meta.xml", mType: "text/xml"})
ET.SubElement(xMani, mFile, attrib={mPath: "styles.xml", mType: "text/xml"})
oRoot = _mkTag("office", "document-settings")
oVers = _mkTag("office", "version")
xSett = ET.Element(oRoot, attrib={oVers: X_VERS})
def putInZip(name: str, xObj: ET.Element, zipObj: ZipFile) -> None:
with zipObj.open(name, mode="w") as fObj:
xml = ET.ElementTree(xObj)
xml.write(fObj, encoding="utf-8", xml_declaration=True) xml.write(fObj, encoding="utf-8", xml_declaration=True)
with ZipFile(path, mode="w") as outZip: else:
outZip.writestr("mimetype", X_MIME) mMani = _mkTag("manifest", "manifest")
putInZip("META-INF/manifest.xml", xMani, outZip) mVers = _mkTag("manifest", "version")
putInZip("settings.xml", xSett, outZip) mPath = _mkTag("manifest", "full-path")
putInZip("content.xml", self._dCont, outZip) mType = _mkTag("manifest", "media-type")
putInZip("meta.xml", self._dMeta, outZip) mFile = _mkTag("manifest", "file-entry")
putInZip("styles.xml", self._dStyl, outZip)
xMani = ET.Element(mMani, attrib={mVers: X_VERS})
ET.SubElement(xMani, mFile, attrib={mPath: "/", mVers: X_VERS, mType: X_MIME})
ET.SubElement(xMani, mFile, attrib={mPath: "settings.xml", mType: "text/xml"})
ET.SubElement(xMani, mFile, attrib={mPath: "content.xml", mType: "text/xml"})
ET.SubElement(xMani, mFile, attrib={mPath: "meta.xml", mType: "text/xml"})
ET.SubElement(xMani, mFile, attrib={mPath: "styles.xml", mType: "text/xml"})
oRoot = _mkTag("office", "document-settings")
oVers = _mkTag("office", "version")
xSett = ET.Element(oRoot, attrib={oVers: X_VERS})
def putInZip(name: str, xObj: ET.Element, zipObj: ZipFile) -> None:
with zipObj.open(name, mode="w") as fObj:
xml = ET.ElementTree(xObj)
xml.write(fObj, encoding="utf-8", xml_declaration=True)
with ZipFile(path, mode="w") as outZip:
outZip.writestr("mimetype", X_MIME)
putInZip("META-INF/manifest.xml", xMani, outZip)
putInZip("settings.xml", xSett, outZip)
putInZip("content.xml", self._dCont, outZip)
putInZip("meta.xml", self._dMeta, outZip)
putInZip("styles.xml", self._dStyl, outZip)
logger.info("Wrote file: %s", path) logger.info("Wrote file: %s", path)
@@ -965,7 +958,7 @@ class ToOdt(Tokenizer):
style.setFontName(self._fontFamily) style.setFontName(self._fontFamily)
style.setFontFamily(self._fontFamily) style.setFontFamily(self._fontFamily)
style.setFontSize(self._fSizeTitle) style.setFontSize(self._fSizeTitle)
style.setFontWeight(self._fontBold) style.setFontWeight(self._headWeight)
style.packXML(self._xStyl) style.packXML(self._xStyl)
self._mainPara[style.name] = style self._mainPara[style.name] = style
@@ -998,7 +991,7 @@ class ToOdt(Tokenizer):
style.setFontName(self._fontFamily) style.setFontName(self._fontFamily)
style.setFontFamily(self._fontFamily) style.setFontFamily(self._fontFamily)
style.setFontSize(self._fSizeHead1) style.setFontSize(self._fSizeHead1)
style.setFontWeight(self._fontBold) style.setFontWeight(self._headWeight)
style.setColour(self._colHead12) style.setColour(self._colHead12)
style.setOpacity(self._opaHead12) style.setOpacity(self._opaHead12)
style.packXML(self._xStyl) style.packXML(self._xStyl)
@@ -1016,7 +1009,7 @@ class ToOdt(Tokenizer):
style.setFontName(self._fontFamily) style.setFontName(self._fontFamily)
style.setFontFamily(self._fontFamily) style.setFontFamily(self._fontFamily)
style.setFontSize(self._fSizeHead2) style.setFontSize(self._fSizeHead2)
style.setFontWeight(self._fontBold) style.setFontWeight(self._headWeight)
style.setColour(self._colHead12) style.setColour(self._colHead12)
style.setOpacity(self._opaHead12) style.setOpacity(self._opaHead12)
style.packXML(self._xStyl) style.packXML(self._xStyl)
@@ -1034,7 +1027,7 @@ class ToOdt(Tokenizer):
style.setFontName(self._fontFamily) style.setFontName(self._fontFamily)
style.setFontFamily(self._fontFamily) style.setFontFamily(self._fontFamily)
style.setFontSize(self._fSizeHead3) style.setFontSize(self._fSizeHead3)
style.setFontWeight(self._fontBold) style.setFontWeight(self._headWeight)
style.setColour(self._colHead34) style.setColour(self._colHead34)
style.setOpacity(self._opaHead34) style.setOpacity(self._opaHead34)
style.packXML(self._xStyl) style.packXML(self._xStyl)
@@ -1052,7 +1045,7 @@ class ToOdt(Tokenizer):
style.setFontName(self._fontFamily) style.setFontName(self._fontFamily)
style.setFontFamily(self._fontFamily) style.setFontFamily(self._fontFamily)
style.setFontSize(self._fSizeHead4) style.setFontSize(self._fSizeHead4)
style.setFontWeight(self._fontBold) style.setFontWeight(self._headWeight)
style.setColour(self._colHead34) style.setColour(self._colHead34)
style.setOpacity(self._opaHead34) style.setOpacity(self._opaHead34)
style.packXML(self._xStyl) style.packXML(self._xStyl)
@@ -25,6 +25,8 @@ from __future__ import annotations
import logging import logging
from pathlib import Path
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat, QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat,
QTextCursor, QTextDocument QTextCursor, QTextDocument
@@ -32,7 +34,7 @@ from PyQt5.QtGui import (
from novelwriter.constants import nwHeaders, nwHeadFmt, nwKeyWords, nwLabels, nwUnicode from novelwriter.constants import nwHeaders, nwHeadFmt, nwKeyWords, nwLabels, nwUnicode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import T_Formats, Tokenizer from novelwriter.formats.tokenizer import T_Formats, Tokenizer
from novelwriter.types import ( from novelwriter.types import (
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight, QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
QtBlack, QtPageBreakAfter, QtPageBreakBefore, QtTransparent, QtBlack, QtPageBreakAfter, QtPageBreakBefore, QtTransparent,
@@ -114,12 +116,13 @@ class ToQTextDocument(Tokenizer):
self.T_HEAD4: (mPx * self._marginHead4[0], mPx * self._marginHead4[1]), self.T_HEAD4: (mPx * self._marginHead4[0], mPx * self._marginHead4[1]),
} }
hScale = self._scaleHeads
self._sHead = { self._sHead = {
self.T_TITLE: nwHeaders.H_SIZES.get(0, 1.0) * fPt, self.T_TITLE: (nwHeaders.H_SIZES.get(0, 1.0) * fPt) if hScale else fPt,
self.T_HEAD1: nwHeaders.H_SIZES.get(1, 1.0) * fPt, self.T_HEAD1: (nwHeaders.H_SIZES.get(1, 1.0) * fPt) if hScale else fPt,
self.T_HEAD2: nwHeaders.H_SIZES.get(2, 1.0) * fPt, self.T_HEAD2: (nwHeaders.H_SIZES.get(2, 1.0) * fPt) if hScale else fPt,
self.T_HEAD3: nwHeaders.H_SIZES.get(3, 1.0) * fPt, self.T_HEAD3: (nwHeaders.H_SIZES.get(3, 1.0) * fPt) if hScale else fPt,
self.T_HEAD4: nwHeaders.H_SIZES.get(4, 1.0) * fPt, self.T_HEAD4: (nwHeaders.H_SIZES.get(4, 1.0) * fPt) if hScale else fPt,
} }
self._mText = (mPx * self._marginText[0], mPx * self._marginText[1]) self._mText = (mPx * self._marginText[0], mPx * self._marginText[1])
@@ -147,9 +150,6 @@ class ToQTextDocument(Tokenizer):
self._cText.setBackground(QtTransparent) self._cText.setBackground(QtTransparent)
self._cText.setForeground(self._theme.text) self._cText.setForeground(self._theme.text)
self._cHead = QTextCharFormat(self._cText)
self._cHead.setForeground(self._theme.head)
self._cComment = QTextCharFormat(self._cText) self._cComment = QTextCharFormat(self._cText)
self._cComment.setForeground(self._theme.comment) self._cComment.setForeground(self._theme.comment)
@@ -275,6 +275,10 @@ class ToQTextDocument(Tokenizer):
return return
def saveDocument(self, path: str | Path) -> None:
"""Not implemented."""
return
def appendFootnotes(self) -> None: def appendFootnotes(self) -> None:
"""Append the footnotes in the buffer.""" """Append the footnotes in the buffer."""
if self._usedNotes: if self._usedNotes:
@@ -409,8 +413,13 @@ class ToQTextDocument(Tokenizer):
bFmt.setTopMargin(mTop) bFmt.setTopMargin(mTop)
bFmt.setBottomMargin(mBottom) bFmt.setBottomMargin(mBottom)
cFmt = QTextCharFormat(self._cText if hType == self.T_TITLE else self._cHead) self._cTitle = QTextCharFormat(self._cText)
cFmt.setFontWeight(self._bold) self._cTitle.setFontWeight(self._bold if self._boldHeads else self._normal)
hCol = self._colorHeads and hType != self.T_TITLE
cFmt = QTextCharFormat(self._cText)
cFmt.setForeground(self._theme.head if hCol else self._theme.text)
cFmt.setFontWeight(self._bold if self._boldHeads else self._normal)
cFmt.setFontPointSize(self._sHead.get(hType, 1.0)) cFmt.setFontPointSize(self._sHead.get(hType, 1.0))
if nHead >= 0: if nHead >= 0:
cFmt.setAnchorNames([f"{self._handle}:T{nHead:04d}"]) cFmt.setAnchorNames([f"{self._handle}:T{nHead:04d}"])
+1 -1
View File
@@ -39,12 +39,12 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwHeaders, nwUnicode from novelwriter.constants import nwHeaders, nwUnicode
from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
from novelwriter.enum import nwDocAction, nwDocMode, nwItemType from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.extensions.configlayout import NColourLabel from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.eventfilters import WheelEventFilter from novelwriter.extensions.eventfilters import WheelEventFilter
from novelwriter.extensions.modified import NIconToolButton from novelwriter.extensions.modified import NIconToolButton
from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
from novelwriter.types import ( from novelwriter.types import (
QtAlignCenterTop, QtKeepAnchor, QtMouseLeft, QtMoveAnchor, QtAlignCenterTop, QtKeepAnchor, QtMouseLeft, QtMoveAnchor,
+3 -3
View File
@@ -42,10 +42,10 @@ from novelwriter import CONFIG, SHARED
from novelwriter.common import fuzzyTime from novelwriter.common import fuzzyTime
from novelwriter.core.buildsettings import BuildCollection, BuildSettings from novelwriter.core.buildsettings import BuildCollection, BuildSettings
from novelwriter.core.docbuild import NWBuildDocument from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.core.tokenizer import HeadingFormatter
from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog
from novelwriter.extensions.progressbars import NProgressCircle from novelwriter.extensions.progressbars import NProgressCircle
from novelwriter.formats.tokenizer import HeadingFormatter
from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
from novelwriter.tools.manusbuild import GuiManuscriptBuild from novelwriter.tools.manusbuild import GuiManuscriptBuild
from novelwriter.tools.manussettings import GuiBuildSettings from novelwriter.tools.manussettings import GuiBuildSettings
@@ -593,7 +593,7 @@ class _DetailsWidget(QWidget):
item.setText(1, "") item.setText(1, "")
self.listView.addTopLevelItem(item) self.listView.addTopLevelItem(item)
for hFormat, hHide in [ for hFormat, hHide in [
("headings.fmtTitle", "headings.hideTitle"), ("headings.fmtPart", "headings.hidePart"),
("headings.fmtChapter", "headings.hideChapter"), ("headings.fmtChapter", "headings.hideChapter"),
("headings.fmtUnnumbered", "headings.hideUnnumbered"), ("headings.fmtUnnumbered", "headings.hideUnnumbered"),
("headings.fmtScene", "headings.hideScene"), ("headings.fmtScene", "headings.hideScene"),
+68 -61
View File
@@ -50,8 +50,8 @@ from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.extensions.switchbox import NSwitchBox from novelwriter.extensions.switchbox import NSwitchBox
from novelwriter.types import ( from novelwriter.types import (
QtAlignLeft, QtDialogApply, QtDialogClose, QtDialogSave, QtRoleAccept, QtAlignCenter, QtAlignLeft, QtDialogApply, QtDialogClose, QtDialogSave,
QtRoleApply, QtRoleReject, QtUserRole QtRoleAccept, QtRoleApply, QtRoleReject, QtUserRole
) )
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
@@ -585,20 +585,20 @@ class _HeadingsTab(NScrollablePage):
self.formatBox.setHorizontalSpacing(bSp) self.formatBox.setHorizontalSpacing(bSp)
# Title Heading # Title Heading
self.lblTitle = QLabel(self._build.getLabel("headings.fmtTitle"), self) self.lblPart = QLabel(self._build.getLabel("headings.fmtPart"), self)
self.fmtTitle = QLineEdit("", self) self.fmtPart = QLineEdit("", self)
self.fmtTitle.setReadOnly(True) self.fmtPart.setReadOnly(True)
self.btnTitle = NIconToolButton(self, iSz, "edit") self.btnPart = NIconToolButton(self, iSz, "edit")
self.btnTitle.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE)) self.btnPart.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE))
self.hdeTitle = QLabel(trHide, self) self.hdePart = QLabel(trHide, self)
self.hdeTitle.setIndent(bSp) self.hdePart.setIndent(bSp)
self.swtTitle = NSwitch(self, height=iPx) self.swtPart = NSwitch(self, height=iPx)
self.formatBox.addWidget(self.lblTitle, 0, 0) self.formatBox.addWidget(self.lblPart, 0, 0)
self.formatBox.addWidget(self.fmtTitle, 0, 1) self.formatBox.addWidget(self.fmtPart, 0, 1)
self.formatBox.addWidget(self.btnTitle, 0, 2) self.formatBox.addWidget(self.btnPart, 0, 2)
self.formatBox.addWidget(self.hdeTitle, 0, 3) self.formatBox.addWidget(self.hdePart, 0, 3)
self.formatBox.addWidget(self.swtTitle, 0, 4) self.formatBox.addWidget(self.swtPart, 0, 4)
# Chapter Heading # Chapter Heading
self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter"), self) self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter"), self)
@@ -734,56 +734,46 @@ class _HeadingsTab(NScrollablePage):
self.layoutMatrix.setVerticalSpacing(vSp) self.layoutMatrix.setVerticalSpacing(vSp)
self.layoutMatrix.setHorizontalSpacing(vSp) self.layoutMatrix.setHorizontalSpacing(vSp)
# Heading self.layoutMatrix.addWidget(QLabel(self.tr("Centre"), self), 0, 1)
self.layoutHeading = QLabel("<b>{0}</b>".format(self.tr("Additional Styling")), self) self.layoutMatrix.addWidget(QLabel(self.tr("Page Break"), self), 0, 2)
self.layoutMatrix.addWidget(self.layoutHeading, 0, 0, 1, 5)
# Title Layout # Title Layout
self.mtxTitle = QLabel(self._build.getLabel("headings.fmtTitle"), self) self.lblTitle = QLabel(self._build.getLabel("headings.styleTitle"), self)
self.centerTitle = NSwitch(self, height=iPx) self.centerTitle = NSwitch(self, height=iPx)
self.breakTitle = NSwitch(self, height=iPx) self.breakTitle = NSwitch(self, height=iPx)
lblCenterT = QLabel(self.tr("Centre"), self)
lblCenterT.setIndent(sSp)
lblBreakT = QLabel(self.tr("Page Break"), self)
lblBreakT.setIndent(sSp)
self.layoutMatrix.addWidget(self.mtxTitle, 1, 0) self.layoutMatrix.addWidget(self.lblTitle, 1, 0)
self.layoutMatrix.addWidget(lblCenterT, 1, 1) self.layoutMatrix.addWidget(self.centerTitle, 1, 1, QtAlignCenter)
self.layoutMatrix.addWidget(self.centerTitle, 1, 2) self.layoutMatrix.addWidget(self.breakTitle, 1, 2, QtAlignCenter)
self.layoutMatrix.addWidget(lblBreakT, 1, 3)
self.layoutMatrix.addWidget(self.breakTitle, 1, 4) # Partition Layout
self.lblPart = QLabel(self._build.getLabel("headings.stylePart"), self)
self.centerPart = NSwitch(self, height=iPx)
self.breakPart = NSwitch(self, height=iPx)
self.layoutMatrix.addWidget(self.lblPart, 2, 0)
self.layoutMatrix.addWidget(self.centerPart, 2, 1, QtAlignCenter)
self.layoutMatrix.addWidget(self.breakPart, 2, 2, QtAlignCenter)
# Chapter Layout # Chapter Layout
self.mtxChapter = QLabel(self._build.getLabel("headings.fmtChapter"), self) self.lblChapter = QLabel(self._build.getLabel("headings.styleChapter"), self)
self.centerChapter = NSwitch(self, height=iPx) self.centerChapter = NSwitch(self, height=iPx)
self.breakChapter = NSwitch(self, height=iPx) self.breakChapter = NSwitch(self, height=iPx)
lblCenterC = QLabel(self.tr("Centre"), self)
lblCenterC.setIndent(sSp)
lblBreakC = QLabel(self.tr("Page Break"), self)
lblBreakC.setIndent(sSp)
self.layoutMatrix.addWidget(self.mtxChapter, 2, 0) self.layoutMatrix.addWidget(self.lblChapter, 3, 0)
self.layoutMatrix.addWidget(lblCenterC, 2, 1) self.layoutMatrix.addWidget(self.centerChapter, 3, 1, QtAlignCenter)
self.layoutMatrix.addWidget(self.centerChapter, 2, 2) self.layoutMatrix.addWidget(self.breakChapter, 3, 2, QtAlignCenter)
self.layoutMatrix.addWidget(lblBreakC, 2, 3)
self.layoutMatrix.addWidget(self.breakChapter, 2, 4)
# Scene Layout # Scene Layout
self.mtxScene = QLabel(self._build.getLabel("headings.fmtScene"), self) self.lblScene = QLabel(self._build.getLabel("headings.styleScene"), self)
self.centerScene = NSwitch(self, height=iPx) self.centerScene = NSwitch(self, height=iPx)
self.breakScene = NSwitch(self, height=iPx) self.breakScene = NSwitch(self, height=iPx)
lblCenterS = QLabel(self.tr("Centre"), self)
lblCenterS.setIndent(sSp)
lblBreakS = QLabel(self.tr("Page Break"), self)
lblBreakS.setIndent(sSp)
self.layoutMatrix.addWidget(self.mtxScene, 3, 0) self.layoutMatrix.addWidget(self.lblScene, 4, 0)
self.layoutMatrix.addWidget(lblCenterS, 3, 1) self.layoutMatrix.addWidget(self.centerScene, 4, 1, QtAlignCenter)
self.layoutMatrix.addWidget(self.centerScene, 3, 2) self.layoutMatrix.addWidget(self.breakScene, 4, 2, QtAlignCenter)
self.layoutMatrix.addWidget(lblBreakS, 3, 3)
self.layoutMatrix.addWidget(self.breakScene, 3, 4)
self.layoutMatrix.setColumnStretch(5, 1) self.layoutMatrix.setColumnStretch(3, 1)
# Assemble # Assemble
# ======== # ========
@@ -802,14 +792,14 @@ class _HeadingsTab(NScrollablePage):
def loadContent(self) -> None: def loadContent(self) -> None:
"""Populate the widgets.""" """Populate the widgets."""
self.fmtTitle.setText(self._build.getStr("headings.fmtTitle")) self.fmtPart.setText(self._build.getStr("headings.fmtPart"))
self.fmtChapter.setText(self._build.getStr("headings.fmtChapter")) self.fmtChapter.setText(self._build.getStr("headings.fmtChapter"))
self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered")) self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered"))
self.fmtScene.setText(self._build.getStr("headings.fmtScene")) self.fmtScene.setText(self._build.getStr("headings.fmtScene"))
self.fmtAScene.setText(self._build.getStr("headings.fmtAltScene")) self.fmtAScene.setText(self._build.getStr("headings.fmtAltScene"))
self.fmtSection.setText(self._build.getStr("headings.fmtSection")) self.fmtSection.setText(self._build.getStr("headings.fmtSection"))
self.swtTitle.setChecked(self._build.getBool("headings.hideTitle")) self.swtPart.setChecked(self._build.getBool("headings.hidePart"))
self.swtChapter.setChecked(self._build.getBool("headings.hideChapter")) self.swtChapter.setChecked(self._build.getBool("headings.hideChapter"))
self.swtUnnumbered.setChecked(self._build.getBool("headings.hideUnnumbered")) self.swtUnnumbered.setChecked(self._build.getBool("headings.hideUnnumbered"))
self.swtScene.setChecked(self._build.getBool("headings.hideScene")) self.swtScene.setChecked(self._build.getBool("headings.hideScene"))
@@ -817,16 +807,19 @@ class _HeadingsTab(NScrollablePage):
self.swtSection.setChecked(self._build.getBool("headings.hideSection")) self.swtSection.setChecked(self._build.getBool("headings.hideSection"))
self.centerTitle.setChecked(self._build.getBool("headings.centerTitle")) self.centerTitle.setChecked(self._build.getBool("headings.centerTitle"))
self.centerPart.setChecked(self._build.getBool("headings.centerPart"))
self.centerChapter.setChecked(self._build.getBool("headings.centerChapter")) self.centerChapter.setChecked(self._build.getBool("headings.centerChapter"))
self.centerScene.setChecked(self._build.getBool("headings.centerScene")) self.centerScene.setChecked(self._build.getBool("headings.centerScene"))
self.breakTitle.setChecked(self._build.getBool("headings.breakTitle")) self.breakTitle.setChecked(self._build.getBool("headings.breakTitle"))
self.breakPart.setChecked(self._build.getBool("headings.breakPart"))
self.breakChapter.setChecked(self._build.getBool("headings.breakChapter")) self.breakChapter.setChecked(self._build.getBool("headings.breakChapter"))
self.breakScene.setChecked(self._build.getBool("headings.breakScene")) self.breakScene.setChecked(self._build.getBool("headings.breakScene"))
return return
def saveContent(self) -> None: def saveContent(self) -> None:
"""Save choices back into build object.""" """Save choices back into build object."""
self._build.setValue("headings.hideTitle", self.swtTitle.isChecked()) self._build.setValue("headings.hidePart", self.swtPart.isChecked())
self._build.setValue("headings.hideChapter", self.swtChapter.isChecked()) self._build.setValue("headings.hideChapter", self.swtChapter.isChecked())
self._build.setValue("headings.hideUnnumbered", self.swtUnnumbered.isChecked()) self._build.setValue("headings.hideUnnumbered", self.swtUnnumbered.isChecked())
self._build.setValue("headings.hideScene", self.swtScene.isChecked()) self._build.setValue("headings.hideScene", self.swtScene.isChecked())
@@ -834,9 +827,12 @@ class _HeadingsTab(NScrollablePage):
self._build.setValue("headings.hideSection", self.swtSection.isChecked()) self._build.setValue("headings.hideSection", self.swtSection.isChecked())
self._build.setValue("headings.centerTitle", self.centerTitle.isChecked()) self._build.setValue("headings.centerTitle", self.centerTitle.isChecked())
self._build.setValue("headings.centerPart", self.centerPart.isChecked())
self._build.setValue("headings.centerChapter", self.centerChapter.isChecked()) self._build.setValue("headings.centerChapter", self.centerChapter.isChecked())
self._build.setValue("headings.centerScene", self.centerScene.isChecked()) self._build.setValue("headings.centerScene", self.centerScene.isChecked())
self._build.setValue("headings.breakTitle", self.breakTitle.isChecked()) self._build.setValue("headings.breakTitle", self.breakTitle.isChecked())
self._build.setValue("headings.breakPart", self.breakPart.isChecked())
self._build.setValue("headings.breakChapter", self.breakChapter.isChecked()) self._build.setValue("headings.breakChapter", self.breakChapter.isChecked())
self._build.setValue("headings.breakScene", self.breakScene.isChecked()) self._build.setValue("headings.breakScene", self.breakScene.isChecked())
return return
@@ -858,8 +854,8 @@ class _HeadingsTab(NScrollablePage):
self._editing = heading self._editing = heading
self.editTextBox.setEnabled(True) self.editTextBox.setEnabled(True)
if heading == self.EDIT_TITLE: if heading == self.EDIT_TITLE:
text = self.fmtTitle.text() text = self.fmtPart.text()
label = self._build.getLabel("headings.fmtTitle") label = self._build.getLabel("headings.fmtPart")
elif heading == self.EDIT_CHAPTER: elif heading == self.EDIT_CHAPTER:
text = self.fmtChapter.text() text = self.fmtChapter.text()
label = self._build.getLabel("headings.fmtChapter") label = self._build.getLabel("headings.fmtChapter")
@@ -896,8 +892,8 @@ class _HeadingsTab(NScrollablePage):
heading = self._editing heading = self._editing
text = self.editTextBox.toPlainText().strip().replace("\n", nwHeadFmt.BR) text = self.editTextBox.toPlainText().strip().replace("\n", nwHeadFmt.BR)
if heading == self.EDIT_TITLE: if heading == self.EDIT_TITLE:
self.fmtTitle.setText(text) self.fmtPart.setText(text)
self._build.setValue("headings.fmtTitle", text) self._build.setValue("headings.fmtPart", text)
elif heading == self.EDIT_CHAPTER: elif heading == self.EDIT_CHAPTER:
self.fmtChapter.setText(text) self.fmtChapter.setText(text)
self._build.setValue("headings.fmtChapter", text) self._build.setValue("headings.fmtChapter", text)
@@ -1126,9 +1122,7 @@ class _FormattingTab(NScrollableForm):
self._sidebar.addButton(title, section) self._sidebar.addButton(title, section)
self.addGroupLabel(title, section) self.addGroupLabel(title, section)
self.odtAddColours = NSwitch(self, height=iPx) # Header
self.addRow(self._build.getLabel("odt.addColours"), self.odtAddColours)
self.odtPageHeader = QLineEdit(self) self.odtPageHeader = QLineEdit(self)
self.odtPageHeader.setMinimumWidth(CONFIG.pxInt(200)) self.odtPageHeader.setMinimumWidth(CONFIG.pxInt(200))
self.btnPageHeader = NIconToolButton(self, iSz, "revert") self.btnPageHeader = NIconToolButton(self, iSz, "revert")
@@ -1145,6 +1139,15 @@ class _FormattingTab(NScrollableForm):
self.odtPageCountOffset.setMinimumWidth(spW) self.odtPageCountOffset.setMinimumWidth(spW)
self.addRow(self._build.getLabel("odt.pageCountOffset"), self.odtPageCountOffset) self.addRow(self._build.getLabel("odt.pageCountOffset"), self.odtPageCountOffset)
# Headings
self.colorHeadings = NSwitch(self, height=iPx)
self.scaleHeadings = NSwitch(self, height=iPx)
self.boldHeadings = NSwitch(self, height=iPx)
self.addRow(self._build.getLabel("odt.colorHeadings"), self.colorHeadings)
self.addRow(self._build.getLabel("odt.scaleHeadings"), self.scaleHeadings)
self.addRow(self._build.getLabel("odt.boldHeadings"), self.boldHeadings)
# HTML Document # HTML Document
# ============= # =============
@@ -1229,7 +1232,9 @@ class _FormattingTab(NScrollableForm):
# ODT Document # ODT Document
# ============ # ============
self.odtAddColours.setChecked(self._build.getBool("odt.addColours")) self.colorHeadings.setChecked(self._build.getBool("odt.colorHeadings"))
self.scaleHeadings.setChecked(self._build.getBool("odt.scaleHeadings"))
self.boldHeadings.setChecked(self._build.getBool("odt.boldHeadings"))
self.odtPageHeader.setText(self._build.getStr("odt.pageHeader")) self.odtPageHeader.setText(self._build.getStr("odt.pageHeader"))
self.odtPageCountOffset.setValue(self._build.getInt("odt.pageCountOffset")) self.odtPageCountOffset.setValue(self._build.getInt("odt.pageCountOffset"))
self.odtPageHeader.setCursorPosition(0) self.odtPageHeader.setCursorPosition(0)
@@ -1279,7 +1284,9 @@ class _FormattingTab(NScrollableForm):
self._build.setValue("format.rightMargin", self.rightMargin.value()) self._build.setValue("format.rightMargin", self.rightMargin.value())
# ODT Document # ODT Document
self._build.setValue("odt.addColours", self.odtAddColours.isChecked()) self._build.setValue("odt.colorHeadings", self.colorHeadings.isChecked())
self._build.setValue("odt.scaleHeadings", self.scaleHeadings.isChecked())
self._build.setValue("odt.boldHeadings", self.boldHeadings.isChecked())
self._build.setValue("odt.pageHeader", self.odtPageHeader.text()) self._build.setValue("odt.pageHeader", self.odtPageHeader.text())
self._build.setValue("odt.pageCountOffset", self.odtPageCountOffset.value()) self._build.setValue("odt.pageCountOffset", self.odtPageCountOffset.value())
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<office:document-styles xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3"> <office:document-styles xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3">
<office:font-face-decls> <office:font-face-decls>
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" /> <style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
</office:font-face-decls> </office:font-face-decls>
@@ -25,7 +25,7 @@
</style:style> </style:style>
<style:style style:name="Text_20_Meta" style:family="paragraph" style:display-name="Text Meta" style:parent-style-name="Standard" style:class="text"> <style:style style:name="Text_20_Meta" style:family="paragraph" style:display-name="Text Meta" style:parent-style-name="Standard" style:class="text">
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.247cm" fo:line-height="115%" /> <style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.247cm" fo:line-height="115%" />
<style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="12pt" fo:font-weight="normal" /> <style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="12pt" fo:font-weight="normal" fo:color="#813709" loext:opacity="100%" />
</style:style> </style:style>
<style:style style:name="Title" style:family="paragraph" style:display-name="Title" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter"> <style:style style:name="Title" style:family="paragraph" style:display-name="Title" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:margin-top="0.600cm" fo:margin-bottom="0.212cm" fo:text-align="center" /> <style:paragraph-properties fo:margin-top="0.600cm" fo:margin-bottom="0.212cm" fo:text-align="center" />
@@ -37,19 +37,19 @@
</style:style> </style:style>
<style:style style:name="Heading_20_1" style:family="paragraph" style:display-name="Heading 1" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text"> <style:style style:name="Heading_20_1" style:family="paragraph" style:display-name="Heading 1" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text">
<style:paragraph-properties fo:margin-top="0.600cm" fo:margin-bottom="0.212cm" /> <style:paragraph-properties fo:margin-top="0.600cm" fo:margin-bottom="0.212cm" />
<style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="24pt" fo:font-weight="bold" /> <style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="24pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
</style:style> </style:style>
<style:style style:name="Heading_20_2" style:family="paragraph" style:display-name="Heading 2" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text"> <style:style style:name="Heading_20_2" style:family="paragraph" style:display-name="Heading 2" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text">
<style:paragraph-properties fo:margin-top="0.706cm" fo:margin-bottom="0.212cm" /> <style:paragraph-properties fo:margin-top="0.706cm" fo:margin-bottom="0.212cm" />
<style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="19pt" fo:font-weight="bold" /> <style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="19pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
</style:style> </style:style>
<style:style style:name="Heading_20_3" style:family="paragraph" style:display-name="Heading 3" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text"> <style:style style:name="Heading_20_3" style:family="paragraph" style:display-name="Heading 3" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text">
<style:paragraph-properties fo:margin-top="0.494cm" fo:margin-bottom="0.212cm" /> <style:paragraph-properties fo:margin-top="0.494cm" fo:margin-bottom="0.212cm" />
<style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="16pt" fo:font-weight="bold" /> <style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="16pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
</style:style> </style:style>
<style:style style:name="Heading_20_4" style:family="paragraph" style:display-name="Heading 4" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text"> <style:style style:name="Heading_20_4" style:family="paragraph" style:display-name="Heading 4" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text">
<style:paragraph-properties fo:margin-top="0.494cm" fo:margin-bottom="0.212cm" /> <style:paragraph-properties fo:margin-top="0.494cm" fo:margin-bottom="0.212cm" />
<style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="14pt" fo:font-weight="bold" /> <style:text-properties style:font-name="Liberation Serif" fo:font-family="Liberation Serif" fo:font-size="14pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
</style:style> </style:style>
<style:style style:name="Header" style:family="paragraph" style:display-name="Header" style:parent-style-name="Header_20_and_20_Footer"> <style:style style:name="Header" style:family="paragraph" style:display-name="Header" style:parent-style-name="Header_20_and_20_Footer">
<style:paragraph-properties fo:text-align="right" /> <style:paragraph-properties fo:text-align="right" />
@@ -18,7 +18,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t
_Lorem Ipsum_ is simply dummy text[1] of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. _Lorem Ipsum_ is simply dummy text[1] of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
# Title: Act One # Part: Act One
“Fusce maximus felis libero” “Fusce maximus felis libero”
@@ -34,7 +34,7 @@ mark {background: rgb(255, 255, 166);}
<h1 style='page-break-before: always;'>Prologue</h1> <h1 style='page-break-before: always;'>Prologue</h1>
<p class='synopsis'><strong>Synopsis:</strong> Explanation from the lipsum.com website.</p> <p class='synopsis'><strong>Synopsis:</strong> Explanation from the lipsum.com website.</p>
<p><em>Lorem Ipsum</em> is simply dummy text<sup><a href='#footnote_1'>1</a></sup> of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p> <p><em>Lorem Ipsum</em> is simply dummy text<sup><a href='#footnote_1'>1</a></sup> of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<h1 class='title' style='text-align: center; page-break-before: always;'>Title: Act One</h1> <h1 class='title' style='text-align: center; page-break-before: always;'>Part: Act One</h1>
<p style='text-align: center;'>“Fusce maximus felis libero”</p> <p style='text-align: center;'>“Fusce maximus felis libero”</p>
<h1 style='page-break-before: always;'>Chapter: Chapter One</h1> <h1 style='page-break-before: always;'>Chapter: Chapter One</h1>
<p class='meta meta-pov' style='margin-bottom: 0;'><span class='keyword'>Point of View:</span> <a class='tag' href='#tag_Bod'>Bod</a></p> <p class='meta meta-pov' style='margin-bottom: 0;'><span class='keyword'>Point of View:</span> <a class='tag' href='#tag_Bod'>Bod</a></p>
@@ -2,8 +2,8 @@
"meta": { "meta": {
"projectName": "Lorem Ipsum", "projectName": "Lorem Ipsum",
"novelAuthor": "lipsum.com", "novelAuthor": "lipsum.com",
"buildTime": 1718057434, "buildTime": 1727777646,
"buildTimeStr": "2024-06-11 00:10:34" "buildTimeStr": "2024-10-01 12:14:06"
}, },
"text": { "text": {
"css": [ "css": [
@@ -42,7 +42,7 @@
"<p><em>Lorem Ipsum</em> is simply dummy text<sup><a href='#footnote_1'>1</a></sup> of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>" "<p><em>Lorem Ipsum</em> is simply dummy text<sup><a href='#footnote_1'>1</a></sup> of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>"
], ],
[ [
"<h1 class='title' style='text-align: center; page-break-before: always;'>Title: Act One</h1>", "<h1 class='title' style='text-align: center; page-break-before: always;'>Part: Act One</h1>",
"<p style='text-align: center;'>\u201cFusce maximus felis libero\u201d</p>" "<p style='text-align: center;'>\u201cFusce maximus felis libero\u201d</p>"
], ],
[ [
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> <office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta> <office:meta>
<meta:creation-date>2024-09-30T14:08:16</meta:creation-date> <meta:creation-date>2024-10-01T12:11:50</meta:creation-date>
<meta:generator>novelWriter/2.6a0</meta:generator> <meta:generator>novelWriter/2.6a0</meta:generator>
<meta:initial-creator>lipsum.com</meta:initial-creator> <meta:initial-creator>lipsum.com</meta:initial-creator>
<meta:editing-cycles>45</meta:editing-cycles> <meta:editing-cycles>45</meta:editing-cycles>
<meta:editing-duration>P0DT0H36M8S</meta:editing-duration> <meta:editing-duration>P0DT0H36M8S</meta:editing-duration>
<dc:title>Lorem Ipsum</dc:title> <dc:title>Lorem Ipsum</dc:title>
<dc:date>2024-09-30T14:08:16</dc:date> <dc:date>2024-10-01T12:11:50</dc:date>
<dc:creator>lipsum.com</dc:creator> <dc:creator>lipsum.com</dc:creator>
</office:meta> </office:meta>
<office:font-face-decls> <office:font-face-decls>
@@ -122,12 +122,12 @@
<text:user-field-decl office:value-type="float" office:value="4163" text:name="ManuscriptAllWords" /> <text:user-field-decl office:value-type="float" office:value="4163" text:name="ManuscriptAllWords" />
<text:user-field-decl office:value-type="float" office:value="3809" text:name="ManuscriptTextWords" /> <text:user-field-decl office:value-type="float" office:value="3809" text:name="ManuscriptTextWords" />
<text:user-field-decl office:value-type="float" office:value="54" text:name="ManuscriptTitleWords" /> <text:user-field-decl office:value-type="float" office:value="54" text:name="ManuscriptTitleWords" />
<text:user-field-decl office:value-type="float" office:value="27849" text:name="ManuscriptAllChars" /> <text:user-field-decl office:value-type="float" office:value="27848" text:name="ManuscriptAllChars" />
<text:user-field-decl office:value-type="float" office:value="25528" text:name="ManuscriptTextChars" /> <text:user-field-decl office:value-type="float" office:value="25528" text:name="ManuscriptTextChars" />
<text:user-field-decl office:value-type="float" office:value="311" text:name="ManuscriptTitleChars" /> <text:user-field-decl office:value-type="float" office:value="310" text:name="ManuscriptTitleChars" />
<text:user-field-decl office:value-type="float" office:value="23782" text:name="ManuscriptAllWordChars" /> <text:user-field-decl office:value-type="float" office:value="23781" text:name="ManuscriptAllWordChars" />
<text:user-field-decl office:value-type="float" office:value="21761" text:name="ManuscriptTextWordChars" /> <text:user-field-decl office:value-type="float" office:value="21761" text:name="ManuscriptTextWordChars" />
<text:user-field-decl office:value-type="float" office:value="276" text:name="ManuscriptTitleWordChars" /> <text:user-field-decl office:value-type="float" office:value="275" text:name="ManuscriptTitleWordChars" />
</text:user-field-decls> </text:user-field-decls>
<text:p text:style-name="Title">Lorem Ipsum</text:p> <text:p text:style-name="Title">Lorem Ipsum</text:p>
<text:p text:style-name="P1"><text:span text:style-name="T1">By lipsum.com</text:span></text:p> <text:p text:style-name="P1"><text:span text:style-name="T1">By lipsum.com</text:span></text:p>
@@ -144,7 +144,7 @@
<text:p text:style-name="Footnote"><text:span text:style-name="T2">Lorem ipsum</text:span> is typically a corrupted version of De finibus bonorum et malorum, a 1st-century BC text by the Roman statesman and philosopher Cicero, with words altered, added, and removed to make it nonsensical and improper Latin. (Source: Wikipedia)</text:p> <text:p text:style-name="Footnote"><text:span text:style-name="T2">Lorem ipsum</text:span> is typically a corrupted version of De finibus bonorum et malorum, a 1st-century BC text by the Roman statesman and philosopher Cicero, with words altered, added, and removed to make it nonsensical and improper Latin. (Source: Wikipedia)</text:p>
</text:note-body> </text:note-body>
</text:note> of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</text:p> </text:note> of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</text:p>
<text:h text:style-name="P4" text:outline-level="1">Title: Act One</text:h> <text:h text:style-name="P4" text:outline-level="1">Part: Act One</text:h>
<text:p text:style-name="P1">“Fusce maximus felis libero”</text:p> <text:p text:style-name="P1">“Fusce maximus felis libero”</text:p>
<text:h text:style-name="P3" text:outline-level="2">Chapter: Chapter One</text:h> <text:h text:style-name="P3" text:outline-level="2">Chapter: Chapter One</text:h>
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p> <text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
@@ -18,7 +18,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t
_Lorem Ipsum_ is simply dummy text[1] of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. _Lorem Ipsum_ is simply dummy text[1] of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
# Title: Act One # Part: Act One
“Fusce maximus felis libero” “Fusce maximus felis libero”
+17 -18
View File
@@ -29,7 +29,7 @@ from pathlib import Path
import pytest import pytest
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles, nwHeadFmt
from novelwriter.core.buildsettings import BuildCollection, BuildSettings, FilterMode from novelwriter.core.buildsettings import BuildCollection, BuildSettings, FilterMode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.enum import nwBuildFmt, nwItemClass from novelwriter.enum import nwBuildFmt, nwItemClass
@@ -147,49 +147,48 @@ def testCoreBuildSettings_BuildValues():
"""Test BuildSettings get/set of build values.""" """Test BuildSettings get/set of build values."""
build = BuildSettings() build = BuildSettings()
strSetting = "headings.fmtTitle" strSetting = "headings.fmtPart"
intSetting = "odt.pageCountOffset" intSetting = "odt.pageCountOffset"
boolSetting = "filter.includeNovel" boolSetting = "filter.includeNovel"
floatSetting = "format.lineHeight" floatSetting = "format.lineHeight"
# Invalid setting # Invalid setting
assert build.setValue("foo", "bar") is False build.setValue("foo", "bar")
assert build.getStr("foo") == "None"
# Value must be correct type # Value must be correct type
assert build.setValue(strSetting, 15) is False build.setValue(strSetting, 15)
assert build.setValue(intSetting, 15.0) is False assert build.getStr(strSetting) == nwHeadFmt.TITLE
assert build.setValue(boolSetting, "string") is False build.setValue(intSetting, 15.0)
assert build.setValue(floatSetting, 15) is False assert build.getInt(intSetting) == 0
build.setValue(floatSetting, 15)
# Check min/max range assert build.getFloat(floatSetting) == 1.15
assert build.setValue(floatSetting, 200.0) is True build.setValue(boolSetting, "string")
assert build.getFloat(floatSetting) == 3.0 assert build.getBool(boolSetting) is True
assert build.setValue(floatSetting, 0.0) is True
assert build.getFloat(floatSetting) == 0.75
# Check string values # Check string values
assert build.setValue(strSetting, "foobar") is True build.setValue(strSetting, "foobar")
assert build.getStr(strSetting) == "foobar" assert build.getStr(strSetting) == "foobar"
assert build.getInt(strSetting) == 0 assert build.getInt(strSetting) == 0
assert build.getBool(strSetting) is True assert build.getBool(strSetting) is True
assert build.getFloat(strSetting) == 0.0 assert build.getFloat(strSetting) == 0.0
# Check int values # Check int values
assert build.setValue(intSetting, 42) is True build.setValue(intSetting, 42)
assert build.getStr(intSetting) == "42" assert build.getStr(intSetting) == "42"
assert build.getInt(intSetting) == 42 assert build.getInt(intSetting) == 42
assert build.getBool(intSetting) is True assert build.getBool(intSetting) is True
assert build.getFloat(intSetting) == 42.0 assert build.getFloat(intSetting) == 42.0
# Check bool values # Check bool values
assert build.setValue(boolSetting, True) is True build.setValue(boolSetting, True)
assert build.getStr(boolSetting) == "True" assert build.getStr(boolSetting) == "True"
assert build.getInt(boolSetting) == 1 assert build.getInt(boolSetting) == 1
assert build.getBool(boolSetting) is True assert build.getBool(boolSetting) is True
assert build.getFloat(boolSetting) == 1.0 assert build.getFloat(boolSetting) == 1.0
# Check float values # Check float values
assert build.setValue(floatSetting, 2.5) is True build.setValue(floatSetting, 2.5)
assert build.getStr(floatSetting) == "2.5" assert build.getStr(floatSetting) == "2.5"
assert build.getInt(floatSetting) == 2 assert build.getInt(floatSetting) == 2
assert build.getBool(floatSetting) is True assert build.getBool(floatSetting) is True
@@ -334,7 +333,7 @@ def testCoreBuildSettings_Filters(mockGUI, fncPath: Path, mockRnd):
hCharDoc: (True, FilterMode.INCLUDED), hCharDoc: (True, FilterMode.INCLUDED),
} }
# Set everything back to filered # Set everything back to filtered
build.setValue("filter.includeNotes", False) build.setValue("filter.includeNotes", False)
build.setAllowRoot(C.hPlotRoot, False) build.setAllowRoot(C.hPlotRoot, False)
build.setAllowRoot(C.hCharRoot, True) build.setAllowRoot(C.hCharRoot, True)
+6 -6
View File
@@ -30,10 +30,10 @@ import pytest
from novelwriter.core.buildsettings import BuildSettings from novelwriter.core.buildsettings import BuildSettings
from novelwriter.core.docbuild import NWBuildDocument from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tohtml import ToHtml
from novelwriter.core.tomarkdown import ToMarkdown
from novelwriter.core.toodt import ToOdt
from novelwriter.enum import nwBuildFmt from novelwriter.enum import nwBuildFmt
from novelwriter.formats.tohtml import ToHtml
from novelwriter.formats.tomarkdown import ToMarkdown
from novelwriter.formats.toodt import ToOdt
from tests.mocked import causeException, causeOSError from tests.mocked import causeException, causeOSError
from tests.tools import ODT_IGNORE, C, buildTestProject, cmpFiles from tests.tools import ODT_IGNORE, C, buildTestProject, cmpFiles
@@ -45,7 +45,7 @@ BUILD_CONF = {
"filter.includeNovel": True, "filter.includeNovel": True,
"filter.includeNotes": True, "filter.includeNotes": True,
"filter.includeInactive": True, "filter.includeInactive": True,
"headings.fmtTitle": "Title: {Title}", "headings.fmtPart": "Part: {Title}",
"headings.fmtChapter": "Chapter: {Title}", "headings.fmtChapter": "Chapter: {Title}",
"headings.fmtUnnumbered": "{Title}", "headings.fmtUnnumbered": "{Title}",
"headings.fmtScene": "Scene: {Title}", "headings.fmtScene": "Scene: {Title}",
@@ -64,7 +64,7 @@ BUILD_CONF = {
"format.stripUnicode": False, "format.stripUnicode": False,
"format.replaceTabs": True, "format.replaceTabs": True,
"format.firstLineIndent": True, "format.firstLineIndent": True,
"odt.addColours": True, "odt.colorHeadings": True,
"html.addStyles": True, "html.addStyles": True,
}, },
"content": { "content": {
@@ -146,7 +146,7 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
# ================== # ==================
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.toodt.ToOdt.doConvert", causeException) mp.setattr("novelwriter.formats.toodt.ToOdt.doConvert", causeException)
assert len(docBuild) == 21 assert len(docBuild) == 21
count = 0 count = 0
@@ -26,11 +26,11 @@ import pytest
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tohtml import ToHtml from novelwriter.formats.tohtml import ToHtml
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_ConvertHeaders(mockGUI): def testFmtToHtml_ConvertHeaders(mockGUI):
"""Test header formats in the ToHtml class.""" """Test header formats in the ToHtml class."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -132,7 +132,7 @@ def testCoreToHtml_ConvertHeaders(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_ConvertParagraphs(mockGUI): def testFmtToHtml_ConvertParagraphs(mockGUI):
"""Test paragraph formats in the ToHtml class.""" """Test paragraph formats in the ToHtml class."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -309,7 +309,7 @@ def testCoreToHtml_ConvertParagraphs(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_CloseTags(mockGUI): def testFmtToHtml_CloseTags(mockGUI):
"""Test automatic closing of HTML tags for shortcodes.""" """Test automatic closing of HTML tags for shortcodes."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -345,7 +345,7 @@ def testCoreToHtml_CloseTags(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_ConvertDirect(mockGUI): def testFmtToHtml_ConvertDirect(mockGUI):
"""Test the converter directly using the ToHtml class.""" """Test the converter directly using the ToHtml class."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -492,7 +492,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_SpecialCases(mockGUI): def testFmtToHtml_SpecialCases(mockGUI):
"""Test some special cases that have caused errors in the past.""" """Test some special cases that have caused errors in the past."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -564,7 +564,7 @@ def testCoreToHtml_SpecialCases(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_Save(mockGUI, fncPath): def testFmtToHtml_Save(mockGUI, fncPath):
"""Test the save method of the ToHtml class.""" """Test the save method of the ToHtml class."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -644,12 +644,12 @@ def testCoreToHtml_Save(mockGUI, fncPath):
) )
saveFile = fncPath / "outFile.htm" saveFile = fncPath / "outFile.htm"
html.saveHtml5(saveFile) html.saveDocument(saveFile, asJson=False)
assert saveFile.read_text(encoding="utf-8") == htmlDoc assert saveFile.read_text(encoding="utf-8") == htmlDoc
# JSON + HTML # JSON + HTML
saveFile = fncPath / "outFile.json" saveFile = fncPath / "outFile.json"
html.saveHtmlJson(saveFile) html.saveDocument(saveFile, asJson=True)
data = json.loads(saveFile.read_text(encoding="utf-8")) data = json.loads(saveFile.read_text(encoding="utf-8"))
assert data["meta"]["projectName"] == "" assert data["meta"]["projectName"] == ""
assert data["meta"]["novelAuthor"] == "" assert data["meta"]["novelAuthor"] == ""
@@ -660,7 +660,7 @@ def testCoreToHtml_Save(mockGUI, fncPath):
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_Methods(mockGUI): def testFmtToHtml_Methods(mockGUI):
"""Test all the other methods of the ToHtml class.""" """Test all the other methods of the ToHtml class."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -707,7 +707,7 @@ def testCoreToHtml_Methods(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToHtml_Format(mockGUI): def testFmtToHtml_Format(mockGUI):
"""Test all the formatters for the ToHtml class.""" """Test all the formatters for the ToHtml class."""
project = NWProject() project = NWProject()
html = ToHtml(project) html = ToHtml(project)
@@ -29,8 +29,8 @@ from PyQt5.QtGui import QFont
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwHeadFmt from novelwriter.constants import nwHeadFmt
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import HeadingFormatter, Tokenizer, stripEscape from novelwriter.formats.tokenizer import HeadingFormatter, Tokenizer, stripEscape
from novelwriter.core.tomarkdown import ToMarkdown from novelwriter.formats.tomarkdown import ToMarkdown
from tests.tools import C, buildTestProject, readFile from tests.tools import C, buildTestProject, readFile
@@ -39,15 +39,18 @@ class BareTokenizer(Tokenizer):
def doConvert(self): def doConvert(self):
super().doConvert() # type: ignore (deliberate check) super().doConvert() # type: ignore (deliberate check)
def saveDocument(self, path) -> None:
super().saveDocument(path) # type: ignore (deliberate check)
@pytest.mark.core @pytest.mark.core
def testCoreToken_Setters(mockGUI): def testFmtToken_Setters(mockGUI):
"""Test all the setters for the Tokenizer class.""" """Test all the setters for the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
# Verify defaults # Verify defaults
assert tokens._fmtTitle == nwHeadFmt.TITLE assert tokens._fmtPart == nwHeadFmt.TITLE
assert tokens._fmtChapter == nwHeadFmt.TITLE assert tokens._fmtChapter == nwHeadFmt.TITLE
assert tokens._fmtUnNum == nwHeadFmt.TITLE assert tokens._fmtUnNum == nwHeadFmt.TITLE
assert tokens._fmtScene == nwHeadFmt.TITLE assert tokens._fmtScene == nwHeadFmt.TITLE
@@ -65,7 +68,7 @@ def testCoreToken_Setters(mockGUI):
assert tokens._marginText == (0.000, 0.584) assert tokens._marginText == (0.000, 0.584)
assert tokens._marginMeta == (0.000, 0.584) assert tokens._marginMeta == (0.000, 0.584)
assert tokens._marginSep == (1.168, 1.168) assert tokens._marginSep == (1.168, 1.168)
assert tokens._hideTitle is False assert tokens._hidePart is False
assert tokens._hideChapter is False assert tokens._hideChapter is False
assert tokens._hideUnNum is False assert tokens._hideUnNum is False
assert tokens._hideScene is False assert tokens._hideScene is False
@@ -78,7 +81,7 @@ def testCoreToken_Setters(mockGUI):
assert tokens._doKeywords is False assert tokens._doKeywords is False
# Set new values # Set new values
tokens.setTitleFormat(f"T: {nwHeadFmt.TITLE}", True) tokens.setPartitionFormat(f"T: {nwHeadFmt.TITLE}", True)
tokens.setChapterFormat(f"C: {nwHeadFmt.TITLE}", True) tokens.setChapterFormat(f"C: {nwHeadFmt.TITLE}", True)
tokens.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}", True) tokens.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}", True)
tokens.setSceneFormat(f"S: {nwHeadFmt.TITLE}", True) tokens.setSceneFormat(f"S: {nwHeadFmt.TITLE}", True)
@@ -103,7 +106,7 @@ def testCoreToken_Setters(mockGUI):
tokens.setKeywords(True) tokens.setKeywords(True)
# Check new values # Check new values
assert tokens._fmtTitle == f"T: {nwHeadFmt.TITLE}" assert tokens._fmtPart == f"T: {nwHeadFmt.TITLE}"
assert tokens._fmtChapter == f"C: {nwHeadFmt.TITLE}" assert tokens._fmtChapter == f"C: {nwHeadFmt.TITLE}"
assert tokens._fmtUnNum == f"U: {nwHeadFmt.TITLE}" assert tokens._fmtUnNum == f"U: {nwHeadFmt.TITLE}"
assert tokens._fmtScene == f"S: {nwHeadFmt.TITLE}" assert tokens._fmtScene == f"S: {nwHeadFmt.TITLE}"
@@ -121,7 +124,7 @@ def testCoreToken_Setters(mockGUI):
assert tokens._marginText == (2.0, 2.0) assert tokens._marginText == (2.0, 2.0)
assert tokens._marginMeta == (2.0, 2.0) assert tokens._marginMeta == (2.0, 2.0)
assert tokens._marginSep == (2.0, 2.0) assert tokens._marginSep == (2.0, 2.0)
assert tokens._hideTitle is True assert tokens._hidePart is True
assert tokens._hideChapter is True assert tokens._hideChapter is True
assert tokens._hideUnNum is True assert tokens._hideUnNum is True
assert tokens._hideScene is True assert tokens._hideScene is True
@@ -152,7 +155,7 @@ def testCoreToken_Setters(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath): def testFmtToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
"""Test handling files and text in the Tokenizer class.""" """Test handling files and text in the Tokenizer class."""
project = NWProject() project = NWProject()
mockRnd.reset() mockRnd.reset()
@@ -219,12 +222,12 @@ def testCoreToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
# Save File # Save File
savePath = fncPath / "dump.nwd" savePath = fncPath / "dump.nwd"
tokens.saveRawMarkdown(savePath) tokens.saveRawDocument(savePath, asJson=False)
assert readFile(savePath) == ( assert readFile(savePath) == (
"#! Notes: Plot\n\n" "#! Notes: Plot\n\n"
"#! Notes: Plot\n\n" "#! Notes: Plot\n\n"
) )
tokens.saveRawMarkdownJSON(savePath) tokens.saveRawDocument(savePath, asJson=True)
assert json.loads(readFile(savePath))["text"] == { assert json.loads(readFile(savePath))["text"] == {
"nwd": [ "nwd": [
["#! Notes: Plot"], ["#! Notes: Plot"],
@@ -232,13 +235,16 @@ def testCoreToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
] ]
} }
# Check abstract method # Check abstract methods
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
tokens.doConvert() tokens.doConvert()
with pytest.raises(NotImplementedError):
tokens.saveDocument(fncPath)
@pytest.mark.core @pytest.mark.core
def testCoreToken_StripEscape(): def testFmtToken_StripEscape():
"""Test the stripEscape helper function.""" """Test the stripEscape helper function."""
text1 = "This is text with escapes: \\** \\~~ \\__" text1 = "This is text with escapes: \\** \\~~ \\__"
text2 = "This is text with escapes: ** ~~ __" text2 = "This is text with escapes: ** ~~ __"
@@ -247,7 +253,7 @@ def testCoreToken_StripEscape():
@pytest.mark.core @pytest.mark.core
def testCoreToken_HeaderFormat(mockGUI): def testFmtToken_HeaderFormat(mockGUI):
"""Test the tokenization of header formats in the Tokenizer class.""" """Test the tokenization of header formats in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -422,7 +428,7 @@ def testCoreToken_HeaderFormat(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_HeaderStyle(mockGUI): def testFmtToken_HeaderStyle(mockGUI):
"""Test the styling of headers in the Tokenizer class.""" """Test the styling of headers in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -436,11 +442,11 @@ def testCoreToken_HeaderStyle(mockGUI):
# No Styles # No Styles
# ========= # =========
tokens.setTitleStyle(False, False) tokens.setPartitionStyle(False, False)
tokens.setChapterStyle(False, False) tokens.setChapterStyle(False, False)
tokens.setSceneStyle(False, False) tokens.setSceneStyle(False, False)
assert tokens._titleStyle == Tokenizer.A_NONE assert tokens._partStyle == Tokenizer.A_NONE
assert tokens._chapterStyle == Tokenizer.A_NONE assert tokens._chapterStyle == Tokenizer.A_NONE
assert tokens._sceneStyle == Tokenizer.A_NONE assert tokens._sceneStyle == Tokenizer.A_NONE
@@ -485,11 +491,11 @@ def testCoreToken_HeaderStyle(mockGUI):
# Center Headers # Center Headers
# ============== # ==============
tokens.setTitleStyle(True, False) tokens.setPartitionStyle(True, False)
tokens.setChapterStyle(True, False) tokens.setChapterStyle(True, False)
tokens.setSceneStyle(True, False) tokens.setSceneStyle(True, False)
assert tokens._titleStyle == Tokenizer.A_CENTRE assert tokens._partStyle == Tokenizer.A_CENTRE
assert tokens._chapterStyle == Tokenizer.A_CENTRE assert tokens._chapterStyle == Tokenizer.A_CENTRE
assert tokens._sceneStyle == Tokenizer.A_CENTRE assert tokens._sceneStyle == Tokenizer.A_CENTRE
@@ -534,11 +540,11 @@ def testCoreToken_HeaderStyle(mockGUI):
# Page Break Headers # Page Break Headers
# ================== # ==================
tokens.setTitleStyle(False, True) tokens.setPartitionStyle(False, True)
tokens.setChapterStyle(False, True) tokens.setChapterStyle(False, True)
tokens.setSceneStyle(False, True) tokens.setSceneStyle(False, True)
assert tokens._titleStyle == Tokenizer.A_PBB assert tokens._partStyle == Tokenizer.A_PBB
assert tokens._chapterStyle == Tokenizer.A_PBB assert tokens._chapterStyle == Tokenizer.A_PBB
assert tokens._sceneStyle == Tokenizer.A_PBB assert tokens._sceneStyle == Tokenizer.A_PBB
@@ -583,11 +589,11 @@ def testCoreToken_HeaderStyle(mockGUI):
# Page Break and Centre Headers # Page Break and Centre Headers
# ============================= # =============================
tokens.setTitleStyle(True, True) tokens.setPartitionStyle(True, True)
tokens.setChapterStyle(True, True) tokens.setChapterStyle(True, True)
tokens.setSceneStyle(True, True) tokens.setSceneStyle(True, True)
assert tokens._titleStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB assert tokens._partStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB
assert tokens._chapterStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB assert tokens._chapterStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB
assert tokens._sceneStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB assert tokens._sceneStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB
@@ -634,11 +640,11 @@ def testCoreToken_HeaderStyle(mockGUI):
tokens._isNovel = True tokens._isNovel = True
# Title Styles # Title Styles
tokens.setTitleStyle(True, True) tokens.setPartitionStyle(True, True)
tokens.setChapterStyle(False, False) tokens.setChapterStyle(False, False)
tokens.setSceneStyle(False, False) tokens.setSceneStyle(False, False)
assert tokens._titleStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB assert tokens._partStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB
assert tokens._chapterStyle == Tokenizer.A_NONE assert tokens._chapterStyle == Tokenizer.A_NONE
assert tokens._sceneStyle == Tokenizer.A_NONE assert tokens._sceneStyle == Tokenizer.A_NONE
@@ -650,11 +656,11 @@ def testCoreToken_HeaderStyle(mockGUI):
assert processStyle("##! Prologue\n", False) == Tokenizer.A_NONE assert processStyle("##! Prologue\n", False) == Tokenizer.A_NONE
# Chapter Styles # Chapter Styles
tokens.setTitleStyle(False, False) tokens.setPartitionStyle(False, False)
tokens.setChapterStyle(True, True) tokens.setChapterStyle(True, True)
tokens.setSceneStyle(False, False) tokens.setSceneStyle(False, False)
assert tokens._titleStyle == Tokenizer.A_NONE assert tokens._partStyle == Tokenizer.A_NONE
assert tokens._chapterStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB assert tokens._chapterStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB
assert tokens._sceneStyle == Tokenizer.A_NONE assert tokens._sceneStyle == Tokenizer.A_NONE
@@ -666,11 +672,11 @@ def testCoreToken_HeaderStyle(mockGUI):
assert processStyle("##! Prologue\n", False) == Tokenizer.A_CENTRE | Tokenizer.A_PBB assert processStyle("##! Prologue\n", False) == Tokenizer.A_CENTRE | Tokenizer.A_PBB
# Scene Styles # Scene Styles
tokens.setTitleStyle(False, False) tokens.setPartitionStyle(False, False)
tokens.setChapterStyle(False, False) tokens.setChapterStyle(False, False)
tokens.setSceneStyle(True, True) tokens.setSceneStyle(True, True)
assert tokens._titleStyle == Tokenizer.A_NONE assert tokens._partStyle == Tokenizer.A_NONE
assert tokens._chapterStyle == Tokenizer.A_NONE assert tokens._chapterStyle == Tokenizer.A_NONE
assert tokens._sceneStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB assert tokens._sceneStyle == Tokenizer.A_CENTRE | Tokenizer.A_PBB
@@ -683,7 +689,7 @@ def testCoreToken_HeaderStyle(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_MetaFormat(mockGUI): def testFmtToken_MetaFormat(mockGUI):
"""Test the tokenization of meta formats in the Tokenizer class.""" """Test the tokenization of meta formats in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -771,7 +777,7 @@ def testCoreToken_MetaFormat(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_MarginFormat(mockGUI): def testFmtToken_MarginFormat(mockGUI):
"""Test the tokenization of margin formats in the Tokenizer class.""" """Test the tokenization of margin formats in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -814,7 +820,7 @@ def testCoreToken_MarginFormat(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_ExtractFormats(mockGUI): def testFmtToken_ExtractFormats(mockGUI):
"""Test the extraction of formats in the Tokenizer class.""" """Test the extraction of formats in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -921,7 +927,7 @@ def testCoreToken_ExtractFormats(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_Paragraphs(mockGUI): def testFmtToken_Paragraphs(mockGUI):
"""Test the splitting of paragraphs.""" """Test the splitting of paragraphs."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -994,7 +1000,7 @@ def testCoreToken_Paragraphs(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_TextFormat(mockGUI): def testFmtToken_TextFormat(mockGUI):
"""Test the tokenization of text formats in the Tokenizer class.""" """Test the tokenization of text formats in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -1085,7 +1091,7 @@ def testCoreToken_TextFormat(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_Dialogue(mockGUI): def testFmtToken_Dialogue(mockGUI):
"""Test the tokenization of dialogue in the Tokenizer class.""" """Test the tokenization of dialogue in the Tokenizer class."""
CONFIG.fmtDQuoteOpen = "\u201c" CONFIG.fmtDQuoteOpen = "\u201c"
CONFIG.fmtDQuoteClose = "\u201d" CONFIG.fmtDQuoteClose = "\u201d"
@@ -1182,7 +1188,7 @@ def testCoreToken_Dialogue(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_SpecialFormat(mockGUI): def testFmtToken_SpecialFormat(mockGUI):
"""Test the tokenization of special formats in the Tokenizer class.""" """Test the tokenization of special formats in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -1344,7 +1350,7 @@ def testCoreToken_SpecialFormat(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_TextIndent(mockGUI): def testFmtToken_TextIndent(mockGUI):
"""Test the handling of text indent in the Tokenizer class.""" """Test the handling of text indent in the Tokenizer class."""
project = NWProject() project = NWProject()
tokens = BareTokenizer(project) tokens = BareTokenizer(project)
@@ -1427,7 +1433,7 @@ def testCoreToken_TextIndent(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_ProcessHeaders(mockGUI): def testFmtToken_ProcessHeaders(mockGUI):
"""Test the header and page parser of the Tokenizer class.""" """Test the header and page parser of the Tokenizer class."""
project = NWProject() project = NWProject()
project.data.setLanguage("en") project.data.setLanguage("en")
@@ -1441,7 +1447,7 @@ def testCoreToken_ProcessHeaders(mockGUI):
# H1: Title, First Page # H1: Title, First Page
assert tokens._isFirst is True assert tokens._isFirst is True
tokens._text = "# Part One\n" tokens._text = "# Part One\n"
tokens.setTitleFormat(f"T: {nwHeadFmt.TITLE}") tokens.setPartitionFormat(f"T: {nwHeadFmt.TITLE}")
tokens.tokenizeText() tokens.tokenizeText()
assert tokens._tokens == [ assert tokens._tokens == [
(Tokenizer.T_HEAD1, 1, "T: Part One", [], Tokenizer.A_CENTRE), (Tokenizer.T_HEAD1, 1, "T: Part One", [], Tokenizer.A_CENTRE),
@@ -1450,7 +1456,7 @@ def testCoreToken_ProcessHeaders(mockGUI):
# H1: Title, Not First Page # H1: Title, Not First Page
assert tokens._isFirst is False assert tokens._isFirst is False
tokens._text = "# Part One\n" tokens._text = "# Part One\n"
tokens.setTitleFormat(f"T: {nwHeadFmt.TITLE}") tokens.setPartitionFormat(f"T: {nwHeadFmt.TITLE}")
tokens.tokenizeText() tokens.tokenizeText()
assert tokens._tokens == [ assert tokens._tokens == [
(Tokenizer.T_HEAD1, 1, "T: Part One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_HEAD1, 1, "T: Part One", [], Tokenizer.A_PBB | Tokenizer.A_CENTRE),
@@ -1616,7 +1622,7 @@ def testCoreToken_ProcessHeaders(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_BuildOutline(mockGUI, ipsumText): def testFmtToken_BuildOutline(mockGUI, ipsumText):
"""Test stats counter of the Tokenizer class.""" """Test stats counter of the Tokenizer class."""
project = NWProject() project = NWProject()
project.data.setLanguage("en") project.data.setLanguage("en")
@@ -1680,7 +1686,7 @@ def testCoreToken_BuildOutline(mockGUI, ipsumText):
@pytest.mark.core @pytest.mark.core
def testCoreToken_CountStats(mockGUI, ipsumText): def testFmtToken_CountStats(mockGUI, ipsumText):
"""Test stats counter of the Tokenizer class.""" """Test stats counter of the Tokenizer class."""
project = NWProject() project = NWProject()
project.data.setLanguage("en") project.data.setLanguage("en")
@@ -1858,7 +1864,7 @@ def testCoreToken_CountStats(mockGUI, ipsumText):
) )
tokens._counts = {} tokens._counts = {}
tokens.setTitleFormat(f"T: {nwHeadFmt.TITLE}") tokens.setPartitionFormat(f"T: {nwHeadFmt.TITLE}")
tokens.setChapterFormat(f"C {nwHeadFmt.CH_NUM}: {nwHeadFmt.TITLE}") tokens.setChapterFormat(f"C {nwHeadFmt.CH_NUM}: {nwHeadFmt.TITLE}")
tokens.setSceneFormat("* * *", False) tokens.setSceneFormat("* * *", False)
tokens.setSynopsis(True) tokens.setSynopsis(True)
@@ -1876,7 +1882,7 @@ def testCoreToken_CountStats(mockGUI, ipsumText):
@pytest.mark.core @pytest.mark.core
def testCoreToken_SceneSeparators(mockGUI): def testFmtToken_SceneSeparators(mockGUI):
"""Test the section and scene separators of the Tokenizer class.""" """Test the section and scene separators of the Tokenizer class."""
project = NWProject() project = NWProject()
project.data.setLanguage("en") project.data.setLanguage("en")
@@ -1899,7 +1905,7 @@ def testCoreToken_SceneSeparators(mockGUI):
"###! Scene Four\n\n" "###! Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
md.setTitleFormat(f"T: {nwHeadFmt.TITLE}") md.setPartitionFormat(f"T: {nwHeadFmt.TITLE}")
md.setChapterFormat(f"C: {nwHeadFmt.TITLE}") md.setChapterFormat(f"C: {nwHeadFmt.TITLE}")
md.setSectionFormat("", True) md.setSectionFormat("", True)
@@ -1953,7 +1959,7 @@ def testCoreToken_SceneSeparators(mockGUI):
"###! Scene Four\n\n" "###! Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
md.setTitleFormat(f"T: {nwHeadFmt.TITLE}") md.setPartitionFormat(f"T: {nwHeadFmt.TITLE}")
md.setChapterFormat(f"C: {nwHeadFmt.TITLE}") md.setChapterFormat(f"C: {nwHeadFmt.TITLE}")
md.setSectionFormat("", True) md.setSectionFormat("", True)
@@ -2027,7 +2033,7 @@ def testCoreToken_SceneSeparators(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_HeaderVisibility(mockGUI): def testFmtToken_HeaderVisibility(mockGUI):
"""Test the heading visibility settings of the Tokenizer class.""" """Test the heading visibility settings of the Tokenizer class."""
project = NWProject() project = NWProject()
project.data.setLanguage("en") project.data.setLanguage("en")
@@ -2058,7 +2064,7 @@ def testCoreToken_HeaderVisibility(mockGUI):
md._isNovel = True md._isNovel = True
# Show All # Show All
md.setTitleFormat(nwHeadFmt.TITLE, False) md.setPartitionFormat(nwHeadFmt.TITLE, False)
md.setChapterFormat(nwHeadFmt.TITLE, False) md.setChapterFormat(nwHeadFmt.TITLE, False)
md.setUnNumberedFormat(nwHeadFmt.TITLE, False) md.setUnNumberedFormat(nwHeadFmt.TITLE, False)
md.setSceneFormat(nwHeadFmt.TITLE, False) md.setSceneFormat(nwHeadFmt.TITLE, False)
@@ -2086,7 +2092,7 @@ def testCoreToken_HeaderVisibility(mockGUI):
) )
# Hide All # Hide All
md.setTitleFormat(nwHeadFmt.TITLE, True) md.setPartitionFormat(nwHeadFmt.TITLE, True)
md.setChapterFormat(nwHeadFmt.TITLE, True) md.setChapterFormat(nwHeadFmt.TITLE, True)
md.setUnNumberedFormat(nwHeadFmt.TITLE, True) md.setUnNumberedFormat(nwHeadFmt.TITLE, True)
md.setSceneFormat(nwHeadFmt.TITLE, True) md.setSceneFormat(nwHeadFmt.TITLE, True)
@@ -2110,7 +2116,7 @@ def testCoreToken_HeaderVisibility(mockGUI):
md._isNovel = False md._isNovel = False
# Hide All # Hide All
md.setTitleFormat(nwHeadFmt.TITLE, True) md.setPartitionFormat(nwHeadFmt.TITLE, True)
md.setChapterFormat(nwHeadFmt.TITLE, True) md.setChapterFormat(nwHeadFmt.TITLE, True)
md.setUnNumberedFormat(nwHeadFmt.TITLE, True) md.setUnNumberedFormat(nwHeadFmt.TITLE, True)
md.setSceneFormat(nwHeadFmt.TITLE, True) md.setSceneFormat(nwHeadFmt.TITLE, True)
@@ -2139,7 +2145,7 @@ def testCoreToken_HeaderVisibility(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_CounterHandling(mockGUI): def testFmtToken_CounterHandling(mockGUI):
"""Test the heading counter of the Tokenizer class.""" """Test the heading counter of the Tokenizer class."""
project = NWProject() project = NWProject()
project.data.setLanguage("en") project.data.setLanguage("en")
@@ -2179,7 +2185,7 @@ def testCoreToken_CounterHandling(mockGUI):
"###! Scene Four\n\n" "###! Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
md.setTitleFormat(f"T: {nwHeadFmt.TITLE}") md.setPartitionFormat(f"T: {nwHeadFmt.TITLE}")
md.setChapterFormat(f"C {nwHeadFmt.CH_NUM}: {nwHeadFmt.TITLE}") md.setChapterFormat(f"C {nwHeadFmt.CH_NUM}: {nwHeadFmt.TITLE}")
md.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}") md.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}")
md.setSceneFormat( md.setSceneFormat(
@@ -2224,7 +2230,7 @@ def testCoreToken_CounterHandling(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToken_HeadingFormatter(fncPath, mockGUI, mockRnd): def testFmtToken_HeadingFormatter(fncPath, mockGUI, mockRnd):
"""Check the HeadingFormatter class.""" """Check the HeadingFormatter class."""
project = NWProject() project = NWProject()
project.setProjectLang("en_GB") project.setProjectLang("en_GB")
@@ -23,11 +23,11 @@ from __future__ import annotations
import pytest import pytest
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tomarkdown import ToMarkdown from novelwriter.formats.tomarkdown import ToMarkdown
@pytest.mark.core @pytest.mark.core
def testCoreToMarkdown_ConvertHeaders(mockGUI): def testFmtToMarkdown_ConvertHeaders(mockGUI):
"""Test header formats in the ToMarkdown class.""" """Test header formats in the ToMarkdown class."""
project = NWProject() project = NWProject()
toMD = ToMarkdown(project) toMD = ToMarkdown(project)
@@ -73,7 +73,7 @@ def testCoreToMarkdown_ConvertHeaders(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToMarkdown_ConvertParagraphs(mockGUI): def testFmtToMarkdown_ConvertParagraphs(mockGUI):
"""Test paragraph formats in the ToMarkdown class.""" """Test paragraph formats in the ToMarkdown class."""
project = NWProject() project = NWProject()
toMD = ToMarkdown(project) toMD = ToMarkdown(project)
@@ -209,7 +209,7 @@ def testCoreToMarkdown_ConvertParagraphs(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToMarkdown_ConvertDirect(mockGUI): def testFmtToMarkdown_ConvertDirect(mockGUI):
"""Test the converter directly using the ToMarkdown class.""" """Test the converter directly using the ToMarkdown class."""
project = NWProject() project = NWProject()
toMD = ToMarkdown(project) toMD = ToMarkdown(project)
@@ -246,7 +246,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToMarkdown_Save(mockGUI, fncPath): def testFmtToMarkdown_Save(mockGUI, fncPath):
"""Test the save method of the ToMarkdown class.""" """Test the save method of the ToMarkdown class."""
project = NWProject() project = NWProject()
toMD = ToMarkdown(project) toMD = ToMarkdown(project)
@@ -293,12 +293,12 @@ def testCoreToMarkdown_Save(mockGUI, fncPath):
# ========== # ==========
saveFile = fncPath / "outFile.md" saveFile = fncPath / "outFile.md"
toMD.saveMarkdown(saveFile) toMD.saveDocument(saveFile)
assert saveFile.read_text(encoding="utf-8") == "".join(resText) assert saveFile.read_text(encoding="utf-8") == "".join(resText)
@pytest.mark.core @pytest.mark.core
def testCoreToMarkdown_Format(mockGUI): def testFmtToMarkdown_Format(mockGUI):
"""Test all the formatters for the ToMarkdown class.""" """Test all the formatters for the ToMarkdown class."""
project = NWProject() project = NWProject()
toMD = ToMarkdown(project) toMD = ToMarkdown(project)
@@ -30,7 +30,7 @@ import pytest
from novelwriter.common import xmlIndent from novelwriter.common import xmlIndent
from novelwriter.constants import nwHeadFmt from novelwriter.constants import nwHeadFmt
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.toodt import ODTParagraphStyle, ODTTextStyle, ToOdt, XMLParagraph, _mkTag from novelwriter.formats.toodt import ODTParagraphStyle, ODTTextStyle, ToOdt, XMLParagraph, _mkTag
from tests.tools import ODT_IGNORE, cmpFiles from tests.tools import ODT_IGNORE, cmpFiles
@@ -54,7 +54,7 @@ def xmlToText(xElem):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_Init(mockGUI): def testFmtToOdt_Init(mockGUI):
"""Test initialisation of the ODT document.""" """Test initialisation of the ODT document."""
project = NWProject() project = NWProject()
@@ -106,7 +106,7 @@ def testCoreToOdt_Init(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_TextFormatting(mockGUI): def testFmtToOdt_TextFormatting(mockGUI):
"""Test formatting of paragraphs.""" """Test formatting of paragraphs."""
project = NWProject() project = NWProject()
odt = ToOdt(project, isFlat=True) odt = ToOdt(project, isFlat=True)
@@ -235,7 +235,7 @@ def testCoreToOdt_TextFormatting(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_DialogueFormatting(mockGUI): def testFmtToOdt_DialogueFormatting(mockGUI):
"""Test formatting of dialogue.""" """Test formatting of dialogue."""
project = NWProject() project = NWProject()
odt = ToOdt(project, isFlat=True) odt = ToOdt(project, isFlat=True)
@@ -271,7 +271,7 @@ def testCoreToOdt_DialogueFormatting(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_ConvertHeaders(mockGUI): def testFmtToOdt_ConvertHeaders(mockGUI):
"""Test the converter of the ToOdt class.""" """Test the converter of the ToOdt class."""
project = NWProject() project = NWProject()
odt = ToOdt(project, isFlat=True) odt = ToOdt(project, isFlat=True)
@@ -358,7 +358,7 @@ def testCoreToOdt_ConvertHeaders(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_ConvertParagraphs(mockGUI): def testFmtToOdt_ConvertParagraphs(mockGUI):
"""Test the converter of the ToOdt class.""" """Test the converter of the ToOdt class."""
project = NWProject() project = NWProject()
odt = ToOdt(project, isFlat=True) odt = ToOdt(project, isFlat=True)
@@ -706,7 +706,7 @@ def testCoreToOdt_ConvertParagraphs(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_ConvertDirect(mockGUI): def testFmtToOdt_ConvertDirect(mockGUI):
"""Test the converter directly using the ToOdt class to reach some """Test the converter directly using the ToOdt class to reach some
otherwise hard to reach conditions. otherwise hard to reach conditions.
""" """
@@ -757,7 +757,7 @@ def testCoreToOdt_ConvertDirect(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths): def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
"""Test the document save functions.""" """Test the document save functions."""
project = NWProject() project = NWProject()
project.data.setAuthor("Jane Smith") project.data.setAuthor("Jane Smith")
@@ -772,8 +772,6 @@ def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
assert odt._dLanguage == "" assert odt._dLanguage == ""
odt.setLanguage("nb_NO") odt.setLanguage("nb_NO")
assert odt._dLanguage == "nb" assert odt._dLanguage == "nb"
odt.setColourHeaders(True)
assert odt._colourHead is True
odt.setHeaderFormat(nwHeadFmt.ODT_AUTO, 1) odt.setHeaderFormat(nwHeadFmt.ODT_AUTO, 1)
assert odt._headerFormat == nwHeadFmt.ODT_AUTO assert odt._headerFormat == nwHeadFmt.ODT_AUTO
odt.setFirstLineIndent(True, 1.4, False) odt.setFirstLineIndent(True, 1.4, False)
@@ -804,7 +802,7 @@ def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
testFile = tstPaths.outDir / "coreToOdt_SaveFlat_document.fodt" testFile = tstPaths.outDir / "coreToOdt_SaveFlat_document.fodt"
compFile = tstPaths.refDir / "coreToOdt_SaveFlat_document.fodt" compFile = tstPaths.refDir / "coreToOdt_SaveFlat_document.fodt"
odt.saveFlatXML(flatFile) odt.saveDocument(flatFile)
assert flatFile.exists() assert flatFile.exists()
copyfile(flatFile, testFile) copyfile(flatFile, testFile)
@@ -812,7 +810,7 @@ def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths): def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths):
"""Test the document save functions.""" """Test the document save functions."""
project = NWProject() project = NWProject()
project.data.setAuthor("Jane Smith") project.data.setAuthor("Jane Smith")
@@ -839,7 +837,7 @@ def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths):
fullFile = fncPath / "document.odt" fullFile = fncPath / "document.odt"
odt.saveOpenDocText(fullFile) odt.saveDocument(fullFile)
assert fullFile.exists() assert fullFile.exists()
assert zipfile.is_zipfile(fullFile) assert zipfile.is_zipfile(fullFile)
@@ -890,7 +888,7 @@ def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_SpecialFormats(mockGUI): def testFmtToOdt_SpecialFormats(mockGUI):
"""Test the special formatters for the ToOdt class.""" """Test the special formatters for the ToOdt class."""
project = NWProject() project = NWProject()
odt = ToOdt(project, isFlat=True) odt = ToOdt(project, isFlat=True)
@@ -921,7 +919,7 @@ def testCoreToOdt_SpecialFormats(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_ODTParagraphStyle(): def testFmtToOdt_ODTParagraphStyle():
"""Test the ODTParagraphStyle class.""" """Test the ODTParagraphStyle class."""
parStyle = ODTParagraphStyle("test") parStyle = ODTParagraphStyle("test")
@@ -1158,7 +1156,7 @@ def testCoreToOdt_ODTParagraphStyle():
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_ODTTextStyle(): def testFmtToOdt_ODTTextStyle():
"""Test the ODTTextStyle class.""" """Test the ODTTextStyle class."""
txtStyle = ODTTextStyle("test") txtStyle = ODTTextStyle("test")
@@ -1308,7 +1306,7 @@ def testCoreToOdt_ODTTextStyle():
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_XMLParagraph(): def testFmtToOdt_XMLParagraph():
"""Test XML encoding of paragraph.""" """Test XML encoding of paragraph."""
# Stage 1 : Text # Stage 1 : Text
# ============== # ==============
@@ -1497,7 +1495,7 @@ def testCoreToOdt_XMLParagraph():
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_MkTag(): def testFmtToOdt_MkTag():
"""Test the tag maker function.""" """Test the tag maker function."""
assert _mkTag("office", "text") == "{urn:oasis:names:tc:opendocument:xmlns:office:1.0}text" assert _mkTag("office", "text") == "{urn:oasis:names:tc:opendocument:xmlns:office:1.0}text"
assert _mkTag("style", "text") == "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}text" assert _mkTag("style", "text") == "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}text"
@@ -27,7 +27,7 @@ from PyQt5.QtGui import QColor, QTextBlock, QTextCharFormat, QTextCursor
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument
from novelwriter.types import ( from novelwriter.types import (
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight, QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
QtPageBreakAfter, QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper QtPageBreakAfter, QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper
@@ -56,11 +56,12 @@ def charFmtInBlock(block: QTextBlock, pos: int) -> QTextCharFormat:
@pytest.mark.core @pytest.mark.core
def testCoreToQTextDocument_ConvertHeaders(mockGUI): def testFmtToQTextDocument_ConvertHeaders(mockGUI):
"""Test header formats in the ToQTextDocument class.""" """Test header formats in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument(CONFIG.textFont, THEME)
qdoc.saveDocument("") # Doesn't do anything for this format
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True
@@ -132,7 +133,7 @@ def testCoreToQTextDocument_ConvertHeaders(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToQTextDocument_SeparatorSkip(mockGUI): def testFmtToQTextDocument_SeparatorSkip(mockGUI):
"""Test separator and skip in the ToQTextDocument class.""" """Test separator and skip in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
@@ -198,7 +199,7 @@ def testCoreToQTextDocument_SeparatorSkip(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToQTextDocument_NovelMeta(mockGUI): def testFmtToQTextDocument_NovelMeta(mockGUI):
"""Test novel meta formats in the ToQTextDocument class.""" """Test novel meta formats in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
@@ -275,7 +276,7 @@ def testCoreToQTextDocument_NovelMeta(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToQTextDocument_NoteMeta(mockGUI): def testFmtToQTextDocument_NoteMeta(mockGUI):
"""Test note meta formats in the ToQTextDocument class.""" """Test note meta formats in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
@@ -332,7 +333,7 @@ def testCoreToQTextDocument_NoteMeta(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToQTextDocument_TextBlockFormats(mockGUI): def testFmtToQTextDocument_TextBlockFormats(mockGUI):
"""Test text block formats in the ToQTextDocument class.""" """Test text block formats in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
@@ -441,7 +442,7 @@ def testCoreToQTextDocument_TextBlockFormats(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToQTextDocument_TextCharFormats(mockGUI): def testFmtToQTextDocument_TextCharFormats(mockGUI):
"""Test text char formats in the ToQTextDocument class.""" """Test text char formats in the ToQTextDocument class."""
CONFIG.fmtDQuoteOpen = nwUnicode.U_LDQUO CONFIG.fmtDQuoteOpen = nwUnicode.U_LDQUO
CONFIG.fmtDQuoteClose = nwUnicode.U_RDQUO CONFIG.fmtDQuoteClose = nwUnicode.U_RDQUO
@@ -576,7 +577,7 @@ def testCoreToQTextDocument_TextCharFormats(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToQTextDocument_Footnotes(mockGUI): def testFmtToQTextDocument_Footnotes(mockGUI):
"""Test footnotes in the ToQTextDocument class.""" """Test footnotes in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
+1 -1
View File
@@ -27,8 +27,8 @@ from PyQt5.QtGui import QMouseEvent, QTextCursor
from PyQt5.QtWidgets import QAction, QApplication, QMenu from PyQt5.QtWidgets import QAction, QApplication, QMenu
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.core.toqdoc import ToQTextDocument
from novelwriter.enum import nwDocAction from novelwriter.enum import nwDocAction
from novelwriter.formats.toqdoc import ToQTextDocument
from novelwriter.gui.docviewer import GuiDocViewer from novelwriter.gui.docviewer import GuiDocViewer
from novelwriter.types import QtModNone, QtMouseLeft from novelwriter.types import QtModNone, QtMouseLeft
+22 -14
View File
@@ -311,14 +311,14 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
"""Test the Headings Tab of the GuiBuildSettings dialog.""" """Test the Headings Tab of the GuiBuildSettings dialog."""
build = BuildSettings() build = BuildSettings()
ttTitle = f"Title: {nwHeadFmt.TITLE}" ttTitle = f"Part: {nwHeadFmt.TITLE}"
chTitle = f"Chapter: {nwHeadFmt.TITLE}" chTitle = f"Chapter: {nwHeadFmt.TITLE}"
unTitle = f"Interlude: {nwHeadFmt.TITLE}" unTitle = f"Interlude: {nwHeadFmt.TITLE}"
scTitle = f"Scene: {nwHeadFmt.TITLE}" scTitle = f"Scene: {nwHeadFmt.TITLE}"
shTitle = f"Hard Scene: {nwHeadFmt.TITLE}" shTitle = f"Hard Scene: {nwHeadFmt.TITLE}"
sxTitle = f"Section: {nwHeadFmt.TITLE}" sxTitle = f"Section: {nwHeadFmt.TITLE}"
build.setValue("headings.fmtTitle", ttTitle) build.setValue("headings.fmtPart", ttTitle)
build.setValue("headings.fmtChapter", chTitle) build.setValue("headings.fmtChapter", chTitle)
build.setValue("headings.fmtUnnumbered", unTitle) build.setValue("headings.fmtUnnumbered", unTitle)
build.setValue("headings.fmtScene", scTitle) build.setValue("headings.fmtScene", scTitle)
@@ -337,7 +337,7 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
assert bSettings.toolStack.currentWidget() is headTab assert bSettings.toolStack.currentWidget() is headTab
# Check initial values # Check initial values
assert headTab.fmtTitle.text() == ttTitle assert headTab.fmtPart.text() == ttTitle
assert headTab.fmtChapter.text() == chTitle assert headTab.fmtChapter.text() == chTitle
assert headTab.fmtUnnumbered.text() == unTitle assert headTab.fmtUnnumbered.text() == unTitle
assert headTab.fmtScene.text() == scTitle assert headTab.fmtScene.text() == scTitle
@@ -356,7 +356,7 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
assert headTab.editTextBox.isEnabled() is False assert headTab.editTextBox.isEnabled() is False
# Title # Title
headTab.btnTitle.click() headTab.btnPart.click()
assert headTab._editing == headTab.EDIT_TITLE assert headTab._editing == headTab.EDIT_TITLE
assert headTab.editTextBox.isEnabled() is True assert headTab.editTextBox.isEnabled() is True
assert headTab.editTextBox.toPlainText() == ttTitle assert headTab.editTextBox.toPlainText() == ttTitle
@@ -434,10 +434,10 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
) )
# Set all to plain title # Set all to plain title
headTab.btnTitle.click() headTab.btnPart.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtTitle") == nwHeadFmt.TITLE assert build.getStr("headings.fmtPart") == nwHeadFmt.TITLE
headTab.btnChapter.click() headTab.btnChapter.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
@@ -707,9 +707,11 @@ def testToolBuildSettings_FormatOutput(qtbot, nwGUI):
"""Test the format-specific settings.""" """Test the format-specific settings."""
build = BuildSettings() build = BuildSettings()
build.setValue("odt.addColours", False)
build.setValue("odt.pageHeader", nwHeadFmt.ODT_AUTO) build.setValue("odt.pageHeader", nwHeadFmt.ODT_AUTO)
build.setValue("odt.pageCountOffset", 0) build.setValue("odt.pageCountOffset", 0)
build.setValue("odt.colorHeadings", True)
build.setValue("odt.scaleHeadings", True)
build.setValue("odt.boldHeadings", True)
build.setValue("html.addStyles", False) build.setValue("html.addStyles", False)
build.setValue("html.preserveTabs", False) build.setValue("html.preserveTabs", False)
@@ -724,28 +726,34 @@ def testToolBuildSettings_FormatOutput(qtbot, nwGUI):
assert bSettings.toolStack.currentWidget() is fmtTab assert bSettings.toolStack.currentWidget() is fmtTab
# Check initial values # Check initial values
assert fmtTab.odtAddColours.isChecked() is False
assert fmtTab.odtPageHeader.text() == nwHeadFmt.ODT_AUTO assert fmtTab.odtPageHeader.text() == nwHeadFmt.ODT_AUTO
assert fmtTab.odtPageCountOffset.value() == 0 assert fmtTab.odtPageCountOffset.value() == 0
assert fmtTab.colorHeadings.isChecked() is True
assert fmtTab.scaleHeadings.isChecked() is True
assert fmtTab.boldHeadings.isChecked() is True
assert fmtTab.htmlAddStyles.isChecked() is False assert fmtTab.htmlAddStyles.isChecked() is False
assert fmtTab.htmlPreserveTabs.isChecked() is False assert fmtTab.htmlPreserveTabs.isChecked() is False
# Toggle all
fmtTab.odtAddColours.setChecked(True)
fmtTab.htmlAddStyles.setChecked(True)
fmtTab.htmlPreserveTabs.setChecked(True)
# Change Values # Change Values
fmtTab.odtPageCountOffset.setValue(1) fmtTab.odtPageCountOffset.setValue(1)
fmtTab.odtPageHeader.setText("Stuff") fmtTab.odtPageHeader.setText("Stuff")
# Toggle all
fmtTab.colorHeadings.setChecked(False)
fmtTab.scaleHeadings.setChecked(False)
fmtTab.boldHeadings.setChecked(False)
fmtTab.htmlAddStyles.setChecked(True)
fmtTab.htmlPreserveTabs.setChecked(True)
# Save values # Save values
fmtTab.saveContent() fmtTab.saveContent()
assert build.getBool("odt.addColours") is True
assert build.getStr("odt.pageHeader") == "Stuff" assert build.getStr("odt.pageHeader") == "Stuff"
assert build.getInt("odt.pageCountOffset") == 1 assert build.getInt("odt.pageCountOffset") == 1
assert build.getBool("odt.colorHeadings") is False
assert build.getBool("odt.scaleHeadings") is False
assert build.getBool("odt.boldHeadings") is False
assert build.getBool("html.addStyles") is True assert build.getBool("html.addStyles") is True
assert build.getBool("html.preserveTabs") is True assert build.getBool("html.preserveTabs") is True