Skip to content

Commit

Permalink
Extend heading styling options (#2045)
Browse files Browse the repository at this point in the history
  • Loading branch information
vkbo authored Oct 15, 2024
2 parents 7405c7e + 29eac0b commit 93c105f
Show file tree
Hide file tree
Showing 25 changed files with 477 additions and 429 deletions.
47 changes: 25 additions & 22 deletions novelwriter/core/buildsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,29 +45,30 @@

# The Settings Template
# =====================
# Each entry contains a tuple on the form:
# (type, default, [min value, max value])
# Each entry contains a tuple on the form: (type, default)

SETTINGS_TEMPLATE = {
SETTINGS_TEMPLATE: dict[str, tuple[type, str | int | float | bool]] = {
"filter.includeNovel": (bool, True),
"filter.includeNotes": (bool, False),
"filter.includeInactive": (bool, False),
"headings.fmtTitle": (str, nwHeadFmt.TITLE),
"headings.fmtPart": (str, nwHeadFmt.TITLE),
"headings.fmtChapter": (str, nwHeadFmt.TITLE),
"headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
"headings.fmtScene": (str, "* * *"),
"headings.fmtAltScene": (str, ""),
"headings.fmtSection": (str, ""),
"headings.hideTitle": (bool, False),
"headings.hidePart": (bool, False),
"headings.hideChapter": (bool, False),
"headings.hideUnnumbered": (bool, False),
"headings.hideScene": (bool, False),
"headings.hideAltScene": (bool, False),
"headings.hideSection": (bool, True),
"headings.centerTitle": (bool, True),
"headings.centerPart": (bool, True),
"headings.centerChapter": (bool, False),
"headings.centerScene": (bool, False),
"headings.breakTitle": (bool, True),
"headings.breakPart": (bool, True),
"headings.breakChapter": (bool, True),
"headings.breakScene": (bool, False),
"text.includeSynopsis": (bool, False),
Expand All @@ -77,7 +78,7 @@
"text.ignoredKeywords": (str, ""),
"text.addNoteHeadings": (bool, True),
"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.stripUnicode": (bool, False),
"format.replaceTabs": (bool, False),
Expand All @@ -94,9 +95,11 @@
"format.bottomMargin": (float, 2.0),
"format.leftMargin": (float, 2.0),
"format.rightMargin": (float, 2.0),
"odt.addColours": (bool, True),
"odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
"odt.pageCountOffset": (int, 0),
"odt.colorHeadings": (bool, True),
"odt.scaleHeadings": (bool, True),
"odt.boldHeadings": (bool, True),
"html.addStyles": (bool, True),
"html.preserveTabs": (bool, False),
}
Expand All @@ -108,12 +111,16 @@
"filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"),

"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.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Format"),
"headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Format"),
"headings.fmtAltScene": QT_TRANSLATE_NOOP("Builds", "Alt. Scene 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.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"),
Expand Down Expand Up @@ -147,12 +154,14 @@
"format.leftMargin": QT_TRANSLATE_NOOP("Builds", "Left Margin"),
"format.rightMargin": QT_TRANSLATE_NOOP("Builds", "Right Margin"),

