Fix issue with line spacing in DocX
This commit is contained in:
+187
-160
@@ -29,10 +29,11 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import NamedTuple
|
||||
from zipfile import ZipFile
|
||||
|
||||
from novelwriter import __version__
|
||||
from novelwriter.common import xmlIndent
|
||||
from novelwriter.common import firstFloat, xmlIndent, xmlSubElem
|
||||
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.formats.tokenizer import T_Formats, Tokenizer
|
||||
@@ -75,19 +76,6 @@ def _mkTag(ns: str, tag: str) -> str:
|
||||
return tag
|
||||
|
||||
|
||||
def _addSingle(
|
||||
parent: ET.Element,
|
||||
tag: str,
|
||||
text: str | int | None = None,
|
||||
attrib: dict | None = None
|
||||
) -> None:
|
||||
"""Add a single value to a parent element."""
|
||||
xSub = ET.SubElement(parent, tag, attrib=attrib or {})
|
||||
if text is not None:
|
||||
xSub.text = str(text)
|
||||
return
|
||||
|
||||
|
||||
# Formatting Codes
|
||||
X_BLD = 0x001 # Bold format
|
||||
X_ITA = 0x002 # Italic format
|
||||
@@ -130,6 +118,22 @@ COL_META_TXT = "813709"
|
||||
COL_MARK_TXT = "ffffa6"
|
||||
|
||||
|
||||
class DocXParStyle(NamedTuple):
|
||||
|
||||
name: str
|
||||
styleId: str
|
||||
size: float
|
||||
basedOn: str | None = None
|
||||
nextStyle: str | None = None
|
||||
before: float | None = None
|
||||
after: float | None = None
|
||||
line: float | None = None
|
||||
align: str | None = None
|
||||
default: bool = False
|
||||
level: int | None = None
|
||||
color: str | None = None
|
||||
|
||||
|
||||
class ToDocX(Tokenizer):
|
||||
"""Core: DocX Document Writer
|
||||
|
||||
@@ -154,6 +158,9 @@ class ToDocX(Tokenizer):
|
||||
self._fontSize = 12.0
|
||||
self._dLanguage = "en-GB"
|
||||
|
||||
# Maps
|
||||
self._styles: dict[str, DocXParStyle] = {}
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
@@ -194,7 +201,7 @@ class ToDocX(Tokenizer):
|
||||
self._dDoc = ET.Element(_wTag("document"))
|
||||
self._dStyl = ET.Element(_wTag("styles"))
|
||||
|
||||
self._xBody = ET.SubElement(self._dDoc, _wTag("body"))
|
||||
self._xBody = xmlSubElem(self._dDoc, _wTag("body"))
|
||||
|
||||
self._defaultStyles()
|
||||
self._useableStyles()
|
||||
@@ -295,76 +302,76 @@ class ToDocX(Tokenizer):
|
||||
|
||||
# .rels
|
||||
dRels = ET.Element("Relationships", attrib={"xmlns": RELS_NS})
|
||||
_addSingle(dRels, "Relationship", attrib={
|
||||
xmlSubElem(dRels, "Relationship", attrib={
|
||||
"Id": "rId1", "Type": REL_CORE, "Target": "docProps/core.xml",
|
||||
})
|
||||
_addSingle(dRels, "Relationship", attrib={
|
||||
xmlSubElem(dRels, "Relationship", attrib={
|
||||
"Id": "rId2", "Type": f"{REL_BASE}/extended-properties", "Target": "docProps/app.xml",
|
||||
})
|
||||
_addSingle(dRels, "Relationship", attrib={
|
||||
xmlSubElem(dRels, "Relationship", attrib={
|
||||
"Id": "rId3", "Type": f"{REL_BASE}/officeDocument", "Target": "word/document.xml",
|
||||
})
|
||||
|
||||
# core.xml
|
||||
dCore = ET.Element("coreProperties")
|
||||
tsAttr = {_mkTag("xsi", "type"): "dcterms:W3CDTF"}
|
||||
_addSingle(dCore, _mkTag("dcterms", "created"), timeStamp, attrib=tsAttr)
|
||||
_addSingle(dCore, _mkTag("dcterms", "modified"), timeStamp, attrib=tsAttr)
|
||||
_addSingle(dCore, _mkTag("dc", "creator"), self._project.data.author)
|
||||
_addSingle(dCore, _mkTag("dc", "title"), self._project.data.name)
|
||||
_addSingle(dCore, _mkTag("dc", "creator"), self._project.data.author)
|
||||
_addSingle(dCore, _mkTag("dc", "language"), self._dLanguage)
|
||||
_addSingle(dCore, _mkTag("cp", "revision"), str(self._project.data.saveCount))
|
||||
_addSingle(dCore, _mkTag("cp", "lastModifiedBy"), self._project.data.author)
|
||||
xmlSubElem(dCore, _mkTag("dcterms", "created"), timeStamp, attrib=tsAttr)
|
||||
xmlSubElem(dCore, _mkTag("dcterms", "modified"), timeStamp, attrib=tsAttr)
|
||||
xmlSubElem(dCore, _mkTag("dc", "creator"), self._project.data.author)
|
||||
xmlSubElem(dCore, _mkTag("dc", "title"), self._project.data.name)
|
||||
xmlSubElem(dCore, _mkTag("dc", "creator"), self._project.data.author)
|
||||
xmlSubElem(dCore, _mkTag("dc", "language"), self._dLanguage)
|
||||
xmlSubElem(dCore, _mkTag("cp", "revision"), str(self._project.data.saveCount))
|
||||
xmlSubElem(dCore, _mkTag("cp", "lastModifiedBy"), self._project.data.author)
|
||||
|
||||
# app.xml
|
||||
dApp = ET.Element("Properties", attrib={"xmlns": PROPS_NS})
|
||||
_addSingle(dApp, "TotalTime", self._project.data.editTime // 60)
|
||||
_addSingle(dApp, "Application", f"novelWriter/{__version__}")
|
||||
xmlSubElem(dApp, "TotalTime", self._project.data.editTime // 60)
|
||||
xmlSubElem(dApp, "Application", f"novelWriter/{__version__}")
|
||||
if count := self._counts.get("allWords"):
|
||||
_addSingle(dApp, "Words", count)
|
||||
xmlSubElem(dApp, "Words", count)
|
||||
if count := self._counts.get("textWordChars"):
|
||||
_addSingle(dApp, "Characters", count)
|
||||
xmlSubElem(dApp, "Characters", count)
|
||||
if count := self._counts.get("textChars"):
|
||||
_addSingle(dApp, "CharactersWithSpaces", count)
|
||||
xmlSubElem(dApp, "CharactersWithSpaces", count)
|
||||
if count := self._counts.get("paragraphCount"):
|
||||
_addSingle(dApp, "Paragraphs", count)
|
||||
xmlSubElem(dApp, "Paragraphs", count)
|
||||
|
||||
# document.xml.rels
|
||||
dDRels = ET.Element("Relationships", attrib={"xmlns": RELS_NS})
|
||||
_addSingle(dDRels, "Relationship", attrib={
|
||||
xmlSubElem(dDRels, "Relationship", attrib={
|
||||
"Id": "rId1", "Type": f"{REL_BASE}/styles", "Target": "styles.xml",
|
||||
})
|
||||
|
||||
# [Content_Types].xml
|
||||
dCont = ET.Element("Types", attrib={"xmlns": TYPES_NS})
|
||||
_addSingle(dCont, "Default", attrib={
|
||||
xmlSubElem(dCont, "Default", attrib={
|
||||
"Extension": "xml", "ContentType": "application/xml",
|
||||
})
|
||||
_addSingle(dCont, "Default", attrib={
|
||||
xmlSubElem(dCont, "Default", attrib={
|
||||
"Extension": "rels", "ContentType": RELS_TYPE,
|
||||
})
|
||||
_addSingle(dCont, "Override", attrib={
|
||||
xmlSubElem(dCont, "Override", attrib={
|
||||
"PartName": "/_rels/.rels",
|
||||
"ContentType": RELS_TYPE,
|
||||
})
|
||||
_addSingle(dCont, "Override", attrib={
|
||||
xmlSubElem(dCont, "Override", attrib={
|
||||
"PartName": "/docProps/core.xml",
|
||||
"ContentType": f"{WORD_BASE}.extended-properties+xml",
|
||||
})
|
||||
_addSingle(dCont, "Override", attrib={
|
||||
xmlSubElem(dCont, "Override", attrib={
|
||||
"PartName": "/docProps/app.xml",
|
||||
"ContentType": "application/vnd.openxmlformats-package.core-properties+xml",
|
||||
})
|
||||
_addSingle(dCont, "Override", attrib={
|
||||
xmlSubElem(dCont, "Override", attrib={
|
||||
"PartName": "/word/_rels/document.xml.rels",
|
||||
"ContentType": RELS_TYPE,
|
||||
})
|
||||
_addSingle(dCont, "Override", attrib={
|
||||
xmlSubElem(dCont, "Override", attrib={
|
||||
"PartName": "/word/document.xml",
|
||||
"ContentType": f"{WORD_BASE}.wordprocessingml.document.main+xml",
|
||||
})
|
||||
_addSingle(dCont, "Override", attrib={
|
||||
xmlSubElem(dCont, "Override", attrib={
|
||||
"PartName": "/word/styles.xml",
|
||||
"ContentType": f"{WORD_BASE}.wordprocessingml.styles+xml",
|
||||
})
|
||||
@@ -428,7 +435,7 @@ class ToDocX(Tokenizer):
|
||||
self, par: DocXParagraph, pStyle: str, text: str, tFmt: T_Formats | None = None
|
||||
) -> None:
|
||||
"""Apply formatting tags to text."""
|
||||
par.setStyle(pStyle)
|
||||
par.setStyle(self._styles.get(pStyle))
|
||||
xFmt = 0x00
|
||||
fStart = 0
|
||||
for fPos, fFmt, fData in tFmt or []:
|
||||
@@ -487,27 +494,27 @@ class ToDocX(Tokenizer):
|
||||
def _textRunToXml(self, text: str, fmt: int) -> ET.Element:
|
||||
"""Encode the text run into XML."""
|
||||
run = ET.Element(_wTag("r"))
|
||||
rPr = ET.SubElement(run, _wTag("rPr"))
|
||||
rPr = xmlSubElem(run, _wTag("rPr"))
|
||||
if fmt & X_BLD == X_BLD:
|
||||
ET.SubElement(rPr, _wTag("b"))
|
||||
xmlSubElem(rPr, _wTag("b"))
|
||||
if fmt & X_ITA == X_ITA:
|
||||
ET.SubElement(rPr, _wTag("i"))
|
||||
xmlSubElem(rPr, _wTag("i"))
|
||||
if fmt & X_UND == X_UND:
|
||||
ET.SubElement(rPr, _wTag("u"), attrib={_wTag("val"): "single"})
|
||||
xmlSubElem(rPr, _wTag("u"), attrib={_wTag("val"): "single"})
|
||||
if fmt & X_MRK == X_MRK:
|
||||
ET.SubElement(rPr, _wTag("shd"), attrib={
|
||||
xmlSubElem(rPr, _wTag("shd"), attrib={
|
||||
_wTag("fill"): COL_MARK_TXT, _wTag("val"): "clear",
|
||||
})
|
||||
if fmt & X_DEL == X_DEL:
|
||||
ET.SubElement(rPr, _wTag("strike"))
|
||||
xmlSubElem(rPr, _wTag("strike"))
|
||||
if fmt & X_SUP == X_SUP:
|
||||
ET.SubElement(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "superscript"})
|
||||
xmlSubElem(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "superscript"})
|
||||
if fmt & X_SUB == X_SUB:
|
||||
ET.SubElement(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "subscript"})
|
||||
xmlSubElem(rPr, _wTag("vertAlign"), attrib={_wTag("val"): "subscript"})
|
||||
if fmt & X_DLG == X_DLG:
|
||||
ET.SubElement(rPr, _wTag("color"), attrib={_wTag("val"): COL_DIALOG_M})
|
||||
xmlSubElem(rPr, _wTag("color"), attrib={_wTag("val"): COL_DIALOG_M})
|
||||
if fmt & X_DLA == X_DLA:
|
||||
ET.SubElement(rPr, _wTag("color"), attrib={_wTag("val"): COL_DIALOG_A})
|
||||
xmlSubElem(rPr, _wTag("color"), attrib={_wTag("val"): COL_DIALOG_A})
|
||||
|
||||
remaining = text
|
||||
while (parts := remaining.partition("\n"))[0]:
|
||||
@@ -515,9 +522,9 @@ class ToDocX(Tokenizer):
|
||||
attr = {}
|
||||
if len(segment) != len(segment.strip()):
|
||||
attr[_mkTag("xml", "space")] = "preserve"
|
||||
_addSingle(run, _wTag("t"), segment, attrib=attr)
|
||||
xmlSubElem(run, _wTag("t"), segment, attrib=attr)
|
||||
if parts[1]:
|
||||
_addSingle(run, _wTag("br"))
|
||||
xmlSubElem(run, _wTag("br"))
|
||||
remaining = parts[2]
|
||||
|
||||
return run
|
||||
@@ -528,24 +535,24 @@ class ToDocX(Tokenizer):
|
||||
|
||||
def _defaultStyles(self) -> None:
|
||||
"""Set the default styles."""
|
||||
xStyl = ET.SubElement(self._dStyl, _wTag("docDefaults"))
|
||||
xRDef = ET.SubElement(xStyl, _wTag("rPrDefault"))
|
||||
xPDef = ET.SubElement(xStyl, _wTag("pPrDefault"))
|
||||
xRPr = ET.SubElement(xRDef, _wTag("rPr"))
|
||||
xPPr = ET.SubElement(xPDef, _wTag("pPr"))
|
||||
xStyl = xmlSubElem(self._dStyl, _wTag("docDefaults"))
|
||||
xRDef = xmlSubElem(xStyl, _wTag("rPrDefault"))
|
||||
xPDef = xmlSubElem(xStyl, _wTag("pPrDefault"))
|
||||
xRPr = xmlSubElem(xRDef, _wTag("rPr"))
|
||||
xPPr = xmlSubElem(xPDef, _wTag("pPr"))
|
||||
|
||||
size = str(int(2.0 * self._fontSize))
|
||||
line = str(int(20.0 * self._lineHeight * self._fontSize))
|
||||
|
||||
ET.SubElement(xRPr, _wTag("rFonts"), attrib={
|
||||
xmlSubElem(xRPr, _wTag("rFonts"), attrib={
|
||||
_wTag("ascii"): self._fontFamily,
|
||||
_wTag("hAnsi"): self._fontFamily,
|
||||
_wTag("cs"): self._fontFamily,
|
||||
})
|
||||
ET.SubElement(xRPr, _wTag("sz"), attrib={_wTag("val"): size})
|
||||
ET.SubElement(xRPr, _wTag("szCs"), attrib={_wTag("val"): size})
|
||||
ET.SubElement(xRPr, _wTag("lang"), attrib={_wTag("val"): self._dLanguage})
|
||||
ET.SubElement(xPPr, _wTag("spacing"), attrib={_wTag("line"): line})
|
||||
xmlSubElem(xRPr, _wTag("sz"), attrib={_wTag("val"): size})
|
||||
xmlSubElem(xRPr, _wTag("szCs"), attrib={_wTag("val"): size})
|
||||
xmlSubElem(xRPr, _wTag("lang"), attrib={_wTag("val"): self._dLanguage})
|
||||
xmlSubElem(xPPr, _wTag("spacing"), attrib={_wTag("line"): line})
|
||||
|
||||
return
|
||||
|
||||
@@ -553,143 +560,156 @@ class ToDocX(Tokenizer):
|
||||
"""Set the usable styles."""
|
||||
hScale = self._scaleHeads
|
||||
hColor = self._colorHeads
|
||||
fSz = self._fontSize
|
||||
fSz0 = (nwStyles.H_SIZES[0] * fSz) if hScale else fSz
|
||||
fSz1 = (nwStyles.H_SIZES[1] * fSz) if hScale else fSz
|
||||
fSz2 = (nwStyles.H_SIZES[2] * fSz) if hScale else fSz
|
||||
fSz3 = (nwStyles.H_SIZES[3] * fSz) if hScale else fSz
|
||||
fSz4 = (nwStyles.H_SIZES[4] * fSz) if hScale else fSz
|
||||
|
||||
# Add Normal Style
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Normal",
|
||||
styleId=S_NORM,
|
||||
size=fSz,
|
||||
default=True,
|
||||
margins=self._marginText,
|
||||
)
|
||||
before=fSz * self._marginText[0],
|
||||
after=fSz * self._marginText[1],
|
||||
line=fSz * self._lineHeight,
|
||||
))
|
||||
|
||||
# Add Title
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Title",
|
||||
styleId=S_TITLE,
|
||||
size=nwStyles.H_SIZES[0] if hScale else 1.0,
|
||||
size=fSz0,
|
||||
basedOn=S_NORM,
|
||||
nextStyle=S_NORM,
|
||||
margins=self._marginTitle,
|
||||
before=fSz * self._marginTitle[0],
|
||||
after=fSz * self._marginTitle[1],
|
||||
line=fSz0 * self._lineHeight,
|
||||
level=0,
|
||||
)
|
||||
))
|
||||
|
||||
# Add Heading 1
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Heading 1",
|
||||
styleId=S_HEAD1,
|
||||
size=nwStyles.H_SIZES[1] if hScale else 1.0,
|
||||
size=fSz1,
|
||||
basedOn=S_NORM,
|
||||
nextStyle=S_NORM,
|
||||
margins=self._marginHead1,
|
||||
before=fSz * self._marginHead1[0],
|
||||
after=fSz * self._marginHead1[1],
|
||||
line=fSz1 * self._lineHeight,
|
||||
level=0,
|
||||
color=COL_HEAD_L12 if hColor else None,
|
||||
)
|
||||
))
|
||||
|
||||
# Add Heading 2
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Heading 2",
|
||||
styleId=S_HEAD2,
|
||||
size=nwStyles.H_SIZES[2] if hScale else 1.0,
|
||||
size=fSz2,
|
||||
basedOn=S_NORM,
|
||||
nextStyle=S_NORM,
|
||||
margins=self._marginHead2,
|
||||
before=fSz * self._marginHead2[0],
|
||||
after=fSz * self._marginHead2[1],
|
||||
line=fSz2 * self._lineHeight,
|
||||
level=1,
|
||||
color=COL_HEAD_L12 if hColor else None,
|
||||
)
|
||||
))
|
||||
|
||||
# Add Heading 3
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Heading 3",
|
||||
styleId=S_HEAD3,
|
||||
size=nwStyles.H_SIZES[3] if hScale else 1.0,
|
||||
size=fSz3,
|
||||
basedOn=S_NORM,
|
||||
nextStyle=S_NORM,
|
||||
margins=self._marginHead3,
|
||||
before=fSz * self._marginHead3[0],
|
||||
after=fSz * self._marginHead3[1],
|
||||
line=fSz3 * self._lineHeight,
|
||||
level=1,
|
||||
color=COL_HEAD_L34 if hColor else None,
|
||||
)
|
||||
))
|
||||
|
||||
# Add Heading 4
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Heading 4",
|
||||
styleId=S_HEAD4,
|
||||
size=nwStyles.H_SIZES[4] if hScale else 1.0,
|
||||
size=fSz4,
|
||||
basedOn=S_NORM,
|
||||
nextStyle=S_NORM,
|
||||
margins=self._marginHead4,
|
||||
before=fSz * self._marginHead4[0],
|
||||
after=fSz * self._marginHead4[1],
|
||||
line=fSz4 * self._lineHeight,
|
||||
level=1,
|
||||
color=COL_HEAD_L34 if hColor else None,
|
||||
)
|
||||
))
|
||||
|
||||
# Add Separator
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Separator",
|
||||
styleId=S_SEP,
|
||||
size=fSz,
|
||||
basedOn=S_NORM,
|
||||
nextStyle=S_NORM,
|
||||
margins=self._marginSep,
|
||||
before=fSz * self._marginSep[0],
|
||||
after=fSz * self._marginSep[1],
|
||||
line=fSz * self._lineHeight,
|
||||
align="center",
|
||||
)
|
||||
))
|
||||
|
||||
# Add Text Meta Style
|
||||
self._addParStyle(
|
||||
self._addParStyle(DocXParStyle(
|
||||
name="Text Meta",
|
||||
styleId=S_META,
|
||||
size=fSz,
|
||||
basedOn=S_NORM,
|
||||
nextStyle=S_NORM,
|
||||
margins=self._marginMeta,
|
||||
before=fSz * self._marginMeta[0],
|
||||
after=fSz * self._marginMeta[1],
|
||||
line=fSz * self._lineHeight,
|
||||
color=COL_META_TXT,
|
||||
)
|
||||
))
|
||||
|
||||
return
|
||||
|
||||
def _addParStyle(
|
||||
self, *,
|
||||
name: str,
|
||||
styleId: str,
|
||||
size: float = 1.0,
|
||||
basedOn: str | None = None,
|
||||
nextStyle: str | None = None,
|
||||
margins: tuple[float, float] | None = None,
|
||||
align: str | None = None,
|
||||
default: bool = False,
|
||||
level: int | None = None,
|
||||
color: str | None = None,
|
||||
) -> None:
|
||||
def _addParStyle(self, style: DocXParStyle) -> None:
|
||||
"""Add a paragraph style."""
|
||||
sAttr = {}
|
||||
sAttr[_wTag("type")] = "paragraph"
|
||||
sAttr[_wTag("styleId")] = styleId
|
||||
if default:
|
||||
sAttr[_wTag("styleId")] = style.styleId
|
||||
if style.default:
|
||||
sAttr[_wTag("default")] = "1"
|
||||
|
||||
sz = str(int(2.0 * size * self._fontSize))
|
||||
ln = str(int(20.0 * size * self._lineHeight * self._fontSize))
|
||||
size = firstFloat(style.size, self._fontSize)
|
||||
|
||||
xStyl = ET.SubElement(self._dStyl, _wTag("style"), attrib=sAttr)
|
||||
ET.SubElement(xStyl, _wTag("name"), attrib={_wTag("val"): name})
|
||||
if basedOn:
|
||||
ET.SubElement(xStyl, _wTag("basedOn"), attrib={_wTag("val"): basedOn})
|
||||
if nextStyle:
|
||||
ET.SubElement(xStyl, _wTag("next"), attrib={_wTag("val"): nextStyle})
|
||||
if level is not None:
|
||||
ET.SubElement(xStyl, _wTag("outlineLvl"), attrib={_wTag("val"): str(level)})
|
||||
xStyl = xmlSubElem(self._dStyl, _wTag("style"), attrib=sAttr)
|
||||
xmlSubElem(xStyl, _wTag("name"), attrib={_wTag("val"): style.name})
|
||||
if style.basedOn:
|
||||
xmlSubElem(xStyl, _wTag("basedOn"), attrib={_wTag("val"): style.basedOn})
|
||||
if style.nextStyle:
|
||||
xmlSubElem(xStyl, _wTag("next"), attrib={_wTag("val"): style.nextStyle})
|
||||
if style.level is not None:
|
||||
xmlSubElem(xStyl, _wTag("outlineLvl"), attrib={_wTag("val"): str(style.level)})
|
||||
|
||||
pPr = ET.SubElement(xStyl, _wTag("pPr"))
|
||||
if margins:
|
||||
ET.SubElement(pPr, _wTag("spacing"), attrib={
|
||||
_wTag("before"): str(int(20.0 * margins[0] * self._fontSize)),
|
||||
_wTag("after"): str(int(20.0 * margins[1] * self._fontSize)),
|
||||
_wTag("line"): ln,
|
||||
})
|
||||
if align:
|
||||
ET.SubElement(pPr, _wTag("jc"), attrib={_wTag("val"): align})
|
||||
pPr = xmlSubElem(xStyl, _wTag("pPr"))
|
||||
xmlSubElem(pPr, _wTag("spacing"), attrib={
|
||||
_wTag("before"): str(int(20.0 * firstFloat(style.before))),
|
||||
_wTag("after"): str(int(20.0 * firstFloat(style.after))),
|
||||
_wTag("line"): str(int(20.0 * firstFloat(style.line, size))),
|
||||
})
|
||||
if style.align:
|
||||
xmlSubElem(pPr, _wTag("jc"), attrib={_wTag("val"): style.align})
|
||||
|
||||
rPr = ET.SubElement(xStyl, _wTag("rPr"))
|
||||
ET.SubElement(rPr, _wTag("sz"), attrib={_wTag("val"): sz})
|
||||
ET.SubElement(rPr, _wTag("szCs"), attrib={_wTag("val"): sz})
|
||||
if color:
|
||||
ET.SubElement(rPr, _wTag("color"), attrib={_wTag("val"): color})
|
||||
rPr = xmlSubElem(xStyl, _wTag("rPr"))
|
||||
xmlSubElem(rPr, _wTag("sz"), attrib={_wTag("val"): str(int(2.0 * size))})
|
||||
xmlSubElem(rPr, _wTag("szCs"), attrib={_wTag("val"): str(int(2.0 * size))})
|
||||
if style.color:
|
||||
xmlSubElem(rPr, _wTag("color"), attrib={_wTag("val"): style.color})
|
||||
|
||||
self._styles[style.styleId] = style
|
||||
|
||||
return
|
||||
|
||||
@@ -698,10 +718,10 @@ class DocXParagraph:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._content: list[ET.Element] = []
|
||||
self._style: str = S_NORM
|
||||
self._style: DocXParStyle | None = None
|
||||
self._textAlign: str | None = None
|
||||
self._topMargin: int | None = None
|
||||
self._bottomMargin: int | None = None
|
||||
self._topMargin: float | None = None
|
||||
self._bottomMargin: float | None = None
|
||||
self._breakBefore = False
|
||||
self._breakAfter = False
|
||||
return
|
||||
@@ -710,9 +730,9 @@ class DocXParagraph:
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setStyle(self, value: str) -> None:
|
||||
def setStyle(self, style: DocXParStyle | None) -> None:
|
||||
"""Set the paragraph style."""
|
||||
self._style = value
|
||||
self._style = style
|
||||
return
|
||||
|
||||
def setAlignment(self, value: str) -> None:
|
||||
@@ -723,12 +743,12 @@ class DocXParagraph:
|
||||
|
||||
def setMarginTop(self, value: float) -> None:
|
||||
"""Set margin above in pt."""
|
||||
self._topMargin = int(20.0 * value)
|
||||
self._topMargin = value
|
||||
return
|
||||
|
||||
def setMarginBottom(self, value: float) -> None:
|
||||
"""Set margin below in pt."""
|
||||
self._bottomMargin = int(20.0 * value)
|
||||
self._bottomMargin = value
|
||||
return
|
||||
|
||||
def setPageBreakBefore(self, state: bool) -> None:
|
||||
@@ -752,29 +772,36 @@ class DocXParagraph:
|
||||
|
||||
def finalise(self, body: ET.Element) -> None:
|
||||
"""Called after all content is set."""
|
||||
par = ET.SubElement(body, _wTag("p"))
|
||||
if style := self._style:
|
||||
par = xmlSubElem(body, _wTag("p"))
|
||||
|
||||
# Values
|
||||
spacing = {}
|
||||
if self._topMargin is not None:
|
||||
spacing["before"] = str(self._topMargin)
|
||||
if self._bottomMargin is not None:
|
||||
spacing["after"] = str(self._bottomMargin)
|
||||
# Values
|
||||
spacing = {}
|
||||
if self._topMargin is not None:
|
||||
spacing["before"] = str(self._topMargin)
|
||||
if self._bottomMargin is not None:
|
||||
spacing["after"] = str(self._bottomMargin)
|
||||
|
||||
# Paragraph
|
||||
pPr = ET.SubElement(par, _wTag("pPr"))
|
||||
_addSingle(pPr, _wTag("pStyle"), attrib={_wTag("val"): self._style})
|
||||
if spacing:
|
||||
_addSingle(pPr, _wTag("spacing"), attrib=spacing)
|
||||
if self._textAlign:
|
||||
_addSingle(pPr, _wTag("jc"), attrib={_wTag("val"): self._textAlign})
|
||||
# Paragraph
|
||||
pPr = xmlSubElem(par, _wTag("pPr"))
|
||||
xmlSubElem(pPr, _wTag("pStyle"), attrib={_wTag("val"): style.styleId})
|
||||
if self._topMargin is not None or self._bottomMargin is not None:
|
||||
xmlSubElem(pPr, _wTag("spacing"), attrib={
|
||||
_wTag("before"): str(int(20.0 * firstFloat(self._topMargin, style.before))),
|
||||
_wTag("after"): str(int(20.0 * firstFloat(self._bottomMargin, style.after))),
|
||||
_wTag("line"): str(int(20.0 * firstFloat(style.line, style.size))),
|
||||
})
|
||||
if self._textAlign:
|
||||
xmlSubElem(pPr, _wTag("jc"), attrib={_wTag("val"): self._textAlign})
|
||||
|
||||
# Text
|
||||
if self._breakBefore:
|
||||
_addSingle(ET.SubElement(par, _wTag("r")), _wTag("br"), attrib={_wTag("type"): "page"})
|
||||
for run in self._content:
|
||||
par.append(run)
|
||||
if self._breakAfter:
|
||||
_addSingle(ET.SubElement(par, _wTag("r")), _wTag("br"), attrib={_wTag("type"): "page"})
|
||||
# Text
|
||||
if self._breakBefore:
|
||||
wr = xmlSubElem(par, _wTag("r"))
|
||||
xmlSubElem(wr, _wTag("br"), attrib={_wTag("type"): "page"})
|
||||
for run in self._content:
|
||||
par.append(run)
|
||||
if self._breakAfter:
|
||||
wr = xmlSubElem(par, _wTag("r"))
|
||||
xmlSubElem(wr, _wTag("br"), attrib={_wTag("type"): "page"})
|
||||
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user