diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index b68dd97d..a1f215fd 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -25,21 +25,21 @@ along with this program. If not, see . from __future__ import annotations import json -import uuid import logging +import uuid +from collections.abc import Iterable from enum import Enum from pathlib import Path -from collections.abc import Iterable from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication from novelwriter import CONFIG -from novelwriter.enum import nwBuildFmt -from novelwriter.error import logException from novelwriter.common import checkUuid, isHandle, jsonEncode from novelwriter.constants import nwFiles, nwHeadFmt from novelwriter.core.project import NWProject +from novelwriter.enum import nwBuildFmt +from novelwriter.error import logException logger = logging.getLogger(__name__) @@ -82,6 +82,9 @@ SETTINGS_TEMPLATE = { "format.justifyText": (bool, False), "format.stripUnicode": (bool, False), "format.replaceTabs": (bool, False), + "format.firstLineIndent": (bool, False), + "format.firstIndentWidth": (float, 1.4), + "format.indentFirstPar": (bool, False), "format.pageUnit": (str, "cm"), "format.pageSize": (str, "A4"), "format.pageWidth": (float, 21.0), @@ -93,65 +96,69 @@ SETTINGS_TEMPLATE = { "odt.addColours": (bool, True), "odt.pageHeader": (str, nwHeadFmt.ODT_AUTO), "odt.pageCountOffset": (int, 0), - "odt.firstLineIndent": (bool, False), "md.preserveBreaks": (bool, True), "html.addStyles": (bool, True), "html.preserveTabs": (bool, False), } SETTINGS_LABELS = { - "filter": QT_TRANSLATE_NOOP("Builds", "Document Filters"), - "filter.includeNovel": QT_TRANSLATE_NOOP("Builds", "Novel Documents"), - "filter.includeNotes": QT_TRANSLATE_NOOP("Builds", "Project Notes"), - "filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"), + "filter": QT_TRANSLATE_NOOP("Builds", "Document Filters"), + "filter.includeNovel": QT_TRANSLATE_NOOP("Builds", "Novel Documents"), + "filter.includeNotes": QT_TRANSLATE_NOOP("Builds", "Project Notes"), + "filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"), - "headings": QT_TRANSLATE_NOOP("Builds", "Headings"), - "headings.fmtTitle": 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": QT_TRANSLATE_NOOP("Builds", "Headings"), + "headings.fmtTitle": 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"), - "text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"), - "text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"), - "text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"), - "text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"), - "text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"), - "text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"), - "text.grpInsert": QT_TRANSLATE_NOOP("Builds", "Insert Content"), - "text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"), + "text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"), + "text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"), + "text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"), + "text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"), + "text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"), + "text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"), + "text.grpInsert": QT_TRANSLATE_NOOP("Builds", "Insert Content"), + "text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"), - "format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"), - "format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"), - "format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"), - "format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"), - "format.grpOptions": QT_TRANSLATE_NOOP("Builds", "Text Options"), - "format.justifyText": QT_TRANSLATE_NOOP("Builds", "Justify Text Margins"), - "format.stripUnicode": QT_TRANSLATE_NOOP("Builds", "Replace Unicode Characters"), - "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"), + "format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"), + "format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"), + "format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"), + "format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"), + "format.grpOptions": QT_TRANSLATE_NOOP("Builds", "Text Options"), + "format.justifyText": QT_TRANSLATE_NOOP("Builds", "Justify Text Margins"), + "format.stripUnicode": QT_TRANSLATE_NOOP("Builds", "Replace Unicode Characters"), + "format.replaceTabs": QT_TRANSLATE_NOOP("Builds", "Replace Tabs with Spaces"), - "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"), - "odt.firstLineIndent": QT_TRANSLATE_NOOP("Builds", "First Line Indent"), + "format.grpParIndent": QT_TRANSLATE_NOOP("Builds", "First Line Indent"), + "format.firstLineIndent": QT_TRANSLATE_NOOP("Builds", "Enable Indent"), + "format.firstIndentWidth": QT_TRANSLATE_NOOP("Builds", "Indent Width"), + "format.indentFirstPar": QT_TRANSLATE_NOOP("Builds", "Indent First Paragraph"), - "md": QT_TRANSLATE_NOOP("Builds", "Markdown (.md)"), - "md.preserveBreaks": QT_TRANSLATE_NOOP("Builds", "Preserve Hard Line Breaks"), + "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"), - "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"), + "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"), + + "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"), } diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 3c791334..17452c22 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -25,22 +25,22 @@ from __future__ import annotations import logging -from pathlib import Path from collections.abc import Iterable +from pathlib import Path from PyQt5.QtGui import QFont, QFontInfo from novelwriter import CONFIG -from novelwriter.enum import nwBuildFmt -from novelwriter.error import formatException, logException from novelwriter.constants import nwLabels +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.tomd import ToMarkdown from novelwriter.core.toodt import ToOdt -from novelwriter.core.tohtml import ToHtml -from novelwriter.core.project import NWProject -from novelwriter.core.tokenizer import Tokenizer -from novelwriter.core.buildsettings import BuildSettings +from novelwriter.enum import nwBuildFmt +from novelwriter.error import formatException, logException logger = logging.getLogger(__name__) @@ -333,6 +333,11 @@ class NWBuildDocument: bldObj.setFont(fontFamily, textSize, textFixed) bldObj.setJustify(self._build.getBool("format.justifyText")) 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.setSynopsis(self._build.getBool("text.includeSynopsis")) @@ -350,7 +355,6 @@ class NWBuildDocument: bldObj.setHeaderFormat( 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) pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0)) diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index efeaacde..148fe574 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -116,6 +116,7 @@ class Tokenizer(ABC): # Lookups 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: @@ -142,7 +143,9 @@ class Tokenizer(ABC): self._textFixed = False # Fixed width text self._lineHeight = 1.15 # Line height 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._doBodyText = True # Include body text self._doSynopsis = False # Also process synopsis comments @@ -327,6 +330,15 @@ class Tokenizer(ABC): self._blockIndent = min(max(float(indent), 0.0), 10.0) 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: """Enable or disable text justification.""" self._doJustify = state diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index 435f6d0c..92003f52 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -141,7 +141,6 @@ class ToOdt(Tokenizer): self._textSize = 12 self._textFixed = False self._colourHead = False - self._firstIndent = False self._headerFormat = "" self._pageOffset = 0 @@ -240,11 +239,6 @@ class ToOdt(Tokenizer): self._pageOffset = offset return - def setFirstLineIndent(self, state: bool) -> None: - """Enable or disable first line indent.""" - self._firstIndent = state - return - ## # Class Methods ## @@ -301,7 +295,7 @@ class ToOdt(Tokenizer): self._fLineHeight = f"{round(100 * self._lineHeight):d}%" 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" # Clear Errors @@ -447,7 +441,7 @@ class ToOdt(Tokenizer): if tStyle & self.A_IND_R: 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 # Process Text Types diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index 77b8035a..b3f7d1d5 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -1113,6 +1113,25 @@ class _FormatTab(NScrollableForm): self.addRow(self._build.getLabel("format.stripUnicode"), self.stripUnicode) 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 # =========== @@ -1176,6 +1195,10 @@ class _FormatTab(NScrollableForm): self.stripUnicode.setChecked(self._build.getBool("format.stripUnicode")) 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") index = self.pageUnit.findData(pageUnit) if index >= 0: @@ -1211,6 +1234,10 @@ class _FormatTab(NScrollableForm): self._build.setValue("format.stripUnicode", self.stripUnicode.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.pageSize", str(self.pageSize.currentData())) self._build.setValue("format.pageWidth", self.pageWidth.value()) @@ -1355,9 +1382,6 @@ class _OutputTab(NScrollableForm): self.odtPageCountOffset.setMinimumWidth(spW) 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 self.addGroupLabel(self._build.getLabel("html")) @@ -1383,7 +1407,6 @@ class _OutputTab(NScrollableForm): 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.odtFirstLineIndent.setChecked(self._build.getBool("odt.firstLineIndent")) self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles")) self.htmlPreserveTabs.setChecked(self._build.getBool("html.preserveTabs")) 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.pageHeader", self.odtPageHeader.text()) 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.preserveTabs", self.htmlPreserveTabs.isChecked()) self._build.setValue("md.preserveBreaks", self.mdPreserveBreaks.isChecked())