Extend heading styling options (#2045)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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", "	").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", "	").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", "	").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", "	").rstrip(),
|
||||||
|
))
|
||||||
|
|
||||||
logger.info("Wrote file: %s", path)
|
logger.info("Wrote file: %s", path)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
|
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> 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}"])
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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”
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user