Improve first line indent (#1857)

This commit is contained in:
Veronica Berglyd Olsen
2024-05-10 15:49:59 +02:00
committed by GitHub
9 changed files with 155 additions and 107 deletions
+58 -51
View File
@@ -25,21 +25,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations from __future__ import annotations
import json import json
import uuid
import logging import logging
import uuid
from collections.abc import Iterable
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from collections.abc import Iterable
from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.enum import nwBuildFmt
from novelwriter.error import logException
from novelwriter.common import checkUuid, isHandle, jsonEncode from novelwriter.common import checkUuid, isHandle, jsonEncode
from novelwriter.constants import nwFiles, nwHeadFmt from novelwriter.constants import nwFiles, nwHeadFmt
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.enum import nwBuildFmt
from novelwriter.error import logException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -82,6 +82,9 @@ SETTINGS_TEMPLATE = {
"format.justifyText": (bool, False), "format.justifyText": (bool, False),
"format.stripUnicode": (bool, False), "format.stripUnicode": (bool, False),
"format.replaceTabs": (bool, False), "format.replaceTabs": (bool, False),
"format.firstLineIndent": (bool, False),
"format.firstIndentWidth": (float, 1.4),
"format.indentFirstPar": (bool, False),
"format.pageUnit": (str, "cm"), "format.pageUnit": (str, "cm"),
"format.pageSize": (str, "A4"), "format.pageSize": (str, "A4"),
"format.pageWidth": (float, 21.0), "format.pageWidth": (float, 21.0),
@@ -93,65 +96,69 @@ SETTINGS_TEMPLATE = {
"odt.addColours": (bool, True), "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.firstLineIndent": (bool, False),
"md.preserveBreaks": (bool, True), "md.preserveBreaks": (bool, True),
"html.addStyles": (bool, True), "html.addStyles": (bool, True),
"html.preserveTabs": (bool, False), "html.preserveTabs": (bool, False),
} }
SETTINGS_LABELS = { SETTINGS_LABELS = {
"filter": QT_TRANSLATE_NOOP("Builds", "Document Filters"), "filter": QT_TRANSLATE_NOOP("Builds", "Document Filters"),
"filter.includeNovel": QT_TRANSLATE_NOOP("Builds", "Novel Documents"), "filter.includeNovel": QT_TRANSLATE_NOOP("Builds", "Novel Documents"),
"filter.includeNotes": QT_TRANSLATE_NOOP("Builds", "Project Notes"), "filter.includeNotes": QT_TRANSLATE_NOOP("Builds", "Project Notes"),
"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.fmtTitle": 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"),
"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"),
"text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"), "text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"),
"text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"), "text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"),
"text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"), "text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"),
"text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"), "text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"),
"text.grpInsert": QT_TRANSLATE_NOOP("Builds", "Insert Content"), "text.grpInsert": QT_TRANSLATE_NOOP("Builds", "Insert Content"),
"text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"), "text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"),
"format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"), "format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"),
"format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"), "format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"),
"format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"), "format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"),
"format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"), "format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"),
"format.grpOptions": QT_TRANSLATE_NOOP("Builds", "Text Options"), "format.grpOptions": QT_TRANSLATE_NOOP("Builds", "Text Options"),
"format.justifyText": QT_TRANSLATE_NOOP("Builds", "Justify Text Margins"), "format.justifyText": QT_TRANSLATE_NOOP("Builds", "Justify Text Margins"),
"format.stripUnicode": QT_TRANSLATE_NOOP("Builds", "Replace Unicode Characters"), "format.stripUnicode": QT_TRANSLATE_NOOP("Builds", "Replace Unicode Characters"),
"format.replaceTabs": QT_TRANSLATE_NOOP("Builds", "Replace Tabs with Spaces"), "format.replaceTabs": QT_TRANSLATE_NOOP("Builds", "Replace Tabs with Spaces"),
"format.grpPage": QT_TRANSLATE_NOOP("Builds", "Page Layout"),
"format.pageUnit": QT_TRANSLATE_NOOP("Builds", "Unit"),
"format.pageSize": QT_TRANSLATE_NOOP("Builds", "Page Size"),
"format.pageWidth": QT_TRANSLATE_NOOP("Builds", "Page Width"),
"format.pageHeight": QT_TRANSLATE_NOOP("Builds", "Page Height"),
"format.topMargin": QT_TRANSLATE_NOOP("Builds", "Top Margin"),
"format.bottomMargin": QT_TRANSLATE_NOOP("Builds", "Bottom Margin"),
"format.leftMargin": QT_TRANSLATE_NOOP("Builds", "Left Margin"),
"format.rightMargin": QT_TRANSLATE_NOOP("Builds", "Right Margin"),
"odt": QT_TRANSLATE_NOOP("Builds", "Open Document (.odt)"), "format.grpParIndent": QT_TRANSLATE_NOOP("Builds", "First Line Indent"),
"odt.addColours": QT_TRANSLATE_NOOP("Builds", "Add Highlight Colours"), "format.firstLineIndent": QT_TRANSLATE_NOOP("Builds", "Enable Indent"),
"odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"), "format.firstIndentWidth": QT_TRANSLATE_NOOP("Builds", "Indent Width"),
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"), "format.indentFirstPar": QT_TRANSLATE_NOOP("Builds", "Indent First Paragraph"),
"odt.firstLineIndent": QT_TRANSLATE_NOOP("Builds", "First Line Indent"),
"md": QT_TRANSLATE_NOOP("Builds", "Markdown (.md)"), "format.grpPage": QT_TRANSLATE_NOOP("Builds", "Page Layout"),
"md.preserveBreaks": QT_TRANSLATE_NOOP("Builds", "Preserve Hard Line Breaks"), "format.pageUnit": QT_TRANSLATE_NOOP("Builds", "Unit"),
"format.pageSize": QT_TRANSLATE_NOOP("Builds", "Page Size"),
"format.pageWidth": QT_TRANSLATE_NOOP("Builds", "Page Width"),
"format.pageHeight": QT_TRANSLATE_NOOP("Builds", "Page Height"),
"format.topMargin": QT_TRANSLATE_NOOP("Builds", "Top Margin"),
"format.bottomMargin": QT_TRANSLATE_NOOP("Builds", "Bottom Margin"),
"format.leftMargin": QT_TRANSLATE_NOOP("Builds", "Left Margin"),
"format.rightMargin": QT_TRANSLATE_NOOP("Builds", "Right Margin"),
"html": QT_TRANSLATE_NOOP("Builds", "HTML (.html)"), "odt": QT_TRANSLATE_NOOP("Builds", "Open Document (.odt)"),
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"), "odt.addColours": QT_TRANSLATE_NOOP("Builds", "Add Highlight Colours"),
"html.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"), "odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
"md": QT_TRANSLATE_NOOP("Builds", "Markdown (.md)"),
"md.preserveBreaks": QT_TRANSLATE_NOOP("Builds", "Preserve Hard Line Breaks"),
"html": QT_TRANSLATE_NOOP("Builds", "HTML (.html)"),
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
"html.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"),
} }
+12 -8
View File
@@ -25,22 +25,22 @@ from __future__ import annotations
import logging import logging
from pathlib import Path
from collections.abc import Iterable from collections.abc import Iterable
from pathlib import Path
from PyQt5.QtGui import QFont, QFontInfo from PyQt5.QtGui import QFont, QFontInfo
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.enum import nwBuildFmt
from novelwriter.error import formatException, logException
from novelwriter.constants import nwLabels from novelwriter.constants import nwLabels
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.tohtml import ToHtml
from novelwriter.core.tokenizer import Tokenizer
from novelwriter.core.tomd import ToMarkdown from novelwriter.core.tomd import ToMarkdown
from novelwriter.core.toodt import ToOdt from novelwriter.core.toodt import ToOdt
from novelwriter.core.tohtml import ToHtml from novelwriter.enum import nwBuildFmt
from novelwriter.core.project import NWProject from novelwriter.error import formatException, logException
from novelwriter.core.tokenizer import Tokenizer
from novelwriter.core.buildsettings import BuildSettings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -333,6 +333,11 @@ class NWBuildDocument:
bldObj.setFont(fontFamily, textSize, textFixed) bldObj.setFont(fontFamily, textSize, textFixed)
bldObj.setJustify(self._build.getBool("format.justifyText")) bldObj.setJustify(self._build.getBool("format.justifyText"))
bldObj.setLineHeight(self._build.getFloat("format.lineHeight")) bldObj.setLineHeight(self._build.getFloat("format.lineHeight"))
bldObj.setFirstLineIndent(
self._build.getBool("format.firstLineIndent"),
self._build.getFloat("format.firstIndentWidth"),
self._build.getBool("format.indentFirstPar"),
)
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"))
@@ -350,7 +355,6 @@ class NWBuildDocument:
bldObj.setHeaderFormat( bldObj.setHeaderFormat(
self._build.getStr("odt.pageHeader"), self._build.getInt("odt.pageCountOffset") self._build.getStr("odt.pageHeader"), self._build.getInt("odt.pageCountOffset")
) )
bldObj.setFirstLineIndent(self._build.getBool("odt.firstLineIndent"))
scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0) 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)) pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0))
+13 -1
View File
@@ -116,6 +116,7 @@ class Tokenizer(ABC):
# Lookups # Lookups
L_HEADINGS = [T_TITLE, T_HEAD1, T_HEAD2, T_HEAD3, T_HEAD4] L_HEADINGS = [T_TITLE, T_HEAD1, T_HEAD2, T_HEAD3, T_HEAD4]
L_SKIP_INDENT = [T_TITLE, T_HEAD1, T_HEAD2, T_HEAD2, T_HEAD3, T_HEAD4, T_SEP, T_SKIP]
def __init__(self, project: NWProject) -> None: def __init__(self, project: NWProject) -> None:
@@ -142,7 +143,9 @@ class Tokenizer(ABC):
self._textFixed = False # Fixed width text self._textFixed = False # Fixed width text
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._blockIndent = 4.00 # Block indent in units of em
self._textIndent = 1.40 # First line indent in units of em self._firstIndent = False # Enable first line indent
self._firstWidth = 1.40 # First line indent in units of em
self._indentFirst = False # Indent first paragraph
self._doJustify = False # Justify text self._doJustify = False # Justify text
self._doBodyText = True # Include body text self._doBodyText = True # Include body text
self._doSynopsis = False # Also process synopsis comments self._doSynopsis = False # Also process synopsis comments
@@ -327,6 +330,15 @@ class Tokenizer(ABC):
self._blockIndent = min(max(float(indent), 0.0), 10.0) self._blockIndent = min(max(float(indent), 0.0), 10.0)
return return
def setFirstLineIndent(self, state: bool, indent: float, first: bool) -> None:
"""Set first line indent and whether to also indent first
paragraph after a heading.
"""
self._firstIndent = state
self._firstWidth = indent
self._indentFirst = first
return
def setJustify(self, state: bool) -> None: def setJustify(self, state: bool) -> None:
"""Enable or disable text justification.""" """Enable or disable text justification."""
self._doJustify = state self._doJustify = state
+2 -8
View File
@@ -141,7 +141,6 @@ class ToOdt(Tokenizer):
self._textSize = 12 self._textSize = 12
self._textFixed = False self._textFixed = False
self._colourHead = False self._colourHead = False
self._firstIndent = False
self._headerFormat = "" self._headerFormat = ""
self._pageOffset = 0 self._pageOffset = 0
@@ -240,11 +239,6 @@ class ToOdt(Tokenizer):
self._pageOffset = offset self._pageOffset = offset
return return
def setFirstLineIndent(self, state: bool) -> None:
"""Enable or disable first line indent."""
self._firstIndent = state
return
## ##
# Class Methods # Class Methods
## ##
@@ -301,7 +295,7 @@ class ToOdt(Tokenizer):
self._fLineHeight = f"{round(100 * self._lineHeight):d}%" self._fLineHeight = f"{round(100 * self._lineHeight):d}%"
self._fBlockIndent = self._emToCm(self._blockIndent) self._fBlockIndent = self._emToCm(self._blockIndent)
self._fTextIndent = self._emToCm(self._textIndent) self._fTextIndent = self._emToCm(self._firstWidth)
self._textAlign = "justify" if self._doJustify else "left" self._textAlign = "justify" if self._doJustify else "left"
# Clear Errors # Clear Errors
@@ -447,7 +441,7 @@ class ToOdt(Tokenizer):
if tStyle & self.A_IND_R: if tStyle & self.A_IND_R:
oStyle.setMarginRight(self._fBlockIndent) oStyle.setMarginRight(self._fBlockIndent)
if tType not in (self.T_EMPTY, self.T_TEXT): if not self._indentFirst and tType in self.L_SKIP_INDENT:
pIndent = False pIndent = False
# Process Text Types # Process Text Types
+27 -5
View File
@@ -1113,6 +1113,25 @@ class _FormatTab(NScrollableForm):
self.addRow(self._build.getLabel("format.stripUnicode"), self.stripUnicode) self.addRow(self._build.getLabel("format.stripUnicode"), self.stripUnicode)
self.addRow(self._build.getLabel("format.replaceTabs"), self.replaceTabs) self.addRow(self._build.getLabel("format.replaceTabs"), self.replaceTabs)
# First Line Indent
# =================
self.addGroupLabel(self._build.getLabel("format.grpParIndent"))
self.firstIndent = NSwitch(self, height=iPx)
self.indentFirstPar = NSwitch(self, height=iPx)
self.indentWidth = NDoubleSpinBox(self)
self.indentWidth.setFixedWidth(spW)
self.indentWidth.setMinimum(0.1)
self.indentWidth.setMaximum(9.9)
self.indentWidth.setSingleStep(0.1)
self.indentWidth.setDecimals(1)
self.addRow(self._build.getLabel("format.firstLineIndent"), self.firstIndent)
self.addRow(self._build.getLabel("format.firstIndentWidth"), self.indentWidth, unit="em")
self.addRow(self._build.getLabel("format.indentFirstPar"), self.indentFirstPar)
# Page Layout # Page Layout
# =========== # ===========
@@ -1176,6 +1195,10 @@ class _FormatTab(NScrollableForm):
self.stripUnicode.setChecked(self._build.getBool("format.stripUnicode")) self.stripUnicode.setChecked(self._build.getBool("format.stripUnicode"))
self.replaceTabs.setChecked(self._build.getBool("format.replaceTabs")) self.replaceTabs.setChecked(self._build.getBool("format.replaceTabs"))
self.firstIndent.setChecked(self._build.getBool("format.firstLineIndent"))
self.indentWidth.setValue(self._build.getFloat("format.firstIndentWidth"))
self.indentFirstPar.setChecked(self._build.getBool("format.indentFirstPar"))
pageUnit = self._build.getStr("format.pageUnit") pageUnit = self._build.getStr("format.pageUnit")
index = self.pageUnit.findData(pageUnit) index = self.pageUnit.findData(pageUnit)
if index >= 0: if index >= 0:
@@ -1211,6 +1234,10 @@ class _FormatTab(NScrollableForm):
self._build.setValue("format.stripUnicode", self.stripUnicode.isChecked()) self._build.setValue("format.stripUnicode", self.stripUnicode.isChecked())
self._build.setValue("format.replaceTabs", self.replaceTabs.isChecked()) self._build.setValue("format.replaceTabs", self.replaceTabs.isChecked())
self._build.setValue("format.firstLineIndent", self.firstIndent.isChecked())
self._build.setValue("format.firstIndentWidth", self.indentWidth.value())
self._build.setValue("format.indentFirstPar", self.indentFirstPar.isChecked())
self._build.setValue("format.pageUnit", str(self.pageUnit.currentData())) self._build.setValue("format.pageUnit", str(self.pageUnit.currentData()))
self._build.setValue("format.pageSize", str(self.pageSize.currentData())) self._build.setValue("format.pageSize", str(self.pageSize.currentData()))
self._build.setValue("format.pageWidth", self.pageWidth.value()) self._build.setValue("format.pageWidth", self.pageWidth.value())
@@ -1355,9 +1382,6 @@ class _OutputTab(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)
self.odtFirstLineIndent = NSwitch(self, height=iPx)
self.addRow(self._build.getLabel("odt.firstLineIndent"), self.odtFirstLineIndent)
# HTML Document # HTML Document
self.addGroupLabel(self._build.getLabel("html")) self.addGroupLabel(self._build.getLabel("html"))
@@ -1383,7 +1407,6 @@ class _OutputTab(NScrollableForm):
self.odtAddColours.setChecked(self._build.getBool("odt.addColours")) self.odtAddColours.setChecked(self._build.getBool("odt.addColours"))
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.odtFirstLineIndent.setChecked(self._build.getBool("odt.firstLineIndent"))
self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles")) self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles"))
self.htmlPreserveTabs.setChecked(self._build.getBool("html.preserveTabs")) self.htmlPreserveTabs.setChecked(self._build.getBool("html.preserveTabs"))
self.mdPreserveBreaks.setChecked(self._build.getBool("md.preserveBreaks")) self.mdPreserveBreaks.setChecked(self._build.getBool("md.preserveBreaks"))
@@ -1395,7 +1418,6 @@ class _OutputTab(NScrollableForm):
self._build.setValue("odt.addColours", self.odtAddColours.isChecked()) self._build.setValue("odt.addColours", self.odtAddColours.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())
self._build.setValue("odt.firstLineIndent", self.odtFirstLineIndent.isChecked())
self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked()) self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked())
self._build.setValue("html.preserveTabs", self.htmlPreserveTabs.isChecked()) self._build.setValue("html.preserveTabs", self.htmlPreserveTabs.isChecked())
self._build.setValue("md.preserveBreaks", self.mdPreserveBreaks.isChecked()) self._build.setValue("md.preserveBreaks", self.mdPreserveBreaks.isChecked())
@@ -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-04-27T16:43:44</meta:creation-date> <meta:creation-date>2024-05-10T15:23:56</meta:creation-date>
<meta:generator>novelWriter/2.5a2</meta:generator> <meta:generator>novelWriter/2.5a3</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-04-27T16:43:44</dc:date> <dc:date>2024-05-10T15:23:56</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>
@@ -121,7 +121,7 @@
<text:p text:style-name="P1">“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</text:p> <text:p text:style-name="P1">“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</text:p>
<text:p text:style-name="P1">“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</text:p> <text:p text:style-name="P1">“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</text:p>
<text:p text:style-name="P2"><text:span text:style-name="T1">Comment:</text:span> Exctracted from the lipsum.com website.</text:p> <text:p text:style-name="P2"><text:span text:style-name="T1">Comment:</text:span> Exctracted from the lipsum.com website.</text:p>
<text:p text:style-name="Text_20_body">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</text:p> <text:p text:style-name="First_20_line_20_indent">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</text:p>
<text:p text:style-name="First_20_line_20_indent">The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</text:p> <text:p text:style-name="First_20_line_20_indent">The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</text:p>
<text:h text:style-name="P3" text:outline-level="2">Prologue</text:h> <text:h text:style-name="P3" text:outline-level="2">Prologue</text:h>
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Explanation from the lipsum.com website.</text:p> <text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Explanation from the lipsum.com website.</text:p>
+13 -12
View File
@@ -21,21 +21,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations from __future__ import annotations
import json import json
from pathlib import Path
from shutil import copyfile
import pytest import pytest
from shutil import copyfile from novelwriter.core.buildsettings import BuildSettings
from pathlib import Path from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.core.project import NWProject
from tools import C, ODT_IGNORE, buildTestProject, cmpFiles from novelwriter.core.tohtml import ToHtml
from mocked import causeException, causeOSError
from novelwriter.enum import nwBuildFmt
from novelwriter.core.tomd import ToMarkdown from novelwriter.core.tomd import ToMarkdown
from novelwriter.core.toodt import ToOdt from novelwriter.core.toodt import ToOdt
from novelwriter.core.tohtml import ToHtml from novelwriter.enum import nwBuildFmt
from novelwriter.core.project import NWProject
from novelwriter.core.docbuild import NWBuildDocument from tests.mocked import causeException, causeOSError
from novelwriter.core.buildsettings import BuildSettings from tests.tools import ODT_IGNORE, C, buildTestProject, cmpFiles
BUILD_CONF = { BUILD_CONF = {
"name": "Test Build", "name": "Test Build",
@@ -63,8 +64,8 @@ BUILD_CONF = {
"format.justifyText": True, "format.justifyText": True,
"format.stripUnicode": False, "format.stripUnicode": False,
"format.replaceTabs": True, "format.replaceTabs": True,
"format.firstLineIndent": True,
"odt.addColours": True, "odt.addColours": True,
"odt.firstLineIndent": True,
"html.addStyles": True, "html.addStyles": True,
}, },
"content": { "content": {
+3 -1
View File
@@ -753,8 +753,10 @@ def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
assert odt._colourHead is 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) odt.setFirstLineIndent(True, 1.4, False)
assert odt._firstIndent is True assert odt._firstIndent is True
assert odt._fTextIndent == "0.499cm"
assert odt._indentFirst is False
odt.setPageLayout(148, 210, 20, 18, 17, 15) odt.setPageLayout(148, 210, 20, 18, 17, 15)
assert odt._mDocWidth == "14.800cm" assert odt._mDocWidth == "14.800cm"
+23 -17
View File
@@ -22,27 +22,24 @@ from __future__ import annotations
import pytest import pytest
from pathlib import Path
from pytestqt.qtbot import QtBot
from tools import C, buildTestProject
from PyQt5.QtGui import QFont
from PyQt5.QtCore import pyqtSlot from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QFontDialog from PyQt5.QtWidgets import QFontDialog
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.guimain import GuiMain
from novelwriter.constants import nwHeadFmt from novelwriter.constants import nwHeadFmt
from novelwriter.core.buildsettings import BuildSettings, FilterMode from novelwriter.core.buildsettings import BuildSettings, FilterMode
from novelwriter.tools.manussettings import ( from novelwriter.tools.manussettings import (
GuiBuildSettings, _OutputTab, _FormatTab, _ContentTab, _HeadingsTab, _FilterTab GuiBuildSettings, _ContentTab, _FilterTab, _FormatTab, _HeadingsTab,
_OutputTab
) )
from novelwriter.types import QtDialogApply, QtDialogClose, QtDialogSave from novelwriter.types import QtDialogApply, QtDialogClose, QtDialogSave
from tests.tools import C, buildTestProject
@pytest.mark.gui @pytest.mark.gui
def testBuildSettings_Init(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockRnd): def testBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
"""Test the initialisation of the GuiBuildSettings dialog.""" """Test the initialisation of the GuiBuildSettings dialog."""
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
nwGUI.openProject(projPath) nwGUI.openProject(projPath)
@@ -114,7 +111,7 @@ def testBuildSettings_Init(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockRnd
@pytest.mark.gui @pytest.mark.gui
def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockRnd): def testBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
"""Test the Filter Tab of the GuiBuildSettings dialog.""" """Test the Filter Tab of the GuiBuildSettings dialog."""
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
nwGUI.openProject(projPath) nwGUI.openProject(projPath)
@@ -320,7 +317,7 @@ def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockR
@pytest.mark.gui @pytest.mark.gui
def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain): def testBuildSettings_Headings(qtbot, nwGUI):
"""Test the Headings Tab of the GuiBuildSettings dialog.""" """Test the Headings Tab of the GuiBuildSettings dialog."""
build = BuildSettings() build = BuildSettings()
@@ -492,7 +489,7 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
@pytest.mark.gui @pytest.mark.gui
def testBuildSettings_Content(qtbot: QtBot, nwGUI: GuiMain): def testBuildSettings_Content(qtbot, nwGUI):
"""Test the Content Tab of the GuiBuildSettings dialog.""" """Test the Content Tab of the GuiBuildSettings dialog."""
build = BuildSettings() build = BuildSettings()
@@ -554,7 +551,7 @@ def testBuildSettings_Content(qtbot: QtBot, nwGUI: GuiMain):
@pytest.mark.gui @pytest.mark.gui
def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain): def testBuildSettings_Format(monkeypatch, qtbot, nwGUI):
"""Test the Format Tab of the GuiBuildSettings dialog.""" """Test the Format Tab of the GuiBuildSettings dialog."""
build = BuildSettings() build = BuildSettings()
@@ -596,6 +593,10 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
assert fmtTab.stripUnicode.isChecked() is False assert fmtTab.stripUnicode.isChecked() is False
assert fmtTab.replaceTabs.isChecked() is False assert fmtTab.replaceTabs.isChecked() is False
assert fmtTab.firstIndent.isChecked() is False
assert fmtTab.indentWidth.value() == 1.4
assert fmtTab.indentFirstPar.isChecked() is False
assert fmtTab.pageUnit.currentData() == "mm" assert fmtTab.pageUnit.currentData() == "mm"
assert fmtTab.pageSize.currentData() == "Custom" assert fmtTab.pageSize.currentData() == "Custom"
assert fmtTab.pageWidth.value() == 180.0 assert fmtTab.pageWidth.value() == 180.0
@@ -614,6 +615,10 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
fmtTab.stripUnicode.setChecked(True) fmtTab.stripUnicode.setChecked(True)
fmtTab.replaceTabs.setChecked(True) fmtTab.replaceTabs.setChecked(True)
fmtTab.firstIndent.setChecked(True)
fmtTab.indentWidth.setValue(2.0)
fmtTab.indentFirstPar.setChecked(True)
fmtTab.pageUnit.setCurrentIndex(fmtTab.pageUnit.findData("cm")) fmtTab.pageUnit.setCurrentIndex(fmtTab.pageUnit.findData("cm"))
fmtTab.pageSize.setCurrentIndex(fmtTab.pageSize.findData("A4")) fmtTab.pageSize.setCurrentIndex(fmtTab.pageSize.findData("A4"))
@@ -628,6 +633,10 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
assert build.getBool("format.stripUnicode") is True assert build.getBool("format.stripUnicode") is True
assert build.getBool("format.replaceTabs") is True assert build.getBool("format.replaceTabs") is True
assert build.getBool("format.firstLineIndent") is True
assert build.getFloat("format.firstIndentWidth") == 2.0
assert build.getBool("format.indentFirstPar") is True
assert fmtTab.pageUnit.currentData() == "cm" assert fmtTab.pageUnit.currentData() == "cm"
assert fmtTab.pageSize.currentData() == "A4" assert fmtTab.pageSize.currentData() == "A4"
assert fmtTab.pageWidth.value() == 21.0 assert fmtTab.pageWidth.value() == 21.0
@@ -656,7 +665,7 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
@pytest.mark.gui @pytest.mark.gui
def testBuildSettings_Output(qtbot: QtBot, nwGUI: GuiMain): def testBuildSettings_Output(qtbot, nwGUI):
"""Test the Output Tab of the GuiBuildSettings dialog.""" """Test the Output Tab of the GuiBuildSettings dialog."""
build = BuildSettings() build = BuildSettings()
@@ -676,14 +685,12 @@ def testBuildSettings_Output(qtbot: QtBot, nwGUI: GuiMain):
assert outTab.odtAddColours.isChecked() is False assert outTab.odtAddColours.isChecked() is False
assert outTab.odtPageHeader.text() == nwHeadFmt.ODT_AUTO assert outTab.odtPageHeader.text() == nwHeadFmt.ODT_AUTO
assert outTab.odtPageCountOffset.value() == 0 assert outTab.odtPageCountOffset.value() == 0
assert outTab.odtFirstLineIndent.isChecked() is False
assert outTab.htmlAddStyles.isChecked() is False assert outTab.htmlAddStyles.isChecked() is False
assert outTab.htmlPreserveTabs.isChecked() is False assert outTab.htmlPreserveTabs.isChecked() is False
assert outTab.mdPreserveBreaks.isChecked() is True assert outTab.mdPreserveBreaks.isChecked() is True
# Toggle all # Toggle all
outTab.odtAddColours.setChecked(True) outTab.odtAddColours.setChecked(True)
outTab.odtFirstLineIndent.setChecked(True)
outTab.htmlAddStyles.setChecked(True) outTab.htmlAddStyles.setChecked(True)
outTab.htmlPreserveTabs.setChecked(True) outTab.htmlPreserveTabs.setChecked(True)
outTab.mdPreserveBreaks.setChecked(False) outTab.mdPreserveBreaks.setChecked(False)
@@ -698,7 +705,6 @@ def testBuildSettings_Output(qtbot: QtBot, nwGUI: GuiMain):
assert build.getBool("odt.addColours") is True 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.firstLineIndent") is True
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
assert build.getBool("md.preserveBreaks") is False assert build.getBool("md.preserveBreaks") is False