Merge branch 'main' into feature/preview_generator

This commit is contained in:
Veronica Berglyd Olsen
2024-05-23 18:50:38 +02:00
36 changed files with 317 additions and 262 deletions
@@ -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
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
<path d="m2.5744 5.8208v-4.2853h-1.4527v-1.0064h4.1067v1.0064h-1.4527v4.2853z" fill="#aeaeae" stroke-width="1.1503"/>
</svg>

After

Width:  |  Height:  |  Size: 267 B

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
<path d="m1.3715 3.0659q-0.32092-0.25674-0.44928-0.44928-0.12836-0.19255-0.12837-0.44928 0-0.42361 0.28241-0.69319 0.28241-0.28241 0.68035-0.28241 0.25674 0 0.44928 0.10269 0.19255 0.089858 0.3466 0.23106 0.24391 0.4878 0.24391 1.0141 0 0.42361-0.14121 0.82157-0.12837 0.38511-0.37227 0.71886-0.23106 0.33377-0.55199 0.61618-0.30808 0.26958-0.65467 0.46213l-0.25674-0.41078q0.29525-0.21822 0.57766-0.53914 0.28241-0.33375 0.46213-0.73171zm2.7598 0q-0.32092-0.25674-0.44928-0.44928-0.12837-0.19255-0.12837-0.44928 0-0.42361 0.28241-0.69319 0.28241-0.28241 0.68035-0.28241 0.25674 0 0.44928 0.10269 0.19255 0.089858 0.3466 0.23106 0.24391 0.4878 0.24391 1.0141 0 0.42361-0.14121 0.82157-0.12837 0.38511-0.37227 0.71886-0.23106 0.33377-0.55199 0.61618-0.30808 0.26958-0.65467 0.46213l-0.25674-0.41078q0.29525-0.21822 0.57766-0.53914 0.28241-0.33375 0.46213-0.73171z" fill="#aeaeae" fill-opacity=".78039" stroke-width="1.8194"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -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
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
<path d="m2.5744 5.8208v-4.2853h-1.4527v-1.0064h4.1067v1.0064h-1.4527v4.2853z" fill-opacity=".78039" stroke-width="1.1503"/>
</svg>

After

