diff --git a/novelwriter/assets/icons/typicons_dark/icons.conf b/novelwriter/assets/icons/typicons_dark/icons.conf index 28d72c837..a4f8cf166 100644 --- a/novelwriter/assets/icons/typicons_dark/icons.conf +++ b/novelwriter/assets/icons/typicons_dark/icons.conf @@ -59,6 +59,7 @@ fmt_strike-md = nw_tb-strike-md.svg fmt_subscript = nw_tb-subscript.svg fmt_superscript = nw_tb-superscript.svg fmt_underline = nw_tb-underline.svg +font = nw_font.svg forward = typ_chevron-right.svg import = mixed_import.svg list = typ_th-list.svg @@ -78,6 +79,7 @@ proj_scene = mixed_document-scene.svg proj_section = mixed_document-section.svg proj_stats = typ_chart-bar-grey.svg proj_title = mixed_document-title.svg +quote = nw_quote.svg refresh = typ_refresh.svg remove = typ_minus.svg revert = typ_refresh-flipped.svg diff --git a/novelwriter/assets/icons/typicons_dark/nw_font.svg b/novelwriter/assets/icons/typicons_dark/nw_font.svg new file mode 100644 index 000000000..588dd6151 --- /dev/null +++ b/novelwriter/assets/icons/typicons_dark/nw_font.svg @@ -0,0 +1,4 @@ + + + + diff --git a/novelwriter/assets/icons/typicons_dark/nw_quote.svg b/novelwriter/assets/icons/typicons_dark/nw_quote.svg new file mode 100644 index 000000000..db50fcb4f --- /dev/null +++ b/novelwriter/assets/icons/typicons_dark/nw_quote.svg @@ -0,0 +1,4 @@ + + + + diff --git a/novelwriter/assets/icons/typicons_light/icons.conf b/novelwriter/assets/icons/typicons_light/icons.conf index 1af2ee60d..022f417ea 100644 --- a/novelwriter/assets/icons/typicons_light/icons.conf +++ b/novelwriter/assets/icons/typicons_light/icons.conf @@ -59,6 +59,7 @@ fmt_strike-md = nw_tb-strike-md.svg fmt_subscript = nw_tb-subscript.svg fmt_superscript = nw_tb-superscript.svg fmt_underline = nw_tb-underline.svg +font = nw_font.svg forward = typ_chevron-right.svg import = mixed_import.svg list = typ_th-list.svg @@ -78,6 +79,7 @@ proj_scene = mixed_document-scene.svg proj_section = mixed_document-section.svg proj_stats = typ_chart-bar-grey.svg proj_title = mixed_document-title.svg +quote = nw_quote.svg refresh = typ_refresh.svg remove = typ_minus.svg revert = typ_refresh-flipped.svg diff --git a/novelwriter/assets/icons/typicons_light/nw_font.svg b/novelwriter/assets/icons/typicons_light/nw_font.svg new file mode 100644 index 000000000..987f2fc6e --- /dev/null +++ b/novelwriter/assets/icons/typicons_light/nw_font.svg @@ -0,0 +1,4 @@ + + + + diff --git a/novelwriter/assets/icons/typicons_light/nw_quote.svg b/novelwriter/assets/icons/typicons_light/nw_quote.svg new file mode 100644 index 000000000..bdf76e817 --- /dev/null +++ b/novelwriter/assets/icons/typicons_light/nw_quote.svg @@ -0,0 +1,4 @@ + + + + diff --git a/novelwriter/common.py b/novelwriter/common.py index 70c4ce273..0ef075419 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -405,7 +405,9 @@ def describeFont(font: QFont) -> str: """Describe a font in a way that can be displayed on the GUI.""" if isinstance(font, QFont): info = QFontInfo(font) - return f"{font.family()} {info.styleName()} @ {font.pointSize()} pt" + family = info.family() + styles = [v for v in info.styleName().split() if v not in family] + return " ".join([f"{info.pointSize()} pt", family] + styles) return "Error" diff --git a/novelwriter/config.py b/novelwriter/config.py index 2bff5d2bc..148e75d48 100644 --- a/novelwriter/config.py +++ b/novelwriter/config.py @@ -114,6 +114,7 @@ def __init__(self) -> None: self.hideVScroll = False # Hide vertical scroll bars on main widgets self.hideHScroll = False # Hide horizontal scroll bars on main widgets self.lastNotes = "0x0" # The latest release notes that have been shown + self.nativeFont = True # Use native font dialog # Size Settings self._mainWinSize = [1200, 650] # Last size of the main GUI window @@ -598,6 +599,7 @@ def loadConfig(self) -> bool: self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll) self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll) self.lastNotes = conf.rdStr(sec, "lastnotes", self.lastNotes) + self.nativeFont = conf.rdBool(sec, "nativefont", self.nativeFont) self._lastPath = conf.rdPath(sec, "lastpath", self._lastPath) # Sizes @@ -707,6 +709,7 @@ def saveConfig(self) -> bool: "hidevscroll": str(self.hideVScroll), "hidehscroll": str(self.hideHScroll), "lastnotes": str(self.lastNotes), + "nativefont": str(self.nativeFont), "lastpath": str(self._lastPath), } diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index f7392e26b..aa295b6b2 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -76,12 +76,12 @@ "text.includeBodyText": (bool, True), "text.ignoredKeywords": (str, ""), "text.addNoteHeadings": (bool, True), - "format.textFont": (str, CONFIG.textFont.family()), - "format.textSize": (int, 12), + "format.textFont": (str, CONFIG.textFont.toString()), "format.lineHeight": (float, 1.15, 0.75, 3.0), "format.justifyText": (bool, False), "format.stripUnicode": (bool, False), "format.replaceTabs": (bool, False), + "format.keepBreaks": (bool, True), "format.firstLineIndent": (bool, False), "format.firstIndentWidth": (float, 1.4), "format.indentFirstPar": (bool, False), @@ -96,7 +96,6 @@ "odt.addColours": (bool, True), "odt.pageHeader": (str, nwHeadFmt.ODT_AUTO), "odt.pageCountOffset": (int, 0), - "md.preserveBreaks": (bool, True), "html.addStyles": (bool, True), "html.preserveTabs": (bool, False), } @@ -125,13 +124,13 @@ "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.textFont": QT_TRANSLATE_NOOP("Builds", "Text Font"), "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.keepBreaks": QT_TRANSLATE_NOOP("Builds", "Preserve Hard Line Breaks"), "format.grpParIndent": QT_TRANSLATE_NOOP("Builds", "First Line Indent"), "format.firstLineIndent": QT_TRANSLATE_NOOP("Builds", "Enable Indent"), @@ -153,9 +152,6 @@ "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 c65ac0be0..1b5350a89 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -28,7 +28,7 @@ from collections.abc import Iterable from pathlib import Path -from PyQt5.QtGui import QFont, QFontInfo +from PyQt5.QtGui import QFont from novelwriter import CONFIG from novelwriter.constants import nwLabels @@ -216,16 +216,10 @@ def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int, makeObj = ToMarkdown(self._project) filtered = self._setupBuild(makeObj) - if extendedMd: - makeObj.setExtendedMarkdown() - else: - makeObj.setStandardMarkdown() - + makeObj.setExtendedMarkdown(extendedMd) if self._build.getBool("format.replaceTabs"): makeObj.replaceTabs(nSpaces=4, spaceChar=" ") - makeObj.setPreserveBreaks(self._build.getBool("md.preserveBreaks")) - for i, tHandle in enumerate(self._queue): self._error = None if filtered.get(tHandle, (False, 0))[0]: @@ -285,13 +279,9 @@ def iterBuildNWD(self, path: Path | None, asJson: bool = False) -> Iterable[tupl def _setupBuild(self, bldObj: Tokenizer) -> dict: """Configure the build object.""" # Get Settings - textFont = self._build.getStr("format.textFont") - textSize = self._build.getInt("format.textSize") - - fontFamily = textFont or CONFIG.textFont.family() - bldFont = QFont(fontFamily, textSize) - fontInfo = QFontInfo(bldFont) - textFixed = fontInfo.fixedPitch() + textFont = QFont(CONFIG.textFont) + textFont.fromString(self._build.getStr("format.textFont")) + bldObj.setFont(textFont) bldObj.setTitleFormat( self._build.getStr("headings.fmtTitle"), @@ -330,9 +320,9 @@ def _setupBuild(self, bldObj: Tokenizer) -> dict: self._build.getBool("headings.breakScene") ) - bldObj.setFont(fontFamily, textSize, textFixed) bldObj.setJustify(self._build.getBool("format.justifyText")) bldObj.setLineHeight(self._build.getFloat("format.lineHeight")) + bldObj.setKeepLineBreaks(self._build.getBool("format.keepBreaks")) bldObj.setFirstLineIndent( self._build.getBool("format.firstLineIndent"), self._build.getFloat("format.firstIndentWidth"), diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index 85a91f594..f16cc515c 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -34,6 +34,7 @@ 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.types import FONT_STYLE, FONT_WEIGHTS logger = logging.getLogger(__name__) @@ -373,8 +374,16 @@ def getStyleSheet(self) -> list[str]: mScale = self._lineHeight/1.15 styles = [] - styles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format( - self._textFont, self._textSize + font = self._textFont + styles.append(( + "body {{" + "font-family: '{0:s}'; font-size: {1:d}pt; " + "font-weight: {2:d}; font-style: {3:s};" + "}}" + ).format( + font.family(), font.pointSize(), + FONT_WEIGHTS.get(font.weight(), 400), + FONT_STYLE.get(font.style(), "normal"), )) styles.append(( "p {{" diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index 448cb2ed5..72e684497 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -34,6 +34,7 @@ from time import time from PyQt5.QtCore import QCoreApplication, QRegularExpression +from PyQt5.QtGui import QFont from novelwriter.common import checkInt, formatTimeStamp, numberToRoman from novelwriter.constants import ( @@ -139,9 +140,7 @@ def __init__(self, project: NWProject) -> None: self._markdown: list[str] = [] # User Settings - self._textFont = "Serif" # Output text font - self._textSize = 11 # Output text size - self._textFixed = False # Fixed width text + self._textFont = QFont("Serif", 11) # Output text font self._lineHeight = 1.15 # Line height in units of em self._blockIndent = 4.00 # Block indent in units of em self._firstIndent = False # Enable first line indent @@ -315,11 +314,9 @@ def setSceneStyle(self, center: bool, pageBreak: bool) -> None: ) return - def setFont(self, family: str, size: int, isFixed: bool = False) -> None: + def setFont(self, font: QFont) -> None: """Set the build font.""" - self._textFont = family - self._textSize = round(int(size)) - self._textFixed = isFixed + self._textFont = font return def setLineHeight(self, height: float) -> None: diff --git a/novelwriter/core/tomarkdown.py b/novelwriter/core/tomarkdown.py index 440da0e06..eccf2300e 100644 --- a/novelwriter/core/tomarkdown.py +++ b/novelwriter/core/tomarkdown.py @@ -81,15 +81,11 @@ class ToMarkdown(Tokenizer): supports concatenating novelWriter markup files. """ - M_STD = 0 # Standard Markdown - M_EXT = 1 # Extended Markdown - def __init__(self, project: NWProject) -> None: super().__init__(project) - self._genMode = self.M_STD self._fullMD: list[str] = [] - self._preserveBreaks = True self._usedNotes: dict[str, int] = {} + self._extended = True return ## @@ -105,19 +101,9 @@ def fullMD(self) -> list[str]: # Setters ## - def setStandardMarkdown(self) -> None: - """Set the converter to use standard Markdown formatting.""" - self._genMode = self.M_STD - return - - def setExtendedMarkdown(self) -> None: + def setExtendedMarkdown(self, state: bool) -> None: """Set the converter to use Extended Markdown formatting.""" - self._genMode = self.M_EXT - return - - def setPreserveBreaks(self, state: bool) -> None: - """Preserve line breaks in paragraphs.""" - self._preserveBreaks = state + self._extended = state return ## @@ -132,12 +118,12 @@ def doConvert(self) -> None: """Convert the list of text tokens into a Markdown document.""" self._result = "" - if self._genMode == self.M_STD: - mTags = STD_MD - cSkip = "" - else: + if self._extended: mTags = EXT_MD cSkip = nwUnicode.U_MMSP + else: + mTags = STD_MD + cSkip = "" lines = [] for tType, _, tText, tFormat, tStyle in self._tokens: @@ -195,7 +181,7 @@ def doConvert(self) -> None: def appendFootnotes(self) -> None: """Append the footnotes in the buffer.""" if self._usedNotes: - tags = STD_MD if self._genMode == self.M_STD else EXT_MD + tags = EXT_MD if self._extended else STD_MD footnotes = self._localLookup("Footnotes") lines = [] diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index cd81d100e..9c631345b 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -35,11 +35,14 @@ from pathlib import Path from zipfile import ZipFile +from PyQt5.QtGui import QFont + from novelwriter import __version__ from novelwriter.common import xmlIndent from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels from novelwriter.core.project import NWProject from novelwriter.core.tokenizer import T_Formats, Tokenizer, stripEscape +from novelwriter.types import FONT_STYLE, FONT_WEIGHTS logger = logging.getLogger(__name__) @@ -108,6 +111,10 @@ def _mkTag(ns: str, tag: str) -> str: S_META = "Text_20_Meta" S_HNF = "Header_20_and_20_Footer" +# Font Data +FONT_WEIGHT_NUM = ["100", "200", "300", "400", "500", "600", "700", "800", "900"] +FONT_WEIGHT_MAP = {"400": "normal", "700": "bold"} + class ToOdt(Tokenizer): """Core: Open Document Writer @@ -149,16 +156,18 @@ def __init__(self, project: NWProject, isFlat: bool) -> None: self._errData = [] # List of errors encountered # Properties - self._textFont = "Liberation Serif" - self._textSize = 12 - self._textFixed = False + self._textFont = QFont("Liberation Serif", 12) self._colourHead = False self._headerFormat = "" self._pageOffset = 0 # Internal - self._fontFamily = "'Liberation Serif'" + self._fontFamily = "Liberation Serif" + self._fontSize = 12 + self._fontWeight = "normal" + self._fontStyle = "normal" self._fontPitch = "variable" + self._fontBold = "bold" self._fSizeTitle = "30pt" self._fSizeHead1 = "24pt" self._fSizeHead2 = "20pt" @@ -260,19 +269,25 @@ def initDocument(self) -> None: # Initialise Variables # ==================== - self._fontFamily = self._textFont - if len(self._textFont.split()) > 1: - self._fontFamily = f"'{self._textFont}'" - self._fontPitch = "fixed" if self._textFixed else "variable" - - self._fSizeTitle = f"{round(2.50 * self._textSize):d}pt" - self._fSizeHead1 = f"{round(2.00 * self._textSize):d}pt" - self._fSizeHead2 = f"{round(1.60 * self._textSize):d}pt" - self._fSizeHead3 = f"{round(1.30 * self._textSize):d}pt" - self._fSizeHead4 = f"{round(1.15 * self._textSize):d}pt" - self._fSizeHead = f"{round(1.15 * self._textSize):d}pt" - self._fSizeText = f"{self._textSize:d}pt" - self._fSizeFoot = f"{round(0.8*self._textSize):d}pt" + intWeight = FONT_WEIGHTS.get(self._textFont.weight(), 400) + fontWeight = str(intWeight) + fontBold = str(min(intWeight + 300, 900)) + + self._fontFamily = self._textFont.family() + self._fontSize = self._textFont.pointSize() + self._fontWeight = FONT_WEIGHT_MAP.get(fontWeight, fontWeight) + self._fontStyle = FONT_STYLE.get(self._textFont.style(), "normal") + self._fontPitch = "fixed" if self._textFont.fixedPitch() else "variable" + self._fontBold = FONT_WEIGHT_MAP.get(fontBold, fontBold) + + self._fSizeTitle = f"{round(2.50 * self._fontSize):d}pt" + self._fSizeHead1 = f"{round(2.00 * self._fontSize):d}pt" + self._fSizeHead2 = f"{round(1.60 * self._fontSize):d}pt" + self._fSizeHead3 = f"{round(1.30 * self._fontSize):d}pt" + self._fSizeHead4 = f"{round(1.15 * self._fontSize):d}pt" + self._fSizeHead = f"{round(1.15 * self._fontSize):d}pt" + self._fSizeText = f"{self._fontSize:d}pt" + self._fSizeFoot = f"{round(0.8*self._fontSize):d}pt" mScale = self._lineHeight/1.15 @@ -320,7 +335,7 @@ def initDocument(self) -> None: tAttr[_mkTag("office", "version")] = X_VERS fAttr = {} - fAttr[_mkTag("style", "name")] = self._textFont + fAttr[_mkTag("style", "name")] = self._fontFamily fAttr[_mkTag("style", "font-pitch")] = self._fontPitch if self._isFlat: @@ -726,7 +741,7 @@ def _textStyle(self, hFmt: int) -> str: style = ODTTextStyle(f"T{len(self._autoText)+1:d}") if hFmt & X_BLD: - style.setFontWeight("bold") + style.setFontWeight(self._fontBold) if hFmt & X_ITA: style.setFontStyle("italic") if hFmt & X_DEL: @@ -764,7 +779,7 @@ def _generateFootnote(self, key: str) -> ET.Element | None: def _emToCm(self, value: float) -> str: """Converts an em value to centimetres.""" - return f"{value*2.54/72*self._textSize:.3f}cm" + return f"{value*2.54/72*self._fontSize:.3f}cm" ## # Style Elements @@ -808,8 +823,10 @@ def _defaultStyles(self) -> None: _mkTag("style", "writing-mode"): "page", }) ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib={ - _mkTag("style", "font-name"): self._textFont, + _mkTag("style", "font-name"): self._fontFamily, _mkTag("fo", "font-family"): self._fontFamily, + _mkTag("fo", "font-weight"): self._fontWeight, + _mkTag("fo", "font-style"): self._fontStyle, _mkTag("fo", "font-size"): self._fSizeText, _mkTag("fo", "language"): self._dLanguage, _mkTag("fo", "country"): self._dCountry, @@ -822,8 +839,10 @@ def _defaultStyles(self) -> None: _mkTag("style", "class"): "text", }) ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib={ - _mkTag("style", "font-name"): self._textFont, + _mkTag("style", "font-name"): self._fontFamily, _mkTag("fo", "font-family"): self._fontFamily, + _mkTag("fo", "font-weight"): self._fontWeight, + _mkTag("fo", "font-style"): self._fontStyle, _mkTag("fo", "font-size"): self._fSizeText, }) @@ -841,8 +860,10 @@ def _defaultStyles(self) -> None: _mkTag("fo", "keep-with-next"): "always", }) ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib={ - _mkTag("style", "font-name"): self._textFont, + _mkTag("style", "font-name"): self._fontFamily, _mkTag("fo", "font-family"): self._fontFamily, + _mkTag("fo", "font-weight"): self._fontWeight, + _mkTag("fo", "font-style"): self._fontStyle, _mkTag("fo", "font-size"): self._fSizeHead, }) @@ -868,9 +889,10 @@ def _useableStyles(self) -> None: style.setMarginBottom(self._mBotText) style.setLineHeight(self._fLineHeight) style.setTextAlign(self._textAlign) - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeText) + style.setFontWeight(self._fontWeight) style.packXML(self._xStyl) self._mainPara[style.name] = style @@ -891,9 +913,10 @@ def _useableStyles(self) -> None: style.setMarginTop(self._mTopMeta) style.setMarginBottom(self._mBotMeta) style.setLineHeight(self._fLineHeight) - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeText) + style.setFontWeight(self._fontWeight) style.setColour(self._colMetaTx) style.setOpacity(self._opaMetaTx) style.packXML(self._xStyl) @@ -908,10 +931,10 @@ def _useableStyles(self) -> None: style.setMarginTop(self._mTopTitle) style.setMarginBottom(self._mBotTitle) style.setTextAlign("center") - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeTitle) - style.setFontWeight("bold") + style.setFontWeight(self._fontBold) style.packXML(self._xStyl) self._mainPara[style.name] = style @@ -925,9 +948,10 @@ def _useableStyles(self) -> None: style.setMarginBottom(self._mBotText) style.setLineHeight(self._fLineHeight) style.setTextAlign("center") - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeText) + style.setFontWeight(self._fontWeight) style.packXML(self._xStyl) self._mainPara[style.name] = style @@ -940,10 +964,10 @@ def _useableStyles(self) -> None: style.setClass("text") style.setMarginTop(self._mTopHead1) style.setMarginBottom(self._mBotHead1) - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeHead1) - style.setFontWeight("bold") + style.setFontWeight(self._fontBold) style.setColour(self._colHead12) style.setOpacity(self._opaHead12) style.packXML(self._xStyl) @@ -958,10 +982,10 @@ def _useableStyles(self) -> None: style.setClass("text") style.setMarginTop(self._mTopHead2) style.setMarginBottom(self._mBotHead2) - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeHead2) - style.setFontWeight("bold") + style.setFontWeight(self._fontBold) style.setColour(self._colHead12) style.setOpacity(self._opaHead12) style.packXML(self._xStyl) @@ -976,10 +1000,10 @@ def _useableStyles(self) -> None: style.setClass("text") style.setMarginTop(self._mTopHead3) style.setMarginBottom(self._mBotHead3) - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeHead3) - style.setFontWeight("bold") + style.setFontWeight(self._fontBold) style.setColour(self._colHead34) style.setOpacity(self._opaHead34) style.packXML(self._xStyl) @@ -994,10 +1018,10 @@ def _useableStyles(self) -> None: style.setClass("text") style.setMarginTop(self._mTopHead4) style.setMarginBottom(self._mBotHead4) - style.setFontName(self._textFont) + style.setFontName(self._fontFamily) style.setFontFamily(self._fontFamily) style.setFontSize(self._fSizeHead4) - style.setFontWeight("bold") + style.setFontWeight(self._fontBold) style.setColour(self._colHead34) style.setOpacity(self._opaHead34) style.packXML(self._xStyl) @@ -1077,7 +1101,7 @@ class ODTParagraphStyle: VALID_BREAK = ["auto", "column", "page", "even-page", "odd-page", "inherit"] VALID_LEVEL = ["1", "2", "3", "4"] VALID_CLASS = ["text", "chapter", "extra"] - VALID_WEIGHT = ["normal", "inherit", "bold"] + VALID_WEIGHT = ["normal", "bold"] + FONT_WEIGHT_NUM def __init__(self, name: str) -> None: @@ -1320,8 +1344,8 @@ class ODTTextStyle: Only the used settings are exposed here to keep the class minimal and fast. """ - VALID_WEIGHT = ["normal", "inherit", "bold"] - VALID_STYLE = ["normal", "inherit", "italic"] + VALID_WEIGHT = ["normal", "bold"] + FONT_WEIGHT_NUM + VALID_STYLE = ["normal", "italic", "oblique"] VALID_POS = ["super", "sub"] VALID_LSTYLE = ["none", "solid"] VALID_LTYPE = ["single", "double"] diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 3158fa24c..f1e6bb85d 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -30,8 +30,7 @@ from PyQt5.QtGui import QCloseEvent, QKeyEvent, QKeySequence from PyQt5.QtWidgets import ( QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox, - QFileDialog, QFontDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, - QWidget + QFileDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget ) from novelwriter import CONFIG, SHARED @@ -183,7 +182,7 @@ def buildForm(self) -> None: self.guiFont.setMinimumWidth(fontWidth) self.guiFont.setText(describeFont(self._guiFont)) self.guiFont.setCursorPosition(0) - self.guiFontButton = NIconToolButton(self, iSz, "more") + self.guiFontButton = NIconToolButton(self, iSz, "font") self.guiFontButton.clicked.connect(self._selectGuiFont) self.mainForm.addRow( self.tr("Application font"), self.guiFont, @@ -207,6 +206,14 @@ def buildForm(self) -> None: self.tr("Scrolling available with mouse wheel and keys only.") ) + # Native Font Dialog + self.nativeFont = NSwitch(self) + self.nativeFont.setChecked(CONFIG.nativeFont) + self.mainForm.addRow( + self.tr("Use the system's font selection dialog"), self.nativeFont, + self.tr("Turn off to use the Qt font dialog, which may have more options.") + ) + # Document Style # ============== @@ -233,7 +240,7 @@ def buildForm(self) -> None: self.textFont.setMinimumWidth(fontWidth) self.textFont.setText(describeFont(CONFIG.textFont)) self.textFont.setCursorPosition(0) - self.textFontButton = NIconToolButton(self, iSz, "more") + self.textFontButton = NIconToolButton(self, iSz, "font") self.textFontButton.clicked.connect(self._selectTextFont) self.mainForm.addRow( self.tr("Document font"), self.textFont, @@ -695,7 +702,7 @@ def buildForm(self) -> None: self.quoteSym["SO"].setFixedWidth(boxFixed) self.quoteSym["SO"].setAlignment(QtAlignCenter) self.quoteSym["SO"].setText(CONFIG.fmtSQuoteOpen) - self.btnSingleStyleO = NIconToolButton(self, iSz, "more") + self.btnSingleStyleO = NIconToolButton(self, iSz, "quote") self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO")) self.mainForm.addRow( self.tr("Single quote open style"), self.quoteSym["SO"], @@ -709,7 +716,7 @@ def buildForm(self) -> None: self.quoteSym["SC"].setFixedWidth(boxFixed) self.quoteSym["SC"].setAlignment(QtAlignCenter) self.quoteSym["SC"].setText(CONFIG.fmtSQuoteClose) - self.btnSingleStyleC = NIconToolButton(self, iSz, "more") + self.btnSingleStyleC = NIconToolButton(self, iSz, "quote") self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC")) self.mainForm.addRow( self.tr("Single quote close style"), self.quoteSym["SC"], @@ -724,7 +731,7 @@ def buildForm(self) -> None: self.quoteSym["DO"].setFixedWidth(boxFixed) self.quoteSym["DO"].setAlignment(QtAlignCenter) self.quoteSym["DO"].setText(CONFIG.fmtDQuoteOpen) - self.btnDoubleStyleO = NIconToolButton(self, iSz, "more") + self.btnDoubleStyleO = NIconToolButton(self, iSz, "quote") self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO")) self.mainForm.addRow( self.tr("Double quote open style"), self.quoteSym["DO"], @@ -738,7 +745,7 @@ def buildForm(self) -> None: self.quoteSym["DC"].setFixedWidth(boxFixed) self.quoteSym["DC"].setAlignment(QtAlignCenter) self.quoteSym["DC"].setText(CONFIG.fmtDQuoteClose) - self.btnDoubleStyleC = NIconToolButton(self, iSz, "more") + self.btnDoubleStyleC = NIconToolButton(self, iSz, "quote") self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC")) self.mainForm.addRow( self.tr("Double quote close style"), self.quoteSym["DC"], @@ -804,7 +811,7 @@ def _gotoSearch(self) -> None: @pyqtSlot() def _selectGuiFont(self) -> None: """Open the QFontDialog and set a font for the font style.""" - font, status = QFontDialog.getFont(self._guiFont, self) + font, status = SHARED.getFont(self._guiFont, self.nativeFont.isChecked()) if status: self.guiFont.setText(describeFont(font)) self.guiFont.setCursorPosition(0) @@ -814,7 +821,7 @@ def _selectGuiFont(self) -> None: @pyqtSlot() def _selectTextFont(self) -> None: """Open the QFontDialog and set a font for the font style.""" - font, status = QFontDialog.getFont(CONFIG.textFont, self) + font, status = SHARED.getFont(self._textFont, self.nativeFont.isChecked()) if status: self.textFont.setText(describeFont(font)) self.textFont.setCursorPosition(0) @@ -883,6 +890,7 @@ def _saveValues(self) -> None: CONFIG.guiTheme = guiTheme CONFIG.hideVScroll = self.hideVScroll.isChecked() CONFIG.hideHScroll = self.hideHScroll.isChecked() + CONFIG.nativeFont = self.nativeFont.isChecked() CONFIG.setGuiFont(self._guiFont) # Document Style diff --git a/novelwriter/dialogs/quotes.py b/novelwriter/dialogs/quotes.py index 12bc0b0b5..760e82d64 100644 --- a/novelwriter/dialogs/quotes.py +++ b/novelwriter/dialogs/quotes.py @@ -50,6 +50,7 @@ def __init__(self, parent: QWidget, current: str = '"') -> None: logger.debug("Create: GuiQuoteSelect") self.setObjectName("GuiQuoteSelect") + self.setWindowTitle(self.tr("Select Quote Style")) self.outerBox = QVBoxLayout() self.innerBox = QHBoxLayout() diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index a986f86aa..72a951783 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -501,9 +501,9 @@ class GuiIcons: # General Button Icons "add", "add_document", "backward", "bookmark", "browse", "checked", "close", "cross", - "document", "down", "edit", "export", "forward", "import", "list", "maximise", "menu", - "minimise", "more", "noncheckable", "open", "panel", "refresh", "remove", "revert", - "search_replace", "search", "settings", "star", "unchecked", "up", "view", + "document", "down", "edit", "export", "font", "forward", "import", "list", "maximise", + "menu", "minimise", "more", "noncheckable", "open", "panel", "quote", "refresh", "remove", + "revert", "search_replace", "search", "settings", "star", "unchecked", "up", "view", # Switches "sticky-on", "sticky-off", diff --git a/novelwriter/shared.py b/novelwriter/shared.py index e703eb2cf..d6c19a9b5 100644 --- a/novelwriter/shared.py +++ b/novelwriter/shared.py @@ -31,7 +31,8 @@ from typing import TYPE_CHECKING, TypeVar from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal -from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget +from PyQt5.QtGui import QFont +from PyQt5.QtWidgets import QFileDialog, QFontDialog, QMessageBox, QWidget from novelwriter.common import formatFileFilter from novelwriter.constants import nwFiles @@ -255,8 +256,11 @@ def runInThreadPool(self, runnable: QRunnable, priority: int = 0) -> None: QThreadPool.globalInstance().start(runnable, priority=priority) return - def getProjectPath(self, parent: QWidget, path: str | Path | None = None, - allowZip: bool = False) -> Path | None: + def getProjectPath( + self, parent: QWidget, + path: str | Path | None = None, + allowZip: bool = False + ) -> Path | None: """Open the file dialog and select a novelWriter project file.""" label = (self.tr("novelWriter Project File or Zip File") if allowZip else self.tr("novelWriter Project File")) @@ -267,6 +271,13 @@ def getProjectPath(self, parent: QWidget, path: str | Path | None = None, ) return Path(selected) if selected else None + def getFont(self, current: QFont, native: bool) -> tuple[QFont, bool]: + """Open the font dialog and select a font.""" + kwargs = {} + if not native: + kwargs["options"] = QFontDialog.FontDialogOption.DontUseNativeDialog + return QFontDialog.getFont(current, self.mainGui, self.tr("Select Font"), **kwargs) + def findTopLevelWidget(self, kind: type[NWWidget]) -> NWWidget | None: """Find a top level widget.""" for widget in self.mainGui.children(): diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index ca19e6d08..7e27b8679 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -31,7 +31,7 @@ from typing import TYPE_CHECKING from PyQt5.QtCore import Qt, QTimer, QUrl, pyqtSignal, pyqtSlot -from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QPalette, QResizeEvent +from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QFont, QPalette, QResizeEvent from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog from PyQt5.QtWidgets import ( QAbstractItemView, QApplication, QFormLayout, QGridLayout, QHBoxLayout, @@ -402,12 +402,12 @@ def _printDocument(self) -> None: def _updatePreview(self, data: dict, build: BuildSettings) -> None: """Update the preview widget and set relevant values.""" + textFont = QFont() + textFont.fromString(build.getStr("format.textFont")) + self.docPreview.setContent(data) self.docPreview.setBuildName(build.name) - self.docPreview.setTextFont( - build.getStr("format.textFont"), - build.getInt("format.textSize") - ) + self.docPreview.setTextFont(textFont) self.docPreview.setJustify( build.getBool("format.justifyText") ) @@ -787,7 +787,7 @@ def __init__(self, parent: QWidget) -> None: self._updateDocMargins() self._updateBuildAge() - self.setTextFont(CONFIG.textFont.family(), CONFIG.textFont.pointSize()) + self.setTextFont(CONFIG.textFont) # Age Timer self.ageTimer = QTimer(self) @@ -817,18 +817,14 @@ def setJustify(self, state: bool) -> None: self.document().setDefaultTextOption(pOptions) return - def setTextFont(self, family: str, size: int) -> None: + def setTextFont(self, font: QFont) -> None: """Set the text font properties and then reset for sub-widgets. This needs special attention since there appears to be a bug in Qt 5.15.3. See issues #1862 and #1875. """ - if family and size > 4: - font = self.font() - font.setFamily(family) - font.setPointSize(size) - self.setFont(font) - self.buildProgress.setFont(SHARED.theme.guiFont) - self.ageLabel.setFont(SHARED.theme.guiFontSmall) + self.setFont(font) + self.buildProgress.setFont(SHARED.theme.guiFont) + self.ageLabel.setFont(SHARED.theme.guiFontSmall) return ## diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index dca739f9b..1488c3b81 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -30,13 +30,14 @@ from PyQt5.QtCore import QEvent, pyqtSignal, pyqtSlot from PyQt5.QtGui import QFont, QIcon, QSyntaxHighlighter, QTextCharFormat, QTextDocument from PyQt5.QtWidgets import ( - QAbstractButton, QAbstractItemView, QDialogButtonBox, QFontDialog, QFrame, - QGridLayout, QHBoxLayout, QHeaderView, QLabel, QLineEdit, QMenu, - QPlainTextEdit, QPushButton, QSplitter, QStackedWidget, QTreeWidget, - QTreeWidgetItem, QVBoxLayout, QWidget + QAbstractButton, QAbstractItemView, QDialogButtonBox, QFrame, QGridLayout, + QHBoxLayout, QHeaderView, QLabel, QLineEdit, QMenu, QPlainTextEdit, + QPushButton, QSplitter, QStackedWidget, QTreeWidget, QTreeWidgetItem, + QVBoxLayout, QWidget ) from novelwriter import CONFIG, SHARED +from novelwriter.common import describeFont from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, trConst from novelwriter.core.buildsettings import BuildSettings, FilterMode from novelwriter.extensions.configlayout import ( @@ -1052,6 +1053,7 @@ def __init__(self, parent: QWidget, build: BuildSettings) -> None: self._build = build self._unitScale = 1.0 + self._textFont = QFont(CONFIG.textFont) iPx = SHARED.theme.baseIconHeight iSz = SHARED.theme.baseIconSize @@ -1063,24 +1065,16 @@ def __init__(self, parent: QWidget, build: BuildSettings) -> None: self.addGroupLabel(self._build.getLabel("format.grpFormat")) - # Font Family + # Text Font self.textFont = QLineEdit(self) self.textFont.setReadOnly(True) - self.btnTextFont = NIconToolButton(self, iSz, "more") + self.btnTextFont = NIconToolButton(self, iSz, "font") self.btnTextFont.clicked.connect(self._selectFont) self.addRow( self._build.getLabel("format.textFont"), self.textFont, - button=self.btnTextFont, stretch=(3, 2) + button=self.btnTextFont, stretch=(1, 1) ) - # Font Size - self.textSize = NSpinBox(self) - self.textSize.setMinimum(8) - self.textSize.setMaximum(60) - self.textSize.setSingleStep(1) - self.textSize.setMinimumWidth(spW) - self.addRow(self._build.getLabel("format.textSize"), self.textSize, unit="pt") - # Line Height self.lineHeight = NDoubleSpinBox(self) self.lineHeight.setFixedWidth(spW) @@ -1098,10 +1092,12 @@ def __init__(self, parent: QWidget, build: BuildSettings) -> None: self.justifyText = NSwitch(self, height=iPx) self.stripUnicode = NSwitch(self, height=iPx) self.replaceTabs = NSwitch(self, height=iPx) + self.keepBreaks = NSwitch(self, height=iPx) self.addRow(self._build.getLabel("format.justifyText"), self.justifyText) self.addRow(self._build.getLabel("format.stripUnicode"), self.stripUnicode) self.addRow(self._build.getLabel("format.replaceTabs"), self.replaceTabs) + self.addRow(self._build.getLabel("format.keepBreaks"), self.keepBreaks) # First Line Indent # ================= @@ -1173,17 +1169,17 @@ def __init__(self, parent: QWidget, build: BuildSettings) -> None: def loadContent(self) -> None: """Populate the widgets.""" - textFont = self._build.getStr("format.textFont") - if not textFont: - textFont = str(CONFIG.textFont.family()) + self._textFont = QFont() + self._textFont.fromString(self._build.getStr("format.textFont")) - self.textFont.setText(textFont) - self.textSize.setValue(self._build.getInt("format.textSize")) - self.lineHeight.setValue(self._build.getFloat("format.lineHeight")) + self.textFont.setText(describeFont(self._textFont)) + self.textFont.setCursorPosition(0) + self.lineHeight.setValue(self._build.getFloat("format.lineHeight")) self.justifyText.setChecked(self._build.getBool("format.justifyText")) self.stripUnicode.setChecked(self._build.getBool("format.stripUnicode")) self.replaceTabs.setChecked(self._build.getBool("format.replaceTabs")) + self.keepBreaks.setChecked(self._build.getBool("format.keepBreaks")) self.firstIndent.setChecked(self._build.getBool("format.firstLineIndent")) self.indentWidth.setValue(self._build.getFloat("format.firstIndentWidth")) @@ -1216,13 +1212,13 @@ def loadContent(self) -> None: def saveContent(self) -> None: """Save choices back into build object.""" - self._build.setValue("format.textFont", self.textFont.text()) - self._build.setValue("format.textSize", self.textSize.value()) + self._build.setValue("format.textFont", self._textFont.toString()) self._build.setValue("format.lineHeight", self.lineHeight.value()) self._build.setValue("format.justifyText", self.justifyText.isChecked()) self._build.setValue("format.stripUnicode", self.stripUnicode.isChecked()) self._build.setValue("format.replaceTabs", self.replaceTabs.isChecked()) + self._build.setValue("format.keepBreaks", self.keepBreaks.isChecked()) self._build.setValue("format.firstLineIndent", self.firstIndent.isChecked()) self._build.setValue("format.firstIndentWidth", self.indentWidth.value()) @@ -1245,13 +1241,11 @@ def saveContent(self) -> None: @pyqtSlot() def _selectFont(self) -> None: """Open the QFontDialog and set a font for the font style.""" - currFont = QFont() - currFont.setFamily(self.textFont.text()) - currFont.setPointSize(self.textSize.value()) - newFont, status = QFontDialog.getFont(currFont, self) + font, status = SHARED.getFont(self._textFont, CONFIG.nativeFont) if status: - self.textFont.setText(newFont.family()) - self.textSize.setValue(newFont.pointSize()) + self.textFont.setText(describeFont(font)) + self.textFont.setCursorPosition(0) + self._textFont = font return @pyqtSlot(int) @@ -1379,12 +1373,6 @@ def __init__(self, parent: QWidget, build: BuildSettings) -> None: self.htmlPreserveTabs = NSwitch(self, height=iPx) self.addRow(self._build.getLabel("html.preserveTabs"), self.htmlPreserveTabs) - # Markdown Document - self.addGroupLabel(self._build.getLabel("md")) - - self.mdPreserveBreaks = NSwitch(self, height=iPx) - self.addRow(self._build.getLabel("md.preserveBreaks"), self.mdPreserveBreaks) - # Finalise self.finalise() @@ -1397,7 +1385,6 @@ def loadContent(self) -> None: self.odtPageCountOffset.setValue(self._build.getInt("odt.pageCountOffset")) self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles")) self.htmlPreserveTabs.setChecked(self._build.getBool("html.preserveTabs")) - self.mdPreserveBreaks.setChecked(self._build.getBool("md.preserveBreaks")) self.odtPageHeader.setCursorPosition(0) return @@ -1408,7 +1395,6 @@ def saveContent(self) -> None: self._build.setValue("odt.pageCountOffset", self.odtPageCountOffset.value()) self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked()) self._build.setValue("html.preserveTabs", self.htmlPreserveTabs.isChecked()) - self._build.setValue("md.preserveBreaks", self.mdPreserveBreaks.isChecked()) return ## diff --git a/novelwriter/types.py b/novelwriter/types.py index e4051f8ef..c70d28934 100644 --- a/novelwriter/types.py +++ b/novelwriter/types.py @@ -24,7 +24,7 @@ from __future__ import annotations from PyQt5.QtCore import QRegularExpression, Qt -from PyQt5.QtGui import QColor, QPainter, QTextCursor, QTextFormat +from PyQt5.QtGui import QColor, QFont, QPainter, QTextCursor, QTextFormat from PyQt5.QtWidgets import QDialogButtonBox, QSizePolicy, QStyle # Qt Alignment Flags @@ -105,3 +105,23 @@ # Other QRegExUnicode = QRegularExpression.PatternOption.UseUnicodePropertiesOption + +# Maps + +FONT_WEIGHTS: dict[int, int] = { + QFont.Weight.Thin: 100, + QFont.Weight.ExtraLight: 200, + QFont.Weight.Light: 300, + QFont.Weight.Normal: 400, + QFont.Weight.Medium: 500, + QFont.Weight.DemiBold: 600, + QFont.Weight.Bold: 700, + QFont.Weight.ExtraBold: 800, + QFont.Weight.Black: 900, +} + +FONT_STYLE: dict[int, str] = { + QFont.Style.StyleNormal: "normal", + QFont.Style.StyleItalic: "italic", + QFont.Style.StyleOblique: "oblique", +} diff --git a/tests/reference/baseConfig_novelwriter.conf b/tests/reference/baseConfig_novelwriter.conf index 99559ed87..0554bd6dc 100644 --- a/tests/reference/baseConfig_novelwriter.conf +++ b/tests/reference/baseConfig_novelwriter.conf @@ -9,6 +9,7 @@ localisation = en_GB hidevscroll = False hidehscroll = False lastnotes = 0x0 +nativefont = True lastpath = [Sizes] diff --git a/tests/reference/coreToOdt_SaveFlat_document.fodt b/tests/reference/coreToOdt_SaveFlat_document.fodt index 78c9ab59a..cee2d9b59 100644 --- a/tests/reference/coreToOdt_SaveFlat_document.fodt +++ b/tests/reference/coreToOdt_SaveFlat_document.fodt @@ -1,13 +1,13 @@ - 2024-04-24T18:30:38 - novelWriter/2.5a2 + 2024-05-22T23:05:27 + novelWriter/2.5a4 Jane Smith 1234 P42DT12H34M56S Test Project - 2024-04-24T18:30:38 + 2024-05-22T23:05:27 Jane Smith @@ -16,50 +16,50 @@ - + - + - + - + - + - + - + - + - + - + - + diff --git a/tests/reference/coreToOdt_SaveFull_styles.xml b/tests/reference/coreToOdt_SaveFull_styles.xml index 03395db14..809732e0e 100644 --- a/tests/reference/coreToOdt_SaveFull_styles.xml +++ b/tests/reference/coreToOdt_SaveFull_styles.xml @@ -6,50 +6,50 @@ - + - + - + - + - + - + - + - + - + - + - + diff --git a/tests/reference/mBuildDocBuild_HTML5_Lorem_Ipsum.htm b/tests/reference/mBuildDocBuild_HTML5_Lorem_Ipsum.htm index 60d3800bc..e3e8da375 100644 --- a/tests/reference/mBuildDocBuild_HTML5_Lorem_Ipsum.htm +++ b/tests/reference/mBuildDocBuild_HTML5_Lorem_Ipsum.htm @@ -5,7 +5,7 @@ Lorem Ipsum