Add customisation option for ODT build document header (#1675)

This commit is contained in:
Veronica Berglyd Olsen
2024-01-28 15:58:06 +01:00
committed by GitHub
10 changed files with 117 additions and 45 deletions
+7 -1
View File
@@ -309,7 +309,13 @@ class nwHeadFmt:
SC_NUM = "{Scene}"
SC_ABS = "{Scene:Abs}"
ALL = [TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS]
PAGE_HEADERS = [TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS]
# ODT Document Page Header
ODT_PROJECT = "{Project}"
ODT_AUTHOR = "{Author}"
ODT_PAGE = "{Page}"
ODT_AUTO = "{Project} / {Author} / {Page}"
# END Class nwHeadFmt
+4
View File
@@ -79,6 +79,8 @@ SETTINGS_TEMPLATE = {
"format.leftMargin": (float, 2.0),
"format.rightMargin": (float, 2.0),
"odt.addColours": (bool, True),
"odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
"odt.pageCountOffset": (int, 0),
"html.addStyles": (bool, True),
}
@@ -125,6 +127,8 @@ SETTINGS_LABELS = {
"odt": QT_TRANSLATE_NOOP("Builds", "Open Document (.odt)"),
"odt.addColours": QT_TRANSLATE_NOOP("Builds", "Add Highlight Colours"),
"odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
"html": QT_TRANSLATE_NOOP("Builds", "HTML (.html)"),
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
+3
View File
@@ -284,6 +284,9 @@ class NWBuildDocument:
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")
)
scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0)
pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0))
+1 -1
View File
@@ -34,11 +34,11 @@ from pathlib import Path
from functools import partial
from PyQt5.QtCore import QCoreApplication, QRegularExpression
from novelwriter.core.index import processComment
from novelwriter.enum import nwComment, nwItemLayout
from novelwriter.common import formatTimeStamp, numberToRoman, checkInt
from novelwriter.constants import nwHeadFmt, nwRegEx, nwShortcode, nwUnicode
from novelwriter.core.index import processComment
from novelwriter.core.project import NWProject
logger = logging.getLogger(__name__)
+33 -21
View File
@@ -132,11 +132,12 @@ class ToOdt(Tokenizer):
self._errData = [] # List of errors encountered
# Properties
self._textFont = "Liberation Serif"
self._textSize = 12
self._textFixed = False
self._colourHead = False
self._headerText = ""
self._textFont = "Liberation Serif"
self._textSize = 12
self._textFixed = False
self._colourHead = False
self._headerFormat = ""
self._pageOffset = 0
# Internal
self._fontFamily = "'Liberation Serif'"
@@ -222,6 +223,12 @@ class ToOdt(Tokenizer):
self._mDocRight = f"{right/10.0:.3f}cm"
return
def setHeaderFormat(self, format: str, offset: int) -> None:
"""Set the document header format."""
self._headerFormat = format.strip()
self._pageOffset = offset
return
##
# Class Methods
##
@@ -279,12 +286,6 @@ class ToOdt(Tokenizer):
# Clear Errors
self._errData = []
# Document Header
# ===============
if self._headerText == "":
self._headerText = f"{self._project.data.name} / {self._project.data.author} /"
# Create Roots
# ============
@@ -1010,17 +1011,28 @@ class ToOdt(Tokenizer):
xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib=tAttr)
# Standard Page Header
xHead = ET.SubElement(xPage, _mkTag("style", "header"))
xPar = ET.SubElement(xHead, _mkTag("text", "p"), attrib={
_mkTag("text", "style-name"): "Header"
})
xPar.text = self._headerText.strip() + " "
if self._headerFormat:
pre, page, post = self._headerFormat.partition(nwHeadFmt.ODT_PAGE)
xTail = ET.SubElement(xPar, _mkTag("text", "page-number"), attrib={
_mkTag("text", "select-page"): "current"
})
xTail.text = "2"
xTail.tail = "" # Prevent line break in indented XML
pre = pre.replace(nwHeadFmt.ODT_PROJECT, self._project.data.name)
pre = pre.replace(nwHeadFmt.ODT_AUTHOR, self._project.data.author)
post = post.replace(nwHeadFmt.ODT_PROJECT, self._project.data.name)
post = post.replace(nwHeadFmt.ODT_AUTHOR, self._project.data.author)
xHead = ET.SubElement(xPage, _mkTag("style", "header"))
xPar = ET.SubElement(xHead, _mkTag("text", "p"), attrib={
_mkTag("text", "style-name"): "Header"
})
xPar.text = pre
if page:
attrib = {_mkTag("text", "select-page"): "current"}
if self._pageOffset > 0:
attrib = {_mkTag("text", "page-adjust"): str(0 - self._pageOffset)}
xTail = ET.SubElement(xPar, _mkTag("text", "page-number"), attrib=attrib)
xTail.text = "2"
xTail.tail = post
else:
xPar.text += post
# First Page Header
xHead = ET.SubElement(xPage, _mkTag("style", "header-first"))
+48 -16
View File
@@ -79,17 +79,16 @@ class GuiBuildSettings(QDialog):
self._build = build
self.setWindowTitle(self.tr("Manuscript Build Settings"))
self.setMinimumWidth(CONFIG.pxInt(700))
self.setMinimumHeight(CONFIG.pxInt(400))
self.setMinimumSize(CONFIG.pxInt(700), CONFIG.pxInt(400))
mPx = CONFIG.pxInt(150)
wWin = CONFIG.pxInt(750)
hWin = CONFIG.pxInt(550)
pOptions = SHARED.project.options
options = SHARED.project.options
self.resize(
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winWidth", wWin)),
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "winHeight", hWin))
CONFIG.pxInt(options.getInt("GuiBuildSettings", "winWidth", wWin)),
CONFIG.pxInt(options.getInt("GuiBuildSettings", "winHeight", hWin))
)
# Title
@@ -604,9 +603,9 @@ class _HeadingsTab(NScrollablePage):
# Title Heading
self.lblTitle = QLabel(self._build.getLabel("headings.fmtTitle"))
self.fmtTitle = QLineEdit("")
self.fmtTitle = QLineEdit("", self)
self.fmtTitle.setReadOnly(True)
self.btnTitle = QToolButton()
self.btnTitle = QToolButton(self)
self.btnTitle.setIcon(SHARED.theme.getIcon("edit"))
self.btnTitle.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE))
@@ -620,9 +619,9 @@ class _HeadingsTab(NScrollablePage):
# Chapter Heading
self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter"))
self.fmtChapter = QLineEdit("")
self.fmtChapter = QLineEdit("", self)
self.fmtChapter.setReadOnly(True)
self.btnChapter = QToolButton()
self.btnChapter = QToolButton(self)
self.btnChapter.setIcon(SHARED.theme.getIcon("edit"))
self.btnChapter.clicked.connect(lambda: self._editHeading(self.EDIT_CHAPTER))
@@ -636,9 +635,9 @@ class _HeadingsTab(NScrollablePage):
# Unnumbered Chapter Heading
self.lblUnnumbered = QLabel(self._build.getLabel("headings.fmtUnnumbered"))
self.fmtUnnumbered = QLineEdit("")
self.fmtUnnumbered = QLineEdit("", self)
self.fmtUnnumbered.setReadOnly(True)
self.btnUnnumbered = QToolButton()
self.btnUnnumbered = QToolButton(self)
self.btnUnnumbered.setIcon(SHARED.theme.getIcon("edit"))
self.btnUnnumbered.clicked.connect(lambda: self._editHeading(self.EDIT_UNNUM))
@@ -653,9 +652,9 @@ class _HeadingsTab(NScrollablePage):
# Scene Heading
sceneHideTip = self._build.getLabel("headings.hideScene")
self.lblScene = QLabel(self._build.getLabel("headings.fmtScene"))
self.fmtScene = QLineEdit("")
self.fmtScene = QLineEdit("", self)
self.fmtScene.setReadOnly(True)
self.btnScene = QToolButton()
self.btnScene = QToolButton(self)
self.btnScene.setIcon(SHARED.theme.getIcon("edit"))
self.btnScene.clicked.connect(lambda: self._editHeading(self.EDIT_SCENE))
self.hdeScene = QLabel(self.tr("Hide"))
@@ -680,9 +679,9 @@ class _HeadingsTab(NScrollablePage):
# Section Heading
sectionHideTip = self._build.getLabel("headings.hideSection")
self.lblSection = QLabel(self._build.getLabel("headings.fmtSection"))
self.fmtSection = QLineEdit("")
self.fmtSection = QLineEdit("", self)
self.fmtSection.setReadOnly(True)
self.btnSection = QToolButton()
self.btnSection = QToolButton(self)
self.btnSection.setIcon(SHARED.theme.getIcon("edit"))
self.btnSection.clicked.connect(lambda: self._editHeading(self.EDIT_SECTION))
self.hdeSection = QLabel(self.tr("Hide"))
@@ -867,7 +866,7 @@ class _HeadingSyntaxHighlighter(QSyntaxHighlighter):
def highlightBlock(self, text: str) -> None:
"""Add syntax highlighting to the text block."""
for heading in nwHeadFmt.ALL:
for heading in nwHeadFmt.PAGE_HEADERS:
pos = text.find(heading)
if pos >= 0:
chars = len(heading)
@@ -1211,14 +1210,34 @@ class _OutputTab(NScrollableForm):
self._build = build
iPx = SHARED.theme.baseIconSize
spW = 6*SHARED.theme.textNWidth
# Open Document
self.addGroupLabel(self._build.getLabel("odt"))
self.odtAddColours = NSwitch(self, width=2*iPx, height=iPx)
self.addRow(self._build.getLabel("odt.addColours"), self.odtAddColours)
self.odtPageHeader = QLineEdit(self)
self.odtPageHeader.setMinimumWidth(CONFIG.pxInt(200))
self.btnPageHeader = QToolButton(self)
self.btnPageHeader.setIcon(SHARED.theme.getIcon("revert"))
self.btnPageHeader.clicked.connect(self._resetPageHeader)
self.addRow(
self._build.getLabel("odt.pageHeader"), self.odtPageHeader,
button=self.btnPageHeader, stretch=(1, 1)
)
self.odtPageCountOffset = QSpinBox(self)
self.odtPageCountOffset.setMinimum(0)
self.odtPageCountOffset.setMaximum(999)
self.odtPageCountOffset.setSingleStep(1)
self.odtPageCountOffset.setMinimumWidth(spW)
self.addRow(self._build.getLabel("odt.pageCountOffset"), self.odtPageCountOffset)
# HTML Document
self.addGroupLabel(self._build.getLabel("html"))
self.htmlAddStyles = NSwitch(self, width=2*iPx, height=iPx)
self.addRow(self._build.getLabel("html.addStyles"), self.htmlAddStyles)
@@ -1230,13 +1249,26 @@ class _OutputTab(NScrollableForm):
def loadContent(self) -> None:
"""Populate the widgets."""
self.odtAddColours.setChecked(self._build.getBool("odt.addColours"))
self.odtPageHeader.setText(self._build.getStr("odt.pageHeader"))
self.odtPageCountOffset.setValue(self._build.getInt("odt.pageCountOffset"))
self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles"))
return
def saveContent(self) -> None:
"""Save choices back into build object."""
self._build.setValue("odt.addColours", self.odtAddColours.isChecked())
self._build.setValue("odt.pageHeader", self.odtPageHeader.text())
self._build.setValue("odt.pageCountOffset", self.odtPageCountOffset.value())
self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked())
return
##
# Private Slots
##
def _resetPageHeader(self) -> None:
"""Reset the ODT header format to default."""
self.odtPageHeader.setText(nwHeadFmt.ODT_AUTO)
return
# END Class _OutputTab
@@ -1,13 +1,13 @@
<?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:meta>
<meta:creation-date>2023-10-20T22:51:30</meta:creation-date>
<meta:generator>novelWriter/2.2-alpha1</meta:generator>
<meta:creation-date>2024-01-28T15:48:36</meta:creation-date>
<meta:generator>novelWriter/2.3a3</meta:generator>
<meta:initial-creator>Jane Smith</meta:initial-creator>
<meta:editing-cycles>1234</meta:editing-cycles>
<meta:editing-duration>P42DT12H34M56S</meta:editing-duration>
<dc:title>Test Project</dc:title>
<dc:date>2023-10-20T22:51:30</dc:date>
<dc:date>2024-01-28T15:48:36</dc:date>
<dc:creator>Jane Smith</dc:creator>
</office:meta>
<office:font-face-decls>
@@ -76,7 +76,7 @@
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="PM1">
<style:header>
<text:p text:style-name="Header">Test Project / Jane Smith / <text:page-number text:select-page="current">2</text:page-number></text:p>
<text:p text:style-name="Header">Test Project / Jane Smith / <text:page-number text:page-adjust="-1">2</text:page-number></text:p>
</style:header>
<style:header-first>
<text:p text:style-name="Header" />
@@ -63,8 +63,7 @@
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="PM1">
<style:header>
<text:p text:style-name="Header">Test Project / Jane Smith / <text:page-number text:select-page="current">2</text:page-number>
</text:p>
<text:p text:style-name="Header">Test Project - Jane Smith</text:p>
</style:header>
<style:header-first>
<text:p text:style-name="Header" />
+7
View File
@@ -29,6 +29,7 @@ from shutil import copyfile
from tools import ODT_IGNORE, cmpFiles
from novelwriter.common import xmlIndent
from novelwriter.constants import nwHeadFmt
from novelwriter.core.toodt import ToOdt, ODTParagraphStyle, ODTTextStyle, XMLParagraph, _mkTag
from novelwriter.core.project import NWProject
@@ -691,6 +692,9 @@ def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
odt.setLanguage("nb_NO")
assert odt._dLanguage == "nb"
odt.setColourHeaders(True)
assert odt._colourHead is True
odt.setHeaderFormat(nwHeadFmt.ODT_AUTO, 1)
assert odt._headerFormat == nwHeadFmt.ODT_AUTO
odt.setPageLayout(148, 210, 20, 18, 17, 15)
assert odt._mDocWidth == "14.800cm"
@@ -736,6 +740,9 @@ def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths):
odt = ToOdt(project, isFlat=False)
odt._isNovel = True
# Set a format without page number
odt.setHeaderFormat(f"{nwHeadFmt.ODT_PROJECT} - {nwHeadFmt.ODT_AUTHOR}", 0)
odt._text = (
"## Chapter One\n\n"
"Text\n\n"
@@ -649,18 +649,27 @@ def testBuildSettings_Output(qtbot: QtBot, nwGUI: GuiMain):
# Check initial values
assert outTab.odtAddColours.isChecked() is False
assert outTab.odtPageHeader.text() == nwHeadFmt.ODT_AUTO
assert outTab.htmlAddStyles.isChecked() is False
# Toggle all
outTab.odtAddColours.setChecked(True)
outTab.htmlAddStyles.setChecked(True)
# Change header format
outTab.odtPageHeader.setText("Stuff")
# Save values
outTab.saveContent()
assert build.getBool("odt.addColours") is True
assert build.getStr("odt.pageHeader") == "Stuff"
assert build.getBool("html.addStyles") is True
# Reset header format
outTab.btnPageHeader.click()
assert outTab.odtPageHeader.text() == nwHeadFmt.ODT_AUTO
# Finish
bSettings._dialogButtonClicked(bSettings.buttonBox.button(QDialogButtonBox.Close))
# qtbot.stop()