Width:  |  Height:  |  Size: 274 B

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
<path d="m1.3715 3.0659q-0.32092-0.25674-0.44928-0.44928-0.12836-0.19255-0.12837-0.44928 0-0.42361 0.28241-0.69319 0.28241-0.28241 0.68035-0.28241 0.25674 0 0.44928 0.10269 0.19255 0.089858 0.3466 0.23106 0.24391 0.4878 0.24391 1.0141 0 0.42361-0.14121 0.82157-0.12837 0.38511-0.37227 0.71886-0.23106 0.33377-0.55199 0.61618-0.30808 0.26958-0.65467 0.46213l-0.25674-0.41078q0.29525-0.21822 0.57766-0.53914 0.28241-0.33375 0.46213-0.73171zm2.7598 0q-0.32092-0.25674-0.44928-0.44928-0.12837-0.19255-0.12837-0.44928 0-0.42361 0.28241-0.69319 0.28241-0.28241 0.68035-0.28241 0.25674 0 0.44928 0.10269 0.19255 0.089858 0.3466 0.23106 0.24391 0.4878 0.24391 1.0141 0 0.42361-0.14121 0.82157-0.12837 0.38511-0.37227 0.71886-0.23106 0.33377-0.55199 0.61618-0.30808 0.26958-0.65467 0.46213l-0.25674-0.41078q0.29525-0.21822 0.57766-0.53914 0.28241-0.33375 0.46213-0.73171z" fill-opacity=".78039" stroke-width="1.8194"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+3 -1
View File
@@ -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"
+3
View File
@@ -114,6 +114,7 @@ class Config:
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 @@ class Config:
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 @@ class Config:
"hidevscroll": str(self.hideVScroll),
"hidehscroll": str(self.hideHScroll),
"lastnotes": str(self.lastNotes),
"nativefont": str(self.nativeFont),
"lastpath": str(self._lastPath),
}
+4 -8
View File
@@ -76,12 +76,12 @@ SETTINGS_TEMPLATE = {
"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 @@ SETTINGS_TEMPLATE = {
"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 @@ SETTINGS_LABELS = {
"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 @@ SETTINGS_LABELS = {
"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"),
+6 -16
View File
@@ -28,7 +28,7 @@ import logging
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 @@ class NWBuildDocument:
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 @@ class NWBuildDocument:
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 @@ class NWBuildDocument:
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"),
+11 -2
View File
@@ -34,6 +34,7 @@ from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels
from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import T_Formats, Tokenizer, stripEscape
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS
logger = logging.getLogger(__name__)
@@ -373,8 +374,16 @@ class ToHtml(Tokenizer):
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 {{"
+4 -7
View File
@@ -34,6 +34,7 @@ from pathlib import Path
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 @@ class Tokenizer(ABC):
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 @@ class Tokenizer(ABC):
)
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:
+8 -22
View File
@@ -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 @@ class ToMarkdown(Tokenizer):
# 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 @@ class ToMarkdown(Tokenizer):
"""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 @@ class ToMarkdown(Tokenizer):
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 = []
+62 -38
View File
@@ -35,11 +35,14 @@ from hashlib import sha256
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 @@ S_TEXT = "Text_20_body"
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 @@ class ToOdt(Tokenizer):
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 = "&apos;Liberation Serif&apos;"
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 @@ class ToOdt(Tokenizer):
# 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"
intWeight = FONT_WEIGHTS.get(self._textFont.weight(), 400)
fontWeight = str(intWeight)
fontBold = str(min(intWeight + 300, 900))
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"
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
_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 @@ class ToOdt(Tokenizer):
_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 @@ class ToOdt(Tokenizer):
_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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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 @@ class ToOdt(Tokenizer):
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"]
+18 -10
View File
@@ -30,8 +30,7 @@ from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
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 @@ class GuiPreferences(QDialog):
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 @@ class GuiPreferences(QDialog):
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 @@ class GuiPreferences(QDialog):
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 @@ class GuiPreferences(QDialog):
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 @@ class GuiPreferences(QDialog):
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 @@ class GuiPreferences(QDialog):
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 @@ class GuiPreferences(QDialog):
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 @@ class GuiPreferences(QDialog):
@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 @@ class GuiPreferences(QDialog):
@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 @@ class GuiPreferences(QDialog):
CONFIG.guiTheme = guiTheme
CONFIG.hideVScroll = self.hideVScroll.isChecked()
CONFIG.hideHScroll = self.hideHScroll.isChecked()
CONFIG.nativeFont = self.nativeFont.isChecked()
CONFIG.setGuiFont(self._guiFont)
# Document Style
+1
View File
@@ -50,6 +50,7 @@ class GuiQuoteSelect(QDialog):
logger.debug("Create: GuiQuoteSelect")
self.setObjectName("GuiQuoteSelect")
self.setWindowTitle(self.tr("Select Quote Style"))
self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout()
+3 -3
View File
@@ -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",
+14 -3
View File
@@ -31,7 +31,8 @@ from time import time
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 @@ class SharedData(QObject):
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 @@ class SharedData(QObject):
)
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():
+10 -14
View File
@@ -31,7 +31,7 @@ from time import time
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 @@ class GuiManuscript(NToolDialog):
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 @@ class _PreviewWidget(QTextBrowser):
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 @@ class _PreviewWidget(QTextBrowser):
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
##
+23 -37
View File
@@ -30,13 +30,14 @@ from typing import TYPE_CHECKING
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 @@ class _FormatTab(NScrollableForm):
self._build = build
self._unitScale = 1.0
self._textFont = QFont(CONFIG.textFont)
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
@@ -1063,24 +1065,16 @@ class _FormatTab(NScrollableForm):
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 @@ class _FormatTab(NScrollableForm):
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 @@ class _FormatTab(NScrollableForm):
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(describeFont(self._textFont))
self.textFont.setCursorPosition(0)
self.textFont.setText(textFont)
self.textSize.setValue(self._build.getInt("format.textSize"))
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 @@ class _FormatTab(NScrollableForm):
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 @@ class _FormatTab(NScrollableForm):
@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 @@ class _OutputTab(NScrollableForm):
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 @@ class _OutputTab(NScrollableForm):
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 @@ class _OutputTab(NScrollableForm):
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
##
+21 -1
View File
@@ -24,7 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
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 @@ QtSizeMinimumExpanding = QSizePolicy.Policy.MinimumExpanding
# 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",
}