"odt": QT_TRANSLATE_NOOP("Builds", "ODT Documents"),
"odt.addColours": QT_TRANSLATE_NOOP("Builds", "Add Highlight Colours"),
"odt": QT_TRANSLATE_NOOP("Builds", "Document Options"),
"odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
"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.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"),
}
Expand Down Expand Up @@ -346,18 +355,12 @@ def setAllowRoot(self, tHandle: str, state: bool) -> None:
self._changed = True
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."""
if key not in SETTINGS_TEMPLATE:
return False
definition = SETTINGS_TEMPLATE[key]
if not isinstance(value, definition[0]):
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
if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
self._changed = value != self._settings[key]
self._settings[key] = value
return

##
# Methods
Expand Down
50 changes: 25 additions & 25 deletions novelwriter/core/docbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
from novelwriter.core.buildsettings import BuildSettings
from novelwriter.core.item import NWItem
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.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__)

Expand Down Expand Up @@ -178,10 +178,7 @@ def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int,
self._cache = makeObj

try:
if isFlat:
makeObj.saveFlatXML(path)
else:
makeObj.saveOpenDocText(path)
makeObj.saveDocument(path)
except Exception as exc:
logException()
self._error = formatException(exc)
Expand Down Expand Up @@ -213,10 +210,7 @@ def iterBuildHTML(self, path: Path | None, asJson: bool = False) -> Iterable[tup

if isinstance(path, Path):
try:
if asJson:
makeObj.saveHtmlJson(path)
else:
makeObj.saveHtml5(path)
makeObj.saveDocument(path, asJson=asJson)
except Exception as exc:
logException()
self._error = formatException(exc)
Expand Down Expand Up @@ -246,7 +240,7 @@ def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int,
self._cache = makeObj

try:
makeObj.saveMarkdown(path)
makeObj.saveDocument(path)
except Exception as exc:
logException()
self._error = formatException(exc)
Expand Down Expand Up @@ -276,10 +270,7 @@ def iterBuildNWD(self, path: Path | None, asJson: bool = False) -> Iterable[tupl

if isinstance(path, Path):
try:
if asJson:
makeObj.saveRawMarkdownJSON(path)
else:
makeObj.saveRawMarkdown(path)
makeObj.saveRawDocument(path, asJson=asJson)
except Exception as exc:
logException()
self._error = formatException(exc)
Expand All @@ -297,9 +288,9 @@ def _setupBuild(self, bldObj: Tokenizer) -> dict:
textFont.fromString(self._build.getStr("format.textFont"))
bldObj.setFont(textFont)

bldObj.setTitleFormat(
self._build.getStr("headings.fmtTitle"),
self._build.getBool("headings.hideTitle")
bldObj.setPartitionFormat(
self._build.getStr("headings.fmtPart"),
self._build.getBool("headings.hidePart")
)
bldObj.setChapterFormat(
self._build.getStr("headings.fmtChapter"),
Expand All @@ -322,8 +313,12 @@ def _setupBuild(self, bldObj: Tokenizer) -> dict:
self._build.getBool("headings.hideSection")
)
bldObj.setTitleStyle(
self._build.getBool("headings.centerTitle"),
self._build.getBool("headings.breakTitle")
self._build.getBool("headings.centerPart"),
self._build.getBool("headings.breakPart")
)
bldObj.setPartitionStyle(
self._build.getBool("headings.centerPart"),
self._build.getBool("headings.breakPart")
)
bldObj.setChapterStyle(
self._build.getBool("headings.centerChapter"),
Expand All @@ -343,6 +338,11 @@ def _setupBuild(self, bldObj: Tokenizer) -> dict:
self._build.getFloat("format.firstIndentWidth"),
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.setSynopsis(self._build.getBool("text.includeSynopsis"))
Expand All @@ -355,10 +355,10 @@ def _setupBuild(self, bldObj: Tokenizer) -> dict:
bldObj.setReplaceUnicode(self._build.getBool("format.stripUnicode"))

if isinstance(bldObj, ToOdt):
bldObj.setColourHeaders(self._build.getBool("odt.addColours"))
bldObj.setLanguage(self._project.data.language)
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)
Expand Down
86 changes: 43 additions & 43 deletions novelwriter/core/tohtml.py → novelwriter/formats/tohtml.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels
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

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -290,51 +290,51 @@ def appendFootnotes(self) -> None:

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."""
with open(path, mode="w", encoding="utf-8") as fObj:
fObj.write((
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<meta charset='utf-8'>\n"
"<title>{title:s}</title>\n"
"</head>\n"
"<style>\n"
"{style:s}\n"
"</style>\n"
"<body>\n"
"<article>\n"
"{body:s}\n"
"</article>\n"
"</body>\n"
"</html>\n"
).format(
title=self._project.data.name,
style="\n".join(self.getStyleSheet()),
body=("".join(self._fullHTML)).replace("\t", "&#09;").rstrip(),
))
logger.info("Wrote file: %s", path)
return

def saveHtmlJson(self, path: str | Path) -> None:
"""Save the data to a JSON file."""
timeStamp = time()
data = {
"meta": {
"projectName": self._project.data.name,
"novelAuthor": self._project.data.author,
"buildTime": int(timeStamp),
"buildTimeStr": formatTimeStamp(timeStamp),
},
"text": {
"css": self.getStyleSheet(),
"html": [t.replace("\t", "&#09;").rstrip().split("\n") for t in self.fullHTML],
if asJson:
ts = time()
data = {
"meta": {
"projectName": self._project.data.name,
"novelAuthor": self._project.data.author,
"buildTime": int(ts),
"buildTimeStr": formatTimeStamp(ts),
},
"text": {
"css": self.getStyleSheet(),
"html": [t.replace("\t", "&#09;").rstrip().split("\n") for t in self.fullHTML],
}
}
}
with open(path, mode="w", encoding="utf-8") as fObj:
json.dump(data, fObj, indent=2)
with open(path, mode="w", encoding="utf-8") as fObj:
json.dump(data, fObj, indent=2)

else:
with open(path, mode="w", encoding="utf-8") as fObj:
fObj.write((
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<meta charset='utf-8'>\n"
"<title>{title:s}</title>\n"
"</head>\n"
"<style>\n"
"{style:s}\n"
"</style>\n"
"<body>\n"
"<article>\n"
"{body:s}\n"
"</article>\n"
"</body>\n"
"</html>\n"
).format(
title=self._project.data.name,
style="\n".join(self.getStyleSheet()),
body=("".join(self._fullHTML)).replace("\t", "&#09;").rstrip(),
))

logger.info("Wrote file: %s", path)

return

def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None:
Expand Down
Loading

0 comments on commit 93c105f

Please sign in to comment.