Merge release 2.6 into main

This commit is contained in:
Veronica Berglyd Olsen
2025-01-26 20:50:58 +01:00
69 changed files with 2442 additions and 1751 deletions
+12 -11
View File
@@ -104,17 +104,18 @@ class BlockTyp(IntEnum):
EMPTY = 1 # Empty line (new paragraph)
TITLE = 2 # Title
HEAD1 = 3 # Heading 1
HEAD2 = 4 # Heading 2
HEAD3 = 5 # Heading 3
HEAD4 = 6 # Heading 4
TEXT = 7 # Text line
SEP = 8 # Scene separator
SKIP = 9 # Paragraph break
SUMMARY = 10 # Synopsis/short comment
NOTE = 11 # Note
COMMENT = 12 # Comment
KEYWORD = 13 # Tag/reference keywords
PART = 3 # Partition
HEAD1 = 4 # Heading 1 or Chapter
HEAD2 = 5 # Heading 2 or Scene
HEAD3 = 6 # Heading 3 or Section
HEAD4 = 7 # Heading 4
TEXT = 8 # Text line
SEP = 9 # Scene separator
SKIP = 10 # Paragraph break
SUMMARY = 11 # Synopsis/short comment
NOTE = 12 # Note
COMMENT = 13 # Comment
KEYWORD = 14 # Tag/reference keywords
class BlockFmt(Flag):
+1 -1
View File
@@ -271,7 +271,7 @@ class ToDocX(Tokenizer):
if tType == BlockTyp.TEXT:
self._processFragments(par, S_NORM, tText, tFormat)
elif tType == BlockTyp.TITLE:
elif tType in (BlockTyp.TITLE, BlockTyp.PART):
self._processFragments(par, S_TITLE, tText, tFormat)
elif tType == BlockTyp.HEAD1:
+30 -27
View File
@@ -30,7 +30,7 @@ from pathlib import Path
from time import time
from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHtmlUnicode
from novelwriter.constants import nwHtmlUnicode, nwStyles
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
from novelwriter.formats.tokenizer import Tokenizer
@@ -130,20 +130,6 @@ class ToHtml(Tokenizer):
def doConvert(self) -> None:
"""Convert the list of text tokens into an HTML document."""
if self._isNovel:
# For story files, we bump the titles one level up
h1Cl = " class='title'"
h1 = "h1"
h2 = "h1"
h3 = "h2"
h4 = "h3"
else:
h1Cl = ""
h1 = "h1"
h2 = "h2"
h3 = "h3"
h4 = "h4"
lines = []
for tType, tMeta, tText, tFmt, tStyle in self._blocks:
@@ -213,25 +199,25 @@ class ToHtml(Tokenizer):
if tType == BlockTyp.TEXT:
lines.append(f"<p{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
elif tType == BlockTyp.TITLE:
elif tType in (BlockTyp.TITLE, BlockTyp.PART):
tHead = tText.replace("\n", "<br>")
lines.append(f"<h1 class='title'{hStyle}>{aNm}{tHead}</h1>\n")
elif tType == BlockTyp.HEAD1:
tHead = tText.replace("\n", "<br>")
lines.append(f"<{h1}{h1Cl}{hStyle}>{aNm}{tHead}</{h1}>\n")
lines.append(f"<h1{hStyle}>{aNm}{tHead}</h1>\n")
elif tType == BlockTyp.HEAD2:
tHead = tText.replace("\n", "<br>")
lines.append(f"<{h2}{hStyle}>{aNm}{tHead}</{h2}>\n")
lines.append(f"<h2{hStyle}>{aNm}{tHead}</h2>\n")
elif tType == BlockTyp.HEAD3:
tHead = tText.replace("\n", "<br>")
lines.append(f"<{h3}{hStyle}>{aNm}{tHead}</{h3}>\n")
lines.append(f"<h3{hStyle}>{aNm}{tHead}</h3>\n")
elif tType == BlockTyp.HEAD4:
tHead = tText.replace("\n", "<br>")
lines.append(f"<{h4}{hStyle}>{aNm}{tHead}</{h4}>\n")
lines.append(f"<h4{hStyle}>{aNm}{tHead}</h4>\n")
elif tType == BlockTyp.SEP:
lines.append(f"<p class='sep'{hStyle}>{tText}</p>\n")
@@ -310,9 +296,7 @@ class ToHtml(Tokenizer):
"</style>\n"
"</head>\n"
"<body>\n"
"<article>\n"
"{body:s}\n"
"</article>\n"
"</body>\n"
"</html>\n"
).format(
@@ -359,6 +343,12 @@ class ToHtml(Tokenizer):
mtSP = self._marginSep[0]
mbSP = self._marginSep[1]
fSz0 = nwStyles.H_SIZES[0]
fSz1 = nwStyles.H_SIZES[1]
fSz2 = nwStyles.H_SIZES[2]
fSz3 = nwStyles.H_SIZES[3]
fSz4 = nwStyles.H_SIZES[4]
font = self._textFont
fFam = font.family()
fSz = font.pointSize()
@@ -379,12 +369,25 @@ class ToHtml(Tokenizer):
styles.append(f"a {{color: {lColor};}}")
styles.append(f"mark {{background: {mColor};}}")
styles.append(f"h1, h2, h3, h4 {{color: {hColor}; page-break-after: avoid;}}")
styles.append(f"h1 {{margin-top: {mtH1:.2f}em; margin-bottom: {mbH1:.2f}em;}}")
styles.append(f"h2 {{margin-top: {mtH2:.2f}em; margin-bottom: {mbH2:.2f}em;}}")
styles.append(f"h3 {{margin-top: {mtH3:.2f}em; margin-bottom: {mbH3:.2f}em;}}")
styles.append(f"h4 {{margin-top: {mtH4:.2f}em; margin-bottom: {mbH4:.2f}em;}}")
styles.append(
f".title {{font-size: 2.5em; margin-top: {mtH0:.2f}em; margin-bottom: {mbH0:.2f}em;}}"
f"h1 {{font-size: {fSz1:.2f}em; "
f"margin-top: {mtH1:.2f}em; margin-bottom: {mbH1:.2f}em;}}"
)
styles.append(
f"h2 {{font-size: {fSz2:.2f}em; "
f"margin-top: {mtH2:.2f}em; margin-bottom: {mbH2:.2f}em;}}"
)
styles.append(
f"h3 {{font-size: {fSz3:.2f}em; "
f"margin-top: {mtH3:.2f}em; margin-bottom: {mbH3:.2f}em;}}"
)
styles.append(
f"h4 {{font-size: {fSz4:.2f}em; "
f"margin-top: {mtH4:.2f}em; margin-bottom: {mbH4:.2f}em;}}"
)
styles.append(
f".title {{font-size: {fSz0:.2f}em; "
f"margin-top: {mtH0:.2f}em; margin-bottom: {mbH0:.2f}em;}}"
)
styles.append(
f".sep {{text-align: center; margin-top: {mtSP:.2f}em; margin-bottom: {mbSP:.2f}em;}}"
+14 -8
View File
@@ -68,11 +68,11 @@ COMMENT_STYLE = {
nwComment.COMMENT: ComStyle(),
nwComment.STORY: ComStyle("", "modifier", "note"),
}
HEADINGS = [BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4]
SKIP_INDENT = [
BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD2, BlockTyp.HEAD3,
BlockTyp.HEAD4, BlockTyp.SEP, BlockTyp.SKIP,
HEADINGS = [
BlockTyp.TITLE, BlockTyp.PART, BlockTyp.HEAD1,
BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4,
]
SKIP_INDENT = HEADINGS + [BlockTyp.SEP, BlockTyp.SKIP]
B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
@@ -654,6 +654,7 @@ class Tokenizer(ABC):
if not (isPlain or isNovel and sHide):
tStyle |= self._titleStyle
if isNovel:
tType = BlockTyp.PART if isPlain else BlockTyp.TITLE
if sHide:
tText = ""
tType = BlockTyp.EMPTY
@@ -687,6 +688,7 @@ class Tokenizer(ABC):
sHide = self._hideChapter if isPlain else self._hideUnNum
tFormat = self._fmtChapter if isPlain else self._fmtUnNum
if isNovel:
tType = BlockTyp.HEAD1 # Promote
if isPlain:
self._hFormatter.incChapter()
if sHide:
@@ -721,6 +723,7 @@ class Tokenizer(ABC):
sHide = self._hideScene if isPlain else self._hideHScene
tFormat = self._fmtScene if isPlain else self._fmtHScene
if isNovel:
tType = BlockTyp.HEAD2 # Promote
self._hFormatter.incScene()
if sHide:
tText = ""
@@ -752,6 +755,7 @@ class Tokenizer(ABC):
tText = aLine[5:].strip()
tType = BlockTyp.HEAD4
if isNovel:
tType = BlockTyp.HEAD3 # Promote
if self._hideSection:
tText = ""
tType = BlockTyp.EMPTY
@@ -927,12 +931,14 @@ class Tokenizer(ABC):
for tType, tKey, tText, _, _ in self._blocks:
if tType == BlockTyp.TITLE:
prefix = "TT"
elif tType == BlockTyp.PART:
prefix = "PT"
elif tType == BlockTyp.HEAD1:
prefix = "PT" if isNovel else "H1"
prefix = "CH" if isNovel else "H1"
elif tType == BlockTyp.HEAD2:
prefix = "CH" if isNovel else "H2"
elif tType == BlockTyp.HEAD3:
prefix = "SC" if isNovel else "H3"
prefix = "SC" if isNovel else "H2"
elif tType == BlockTyp.HEAD3 and not isNovel:
prefix = "H3"
else:
continue
+3 -2
View File
@@ -113,9 +113,10 @@ class ToMarkdown(Tokenizer):
tTemp = self._formatText(tText, tFormat, mTags).replace("\n", " \n")
lines.append(f"{tTemp}\n\n")
elif tType == BlockTyp.TITLE:
elif tType in (BlockTyp.TITLE, BlockTyp.PART):
tHead = tText.replace("\n", " - ")
lines.append(f"# {tHead}\n\n")
lines.append(f"{tHead}\n")
lines.append("="*len(tHead) + "\n\n")
elif tType == BlockTyp.HEAD1:
tHead = tText.replace("\n", " - ")
+1 -1
View File
@@ -363,7 +363,7 @@ class ToOdt(Tokenizer):
else:
self._addTextPar(xText, S_TEXT, oStyle, tText, tFmt=tFormat)
elif tType == BlockTyp.TITLE:
elif tType in (BlockTyp.TITLE, BlockTyp.PART):
# Title must be text:p
self._addTextPar(xText, S_TITLE, oStyle, tText, isHead=False)
+2
View File
@@ -156,6 +156,7 @@ class ToQTextDocument(Tokenizer):
self._mHead = {
BlockTyp.TITLE: (fPx * self._marginTitle[0], fPx * self._marginTitle[1]),
BlockTyp.PART: (fPx * self._marginTitle[0], fPx * self._marginTitle[1]),
BlockTyp.HEAD1: (fPx * self._marginHead1[0], fPx * self._marginHead1[1]),
BlockTyp.HEAD2: (fPx * self._marginHead2[0], fPx * self._marginHead2[1]),
BlockTyp.HEAD3: (fPx * self._marginHead3[0], fPx * self._marginHead3[1]),
@@ -165,6 +166,7 @@ class ToQTextDocument(Tokenizer):
hScale = self._scaleHeads
self._sHead = {
BlockTyp.TITLE: (nwStyles.H_SIZES.get(0, 1.0) * fPt) if hScale else fPt,
BlockTyp.PART: (nwStyles.H_SIZES.get(0, 1.0) * fPt) if hScale else fPt,
BlockTyp.HEAD1: (nwStyles.H_SIZES.get(1, 1.0) * fPt) if hScale else fPt,
BlockTyp.HEAD2: (nwStyles.H_SIZES.get(2, 1.0) * fPt) if hScale else fPt,
BlockTyp.HEAD3: (nwStyles.H_SIZES.get(3, 1.0) * fPt) if hScale else fPt,