novelWriter 2.5.1__py3-none-any.whl → 2.6b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/METADATA +2 -1
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/RECORD +61 -56
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/WHEEL +1 -1
 - novelwriter/__init__.py +3 -3
 - novelwriter/assets/i18n/project_en_GB.json +1 -0
 - novelwriter/assets/icons/typicons_dark/icons.conf +1 -0
 - novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
 - novelwriter/assets/icons/typicons_light/icons.conf +1 -0
 - novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
 - novelwriter/assets/manual.pdf +0 -0
 - novelwriter/assets/sample.zip +0 -0
 - novelwriter/assets/themes/default_light.conf +2 -2
 - novelwriter/common.py +63 -0
 - novelwriter/config.py +10 -3
 - novelwriter/constants.py +153 -60
 - novelwriter/core/buildsettings.py +66 -39
 - novelwriter/core/coretools.py +34 -22
 - novelwriter/core/docbuild.py +130 -169
 - novelwriter/core/index.py +29 -18
 - novelwriter/core/item.py +2 -2
 - novelwriter/core/options.py +4 -1
 - novelwriter/core/spellcheck.py +9 -14
 - novelwriter/dialogs/preferences.py +45 -32
 - novelwriter/dialogs/projectsettings.py +3 -3
 - novelwriter/enum.py +29 -23
 - novelwriter/extensions/configlayout.py +24 -11
 - novelwriter/extensions/modified.py +13 -1
 - novelwriter/extensions/pagedsidebar.py +5 -5
 - novelwriter/formats/shared.py +155 -0
 - novelwriter/formats/todocx.py +1195 -0
 - novelwriter/formats/tohtml.py +452 -0
 - novelwriter/{core → formats}/tokenizer.py +483 -485
 - novelwriter/formats/tomarkdown.py +217 -0
 - novelwriter/{core → formats}/toodt.py +270 -320
 - novelwriter/formats/toqdoc.py +436 -0
 - novelwriter/formats/toraw.py +91 -0
 - novelwriter/gui/doceditor.py +240 -193
 - novelwriter/gui/dochighlight.py +96 -84
 - novelwriter/gui/docviewer.py +56 -30
 - novelwriter/gui/docviewerpanel.py +3 -3
 - novelwriter/gui/editordocument.py +17 -2
 - novelwriter/gui/itemdetails.py +8 -4
 - novelwriter/gui/mainmenu.py +121 -60
 - novelwriter/gui/noveltree.py +35 -37
 - novelwriter/gui/outline.py +186 -238
 - novelwriter/gui/projtree.py +142 -131
 - novelwriter/gui/sidebar.py +7 -6
 - novelwriter/gui/theme.py +5 -4
 - novelwriter/guimain.py +43 -155
 - novelwriter/shared.py +14 -4
 - novelwriter/text/counting.py +2 -0
 - novelwriter/text/patterns.py +155 -59
 - novelwriter/tools/manusbuild.py +1 -1
 - novelwriter/tools/manuscript.py +121 -78
 - novelwriter/tools/manussettings.py +403 -260
 - novelwriter/tools/welcome.py +4 -4
 - novelwriter/tools/writingstats.py +3 -3
 - novelwriter/types.py +16 -6
 - novelwriter/core/tohtml.py +0 -530
 - novelwriter/core/tomarkdown.py +0 -252
 - novelwriter/core/toqdoc.py +0 -419
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/LICENSE.md +0 -0
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/entry_points.txt +0 -0
 - {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/top_level.txt +0 -0
 
| 
         @@ -0,0 +1,1195 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            novelWriter – DocX Text Converter
         
     | 
| 
      
 3 
     | 
    
         
            +
            =================================
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            File History:
         
     | 
| 
      
 6 
     | 
    
         
            +
            Created: 2024-10-18 [2.6b1] ToDocX
         
     | 
| 
      
 7 
     | 
    
         
            +
            Created: 2024-10-18 [2.6b1] DocXParagraph
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            This file is a part of novelWriter
         
     | 
| 
      
 10 
     | 
    
         
            +
            Copyright 2018–2024, Veronica Berglyd Olsen
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            This program is free software: you can redistribute it and/or modify
         
     | 
| 
      
 13 
     | 
    
         
            +
            it under the terms of the GNU General Public License as published by
         
     | 
| 
      
 14 
     | 
    
         
            +
            the Free Software Foundation, either version 3 of the License, or
         
     | 
| 
      
 15 
     | 
    
         
            +
            (at your option) any later version.
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            This program is distributed in the hope that it will be useful, but
         
     | 
| 
      
 18 
     | 
    
         
            +
            WITHOUT ANY WARRANTY; without even the implied warranty of
         
     | 
| 
      
 19 
     | 
    
         
            +
            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
         
     | 
| 
      
 20 
     | 
    
         
            +
            General Public License for more details.
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            You should have received a copy of the GNU General Public License
         
     | 
| 
      
 23 
     | 
    
         
            +
            along with this program. If not, see <https://www.gnu.org/licenses/>.
         
     | 
| 
      
 24 
     | 
    
         
            +
            """
         
     | 
| 
      
 25 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            import logging
         
     | 
| 
      
 28 
     | 
    
         
            +
            import re
         
     | 
| 
      
 29 
     | 
    
         
            +
            import xml.etree.ElementTree as ET
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            from datetime import datetime
         
     | 
| 
      
 32 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 33 
     | 
    
         
            +
            from typing import NamedTuple
         
     | 
| 
      
 34 
     | 
    
         
            +
            from zipfile import ZIP_DEFLATED, ZipFile
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            from PyQt5.QtCore import QMarginsF, QSizeF
         
     | 
| 
      
 37 
     | 
    
         
            +
            from PyQt5.QtGui import QColor
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            from novelwriter import __version__
         
     | 
| 
      
 40 
     | 
    
         
            +
            from novelwriter.common import firstFloat, xmlElement, xmlSubElem
         
     | 
| 
      
 41 
     | 
    
         
            +
            from novelwriter.constants import nwHeadFmt, nwStyles
         
     | 
| 
      
 42 
     | 
    
         
            +
            from novelwriter.core.project import NWProject
         
     | 
| 
      
 43 
     | 
    
         
            +
            from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
         
     | 
| 
      
 44 
     | 
    
         
            +
            from novelwriter.formats.tokenizer import Tokenizer
         
     | 
| 
      
 45 
     | 
    
         
            +
            from novelwriter.types import QtHexRgb
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            logger = logging.getLogger(__name__)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            # RegEx
         
     | 
| 
      
 50 
     | 
    
         
            +
            RX_TEXT = re.compile(r"([\n\t])", re.UNICODE)
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            # Types and Relationships
         
     | 
| 
      
 53 
     | 
    
         
            +
            OOXML_SCM = "http://schemas.openxmlformats.org"
         
     | 
| 
      
 54 
     | 
    
         
            +
            WORD_BASE = "application/vnd.openxmlformats-officedocument.wordprocessingml"
         
     | 
| 
      
 55 
     | 
    
         
            +
            RELS_TYPE = "application/vnd.openxmlformats-package.relationships+xml"
         
     | 
| 
      
 56 
     | 
    
         
            +
            RELS_BASE = f"{OOXML_SCM}/officeDocument/2006/relationships"
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            # Main XML NameSpaces
         
     | 
| 
      
 59 
     | 
    
         
            +
            XML_NS = {
         
     | 
| 
      
 60 
     | 
    
         
            +
                "cp":      f"{OOXML_SCM}/package/2006/metadata/core-properties",
         
     | 
| 
      
 61 
     | 
    
         
            +
                "dc":      "http://purl.org/dc/elements/1.1/",
         
     | 
| 
      
 62 
     | 
    
         
            +
                "dcterms": "http://purl.org/dc/terms/",
         
     | 
| 
      
 63 
     | 
    
         
            +
                "r":       RELS_BASE,
         
     | 
| 
      
 64 
     | 
    
         
            +
                "w":       f"{OOXML_SCM}/wordprocessingml/2006/main",
         
     | 
| 
      
 65 
     | 
    
         
            +
                "xml":     "http://www.w3.org/XML/1998/namespace",
         
     | 
| 
      
 66 
     | 
    
         
            +
                "xsi":     "http://www.w3.org/2001/XMLSchema-instance",
         
     | 
| 
      
 67 
     | 
    
         
            +
            }
         
     | 
| 
      
 68 
     | 
    
         
            +
            for ns, uri in XML_NS.items():
         
     | 
| 
      
 69 
     | 
    
         
            +
                ET.register_namespace(ns, uri)
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            def _wTag(tag: str) -> str:
         
     | 
| 
      
 73 
     | 
    
         
            +
                """Assemble namespace and tag name for standard w namespace."""
         
     | 
| 
      
 74 
     | 
    
         
            +
                return f"{{{OOXML_SCM}/wordprocessingml/2006/main}}{tag}"
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
            def _mkTag(ns: str, tag: str) -> str:
         
     | 
| 
      
 78 
     | 
    
         
            +
                """Assemble namespace and tag name."""
         
     | 
| 
      
 79 
     | 
    
         
            +
                if uri := XML_NS.get(ns, ""):
         
     | 
| 
      
 80 
     | 
    
         
            +
                    return f"{{{uri}}}{tag}"
         
     | 
| 
      
 81 
     | 
    
         
            +
                logger.warning("Missing xml namespace '%s'", ns)
         
     | 
| 
      
 82 
     | 
    
         
            +
                return tag
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            def _docXCol(color: QColor) -> str:
         
     | 
| 
      
 86 
     | 
    
         
            +
                """Format a QColor as the DocX accepted value."""
         
     | 
| 
      
 87 
     | 
    
         
            +
                return color.name(QtHexRgb).lstrip("#")
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            def _wText(parent: ET.Element, text: str) -> ET.Element:
         
     | 
| 
      
 91 
     | 
    
         
            +
                """Create a text element and add the preserve flag if necessary."""
         
     | 
| 
      
 92 
     | 
    
         
            +
                attrib = {}
         
     | 
| 
      
 93 
     | 
    
         
            +
                if len(text) > len(text.strip()):
         
     | 
| 
      
 94 
     | 
    
         
            +
                    attrib[_mkTag("xml", "space")] = "preserve"
         
     | 
| 
      
 95 
     | 
    
         
            +
                return xmlSubElem(parent, _wTag("t"), text, attrib=attrib)
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            # Cached
         
     | 
| 
      
 99 
     | 
    
         
            +
            W_VAL = _wTag("val")
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
            # Formatting Codes
         
     | 
| 
      
 102 
     | 
    
         
            +
            X_BLD = 0x001  # Bold format
         
     | 
| 
      
 103 
     | 
    
         
            +
            X_ITA = 0x002  # Italic format
         
     | 
| 
      
 104 
     | 
    
         
            +
            X_DEL = 0x004  # Strikethrough format
         
     | 
| 
      
 105 
     | 
    
         
            +
            X_UND = 0x008  # Underline format
         
     | 
| 
      
 106 
     | 
    
         
            +
            X_MRK = 0x010  # Marked format
         
     | 
| 
      
 107 
     | 
    
         
            +
            X_SUP = 0x020  # Superscript
         
     | 
| 
      
 108 
     | 
    
         
            +
            X_SUB = 0x040  # Subscript
         
     | 
| 
      
 109 
     | 
    
         
            +
            X_COL = 0x080  # Coloured text
         
     | 
| 
      
 110 
     | 
    
         
            +
            X_HRF = 0x100  # Link
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            # Formatting Masks
         
     | 
| 
      
 113 
     | 
    
         
            +
            M_BLD = ~X_BLD
         
     | 
| 
      
 114 
     | 
    
         
            +
            M_ITA = ~X_ITA
         
     | 
| 
      
 115 
     | 
    
         
            +
            M_DEL = ~X_DEL
         
     | 
| 
      
 116 
     | 
    
         
            +
            M_UND = ~X_UND
         
     | 
| 
      
 117 
     | 
    
         
            +
            M_MRK = ~X_MRK
         
     | 
| 
      
 118 
     | 
    
         
            +
            M_SUP = ~X_SUP
         
     | 
| 
      
 119 
     | 
    
         
            +
            M_SUB = ~X_SUB
         
     | 
| 
      
 120 
     | 
    
         
            +
            M_COL = ~X_COL
         
     | 
| 
      
 121 
     | 
    
         
            +
            M_HRF = ~X_HRF
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
            # DocX Styles
         
     | 
| 
      
 124 
     | 
    
         
            +
            S_NORM  = "Normal"
         
     | 
| 
      
 125 
     | 
    
         
            +
            S_TITLE = "Title"
         
     | 
| 
      
 126 
     | 
    
         
            +
            S_HEAD1 = "Heading1"
         
     | 
| 
      
 127 
     | 
    
         
            +
            S_HEAD2 = "Heading2"
         
     | 
| 
      
 128 
     | 
    
         
            +
            S_HEAD3 = "Heading3"
         
     | 
| 
      
 129 
     | 
    
         
            +
            S_HEAD4 = "Heading4"
         
     | 
| 
      
 130 
     | 
    
         
            +
            S_SEP   = "Separator"
         
     | 
| 
      
 131 
     | 
    
         
            +
            S_META  = "MetaText"
         
     | 
| 
      
 132 
     | 
    
         
            +
            S_HEAD  = "Header"
         
     | 
| 
      
 133 
     | 
    
         
            +
            S_FNOTE = "FootnoteText"
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
            class DocXXmlRel(NamedTuple):
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                rId: str
         
     | 
| 
      
 139 
     | 
    
         
            +
                relType: str
         
     | 
| 
      
 140 
     | 
    
         
            +
                targetMode: str | None = None
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
            class DocXXmlFile(NamedTuple):
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                xml: ET.Element
         
     | 
| 
      
 146 
     | 
    
         
            +
                path: str
         
     | 
| 
      
 147 
     | 
    
         
            +
                contentType: str
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
            class DocXParStyle(NamedTuple):
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                name: str
         
     | 
| 
      
 153 
     | 
    
         
            +
                styleId: str
         
     | 
| 
      
 154 
     | 
    
         
            +
                size: float
         
     | 
| 
      
 155 
     | 
    
         
            +
                basedOn: str | None = None
         
     | 
| 
      
 156 
     | 
    
         
            +
                nextStyle: str | None = None
         
     | 
| 
      
 157 
     | 
    
         
            +
                before: float | None = None
         
     | 
| 
      
 158 
     | 
    
         
            +
                after: float | None = None
         
     | 
| 
      
 159 
     | 
    
         
            +
                left: float | None = None
         
     | 
| 
      
 160 
     | 
    
         
            +
                line: float | None = None
         
     | 
| 
      
 161 
     | 
    
         
            +
                indentFirst: float | None = None
         
     | 
| 
      
 162 
     | 
    
         
            +
                align: str | None = None
         
     | 
| 
      
 163 
     | 
    
         
            +
                default: bool = False
         
     | 
| 
      
 164 
     | 
    
         
            +
                level: int | None = None
         
     | 
| 
      
 165 
     | 
    
         
            +
                color: str | None = None
         
     | 
| 
      
 166 
     | 
    
         
            +
                bold: bool = False
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
            class ToDocX(Tokenizer):
         
     | 
| 
      
 170 
     | 
    
         
            +
                """Core: DocX Document Writer
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                Extend the Tokenizer class to writer DocX Document files.
         
     | 
| 
      
 173 
     | 
    
         
            +
                """
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                def __init__(self, project: NWProject) -> None:
         
     | 
| 
      
 176 
     | 
    
         
            +
                    super().__init__(project)
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                    # Properties
         
     | 
| 
      
 179 
     | 
    
         
            +
                    self._headerFormat = ""
         
     | 
| 
      
 180 
     | 
    
         
            +
                    self._pageOffset   = 0
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                    # Internal
         
     | 
| 
      
 183 
     | 
    
         
            +
                    self._fontFamily  = "Liberation Serif"
         
     | 
| 
      
 184 
     | 
    
         
            +
                    self._fontSize    = 12.0
         
     | 
| 
      
 185 
     | 
    
         
            +
                    self._pageSize    = QSizeF(210.0, 297.0)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    self._pageMargins = QMarginsF(20.0, 20.0, 20.0, 20.0)
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                    # Data Variables
         
     | 
| 
      
 189 
     | 
    
         
            +
                    self._pars: list[DocXParagraph] = []
         
     | 
| 
      
 190 
     | 
    
         
            +
                    self._rels: dict[str, DocXXmlRel] = {}
         
     | 
| 
      
 191 
     | 
    
         
            +
                    self._files: dict[str, DocXXmlFile] = {}
         
     | 
| 
      
 192 
     | 
    
         
            +
                    self._styles: dict[str, DocXParStyle] = {}
         
     | 
| 
      
 193 
     | 
    
         
            +
                    self._usedNotes: dict[str, int] = {}
         
     | 
| 
      
 194 
     | 
    
         
            +
                    self._usedFields: list[tuple[ET.Element, str]] = []
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                    return
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                ##
         
     | 
| 
      
 199 
     | 
    
         
            +
                #  Setters
         
     | 
| 
      
 200 
     | 
    
         
            +
                ##
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                def setPageLayout(
         
     | 
| 
      
 203 
     | 
    
         
            +
                    self, width: float, height: float, top: float, bottom: float, left: float, right: float
         
     | 
| 
      
 204 
     | 
    
         
            +
                ) -> None:
         
     | 
| 
      
 205 
     | 
    
         
            +
                    """Set the document page size and margins in millimetres."""
         
     | 
| 
      
 206 
     | 
    
         
            +
                    self._pageSize = QSizeF(width, height)
         
     | 
| 
      
 207 
     | 
    
         
            +
                    self._pageMargins = QMarginsF(left, top, right, bottom)
         
     | 
| 
      
 208 
     | 
    
         
            +
                    return
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                def setHeaderFormat(self, format: str, offset: int) -> None:
         
     | 
| 
      
 211 
     | 
    
         
            +
                    """Set the document header format."""
         
     | 
| 
      
 212 
     | 
    
         
            +
                    self._headerFormat = format.strip()
         
     | 
| 
      
 213 
     | 
    
         
            +
                    self._pageOffset = offset
         
     | 
| 
      
 214 
     | 
    
         
            +
                    return
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                ##
         
     | 
| 
      
 217 
     | 
    
         
            +
                #  Class Methods
         
     | 
| 
      
 218 
     | 
    
         
            +
                ##
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                def initDocument(self) -> None:
         
     | 
| 
      
 221 
     | 
    
         
            +
                    """Initialises the DocX document structure."""
         
     | 
| 
      
 222 
     | 
    
         
            +
                    super().initDocument()
         
     | 
| 
      
 223 
     | 
    
         
            +
                    self._fontFamily = self._textFont.family()
         
     | 
| 
      
 224 
     | 
    
         
            +
                    self._fontSize = self._textFont.pointSizeF()
         
     | 
| 
      
 225 
     | 
    
         
            +
                    self._generateStyles()
         
     | 
| 
      
 226 
     | 
    
         
            +
                    return
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                def doConvert(self) -> None:
         
     | 
| 
      
 229 
     | 
    
         
            +
                    """Convert the list of text tokens into XML elements."""
         
     | 
| 
      
 230 
     | 
    
         
            +
                    bIndent = self._fontSize * self._blockIndent
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
                    for tType, _, tText, tFormat, tStyle in self._blocks:
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                        # Create Paragraph
         
     | 
| 
      
 235 
     | 
    
         
            +
                        par = DocXParagraph()
         
     | 
| 
      
 236 
     | 
    
         
            +
                        self._pars.append(par)
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
      
 238 
     | 
    
         
            +
                        # Styles
         
     | 
| 
      
 239 
     | 
    
         
            +
                        if tStyle & BlockFmt.LEFT:
         
     | 
| 
      
 240 
     | 
    
         
            +
                            par.setAlignment("left")
         
     | 
| 
      
 241 
     | 
    
         
            +
                        elif tStyle & BlockFmt.RIGHT:
         
     | 
| 
      
 242 
     | 
    
         
            +
                            par.setAlignment("right")
         
     | 
| 
      
 243 
     | 
    
         
            +
                        elif tStyle & BlockFmt.CENTRE:
         
     | 
| 
      
 244 
     | 
    
         
            +
                            par.setAlignment("center")
         
     | 
| 
      
 245 
     | 
    
         
            +
                        elif tStyle & BlockFmt.JUSTIFY:
         
     | 
| 
      
 246 
     | 
    
         
            +
                            par.setAlignment("both")
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                        if tStyle & BlockFmt.PBB:
         
     | 
| 
      
 249 
     | 
    
         
            +
                            par.setPageBreakBefore(True)
         
     | 
| 
      
 250 
     | 
    
         
            +
                        if tStyle & BlockFmt.PBA:
         
     | 
| 
      
 251 
     | 
    
         
            +
                            par.setPageBreakAfter(True)
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                        if tStyle & BlockFmt.Z_BTM:
         
     | 
| 
      
 254 
     | 
    
         
            +
                            par.setMarginBottom(0.0)
         
     | 
| 
      
 255 
     | 
    
         
            +
                        if tStyle & BlockFmt.Z_TOP:
         
     | 
| 
      
 256 
     | 
    
         
            +
                            par.setMarginTop(0.0)
         
     | 
| 
      
 257 
     | 
    
         
            +
             
     | 
| 
      
 258 
     | 
    
         
            +
                        if tStyle & BlockFmt.IND_T:
         
     | 
| 
      
 259 
     | 
    
         
            +
                            par.setIndentFirst(True)
         
     | 
| 
      
 260 
     | 
    
         
            +
                        if tStyle & BlockFmt.IND_L:
         
     | 
| 
      
 261 
     | 
    
         
            +
                            par.setMarginLeft(bIndent)
         
     | 
| 
      
 262 
     | 
    
         
            +
                        if tStyle & BlockFmt.IND_R:
         
     | 
| 
      
 263 
     | 
    
         
            +
                            par.setMarginRight(bIndent)
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                        # Process Text Types
         
     | 
| 
      
 266 
     | 
    
         
            +
                        if tType == BlockTyp.TEXT:
         
     | 
| 
      
 267 
     | 
    
         
            +
                            self._processFragments(par, S_NORM, tText, tFormat)
         
     | 
| 
      
 268 
     | 
    
         
            +
             
     | 
| 
      
 269 
     | 
    
         
            +
                        elif tType == BlockTyp.TITLE:
         
     | 
| 
      
 270 
     | 
    
         
            +
                            self._processFragments(par, S_TITLE, tText, tFormat)
         
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
      
 272 
     | 
    
         
            +
                        elif tType == BlockTyp.HEAD1:
         
     | 
| 
      
 273 
     | 
    
         
            +
                            self._processFragments(par, S_HEAD1, tText, tFormat)
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
                        elif tType == BlockTyp.HEAD2:
         
     | 
| 
      
 276 
     | 
    
         
            +
                            self._processFragments(par, S_HEAD2, tText, tFormat)
         
     | 
| 
      
 277 
     | 
    
         
            +
             
     | 
| 
      
 278 
     | 
    
         
            +
                        elif tType == BlockTyp.HEAD3:
         
     | 
| 
      
 279 
     | 
    
         
            +
                            self._processFragments(par, S_HEAD3, tText, tFormat)
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
                        elif tType == BlockTyp.HEAD4:
         
     | 
| 
      
 282 
     | 
    
         
            +
                            self._processFragments(par, S_HEAD4, tText, tFormat)
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
                        elif tType == BlockTyp.SEP:
         
     | 
| 
      
 285 
     | 
    
         
            +
                            self._processFragments(par, S_SEP, tText)
         
     | 
| 
      
 286 
     | 
    
         
            +
             
     | 
| 
      
 287 
     | 
    
         
            +
                        elif tType == BlockTyp.SKIP:
         
     | 
| 
      
 288 
     | 
    
         
            +
                            self._processFragments(par, S_NORM, "")
         
     | 
| 
      
 289 
     | 
    
         
            +
             
     | 
| 
      
 290 
     | 
    
         
            +
                        elif tType == BlockTyp.COMMENT:
         
     | 
| 
      
 291 
     | 
    
         
            +
                            self._processFragments(par, S_META, tText, tFormat)
         
     | 
| 
      
 292 
     | 
    
         
            +
             
     | 
| 
      
 293 
     | 
    
         
            +
                        elif tType == BlockTyp.KEYWORD:
         
     | 
| 
      
 294 
     | 
    
         
            +
                            self._processFragments(par, S_META, tText, tFormat)
         
     | 
| 
      
 295 
     | 
    
         
            +
             
     | 
| 
      
 296 
     | 
    
         
            +
                    return
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
                def closeDocument(self) -> None:
         
     | 
| 
      
 299 
     | 
    
         
            +
                    """Generate all the XML."""
         
     | 
| 
      
 300 
     | 
    
         
            +
                    self._coreXml()
         
     | 
| 
      
 301 
     | 
    
         
            +
                    self._appXml()
         
     | 
| 
      
 302 
     | 
    
         
            +
                    self._stylesXml()
         
     | 
| 
      
 303 
     | 
    
         
            +
                    self._fontTableXml()
         
     | 
| 
      
 304 
     | 
    
         
            +
             
     | 
| 
      
 305 
     | 
    
         
            +
                    fId = None
         
     | 
| 
      
 306 
     | 
    
         
            +
                    dId = None
         
     | 
| 
      
 307 
     | 
    
         
            +
                    if self._headerFormat:
         
     | 
| 
      
 308 
     | 
    
         
            +
                        dId = self._defaultHeaderXml()
         
     | 
| 
      
 309 
     | 
    
         
            +
                        fId = self._firstHeaderXml()
         
     | 
| 
      
 310 
     | 
    
         
            +
             
     | 
| 
      
 311 
     | 
    
         
            +
                    self._documentXml(fId, dId)
         
     | 
| 
      
 312 
     | 
    
         
            +
                    self._settingsXml()
         
     | 
| 
      
 313 
     | 
    
         
            +
                    if self._usedNotes:
         
     | 
| 
      
 314 
     | 
    
         
            +
                        self._footnotesXml()
         
     | 
| 
      
 315 
     | 
    
         
            +
             
     | 
| 
      
 316 
     | 
    
         
            +
                    return
         
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
                def saveDocument(self, path: Path) -> None:
         
     | 
| 
      
 319 
     | 
    
         
            +
                    """Save the data to a .docx file."""
         
     | 
| 
      
 320 
     | 
    
         
            +
                    # Content Lists
         
     | 
| 
      
 321 
     | 
    
         
            +
                    cExts: list[tuple[str, str]] = []
         
     | 
| 
      
 322 
     | 
    
         
            +
                    cExts.append(("xml", "application/xml"))
         
     | 
| 
      
 323 
     | 
    
         
            +
                    cExts.append(("rels", RELS_TYPE))
         
     | 
| 
      
 324 
     | 
    
         
            +
             
     | 
| 
      
 325 
     | 
    
         
            +
                    cDocs: list[tuple[str, str]] = []
         
     | 
| 
      
 326 
     | 
    
         
            +
                    cDocs.append(("/_rels/.rels", RELS_TYPE))
         
     | 
| 
      
 327 
     | 
    
         
            +
                    cDocs.append(("/word/_rels/document.xml.rels", RELS_TYPE))
         
     | 
| 
      
 328 
     | 
    
         
            +
             
     | 
| 
      
 329 
     | 
    
         
            +
                    # Relationships XML
         
     | 
| 
      
 330 
     | 
    
         
            +
                    rRels = xmlElement("Relationships", attrib={
         
     | 
| 
      
 331 
     | 
    
         
            +
                        "xmlns": f"{OOXML_SCM}/package/2006/relationships"
         
     | 
| 
      
 332 
     | 
    
         
            +
                    })
         
     | 
| 
      
 333 
     | 
    
         
            +
                    wRels = xmlElement("Relationships", attrib={
         
     | 
| 
      
 334 
     | 
    
         
            +
                        "xmlns": f"{OOXML_SCM}/package/2006/relationships"
         
     | 
| 
      
 335 
     | 
    
         
            +
                    })
         
     | 
| 
      
 336 
     | 
    
         
            +
                    for name, rel in self._rels.items():
         
     | 
| 
      
 337 
     | 
    
         
            +
                        isRoot = name in ("core.xml", "app.xml", "document.xml")
         
     | 
| 
      
 338 
     | 
    
         
            +
                        if xml := self._files.get(name):
         
     | 
| 
      
 339 
     | 
    
         
            +
                            target = f"{xml.path}/{name}" if isRoot else name
         
     | 
| 
      
 340 
     | 
    
         
            +
                            cDocs.append((f"/{xml.path}/{name}", xml.contentType))
         
     | 
| 
      
 341 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 342 
     | 
    
         
            +
                            target = name
         
     | 
| 
      
 343 
     | 
    
         
            +
                        attrib = {"Id": rel.rId, "Type": rel.relType, "Target": target}
         
     | 
| 
      
 344 
     | 
    
         
            +
                        if rel.targetMode:
         
     | 
| 
      
 345 
     | 
    
         
            +
                            attrib["TargetMode"] = rel.targetMode
         
     | 
| 
      
 346 
     | 
    
         
            +
                        xmlSubElem(rRels if isRoot else wRels, "Relationship", attrib=attrib)
         
     | 
| 
      
 347 
     | 
    
         
            +
             
     | 
| 
      
 348 
     | 
    
         
            +
                    # Content Types XML
         
     | 
| 
      
 349 
     | 
    
         
            +
                    dTypes = xmlElement("Types", attrib={
         
     | 
| 
      
 350 
     | 
    
         
            +
                        "xmlns": f"{OOXML_SCM}/package/2006/content-types"
         
     | 
| 
      
 351 
     | 
    
         
            +
                    })
         
     | 
| 
      
 352 
     | 
    
         
            +
                    for name, content in cExts:
         
     | 
| 
      
 353 
     | 
    
         
            +
                        xmlSubElem(dTypes, "Default", attrib={"Extension": name, "ContentType": content})
         
     | 
| 
      
 354 
     | 
    
         
            +
                    for name, content in cDocs:
         
     | 
| 
      
 355 
     | 
    
         
            +
                        xmlSubElem(dTypes, "Override", attrib={"PartName": name, "ContentType": content})
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
                    def xmlToZip(name: str, root: ET.Element, zipObj: ZipFile) -> None:
         
     | 
| 
      
 358 
     | 
    
         
            +
                        zipObj.writestr(name, ET.tostring(root, encoding="utf-8", xml_declaration=True))
         
     | 
| 
      
 359 
     | 
    
         
            +
             
     | 
| 
      
 360 
     | 
    
         
            +
                    with ZipFile(path, mode="w", compression=ZIP_DEFLATED, compresslevel=3) as outZip:
         
     | 
| 
      
 361 
     | 
    
         
            +
                        xmlToZip("_rels/.rels", rRels, outZip)
         
     | 
| 
      
 362 
     | 
    
         
            +
                        xmlToZip("word/_rels/document.xml.rels", wRels, outZip)
         
     | 
| 
      
 363 
     | 
    
         
            +
                        for name, rel in self._files.items():
         
     | 
| 
      
 364 
     | 
    
         
            +
                            xmlToZip(f"{rel.path}/{name}", rel.xml, outZip)
         
     | 
| 
      
 365 
     | 
    
         
            +
                        xmlToZip("[Content_Types].xml", dTypes, outZip)
         
     | 
| 
      
 366 
     | 
    
         
            +
             
     | 
| 
      
 367 
     | 
    
         
            +
                    return
         
     | 
| 
      
 368 
     | 
    
         
            +
             
     | 
| 
      
 369 
     | 
    
         
            +
                ##
         
     | 
| 
      
 370 
     | 
    
         
            +
                #  Internal Functions
         
     | 
| 
      
 371 
     | 
    
         
            +
                ##
         
     | 
| 
      
 372 
     | 
    
         
            +
             
     | 
| 
      
 373 
     | 
    
         
            +
                def _processFragments(
         
     | 
| 
      
 374 
     | 
    
         
            +
                    self, par: DocXParagraph, pStyle: str, text: str, tFmt: T_Formats | None = None
         
     | 
| 
      
 375 
     | 
    
         
            +
                ) -> None:
         
     | 
| 
      
 376 
     | 
    
         
            +
                    """Apply formatting tags to text."""
         
     | 
| 
      
 377 
     | 
    
         
            +
                    par.setStyle(self._styles.get(pStyle))
         
     | 
| 
      
 378 
     | 
    
         
            +
                    xFmt = 0x00
         
     | 
| 
      
 379 
     | 
    
         
            +
                    xNode = None
         
     | 
| 
      
 380 
     | 
    
         
            +
                    fStart = 0
         
     | 
| 
      
 381 
     | 
    
         
            +
                    fLink = ""
         
     | 
| 
      
 382 
     | 
    
         
            +
                    fClass = ""
         
     | 
| 
      
 383 
     | 
    
         
            +
                    for fPos, fFmt, fData in tFmt or []:
         
     | 
| 
      
 384 
     | 
    
         
            +
             
     | 
| 
      
 385 
     | 
    
         
            +
                        if xNode is not None:
         
     | 
| 
      
 386 
     | 
    
         
            +
                            par.addContent(xNode)
         
     | 
| 
      
 387 
     | 
    
         
            +
                            xNode = None
         
     | 
| 
      
 388 
     | 
    
         
            +
             
     | 
| 
      
 389 
     | 
    
         
            +
                        if temp := text[fStart:fPos]:
         
     | 
| 
      
 390 
     | 
    
         
            +
                            par.addContent(self._textRunToXml(temp, xFmt, fClass, fLink))
         
     | 
| 
      
 391 
     | 
    
         
            +
             
     | 
| 
      
 392 
     | 
    
         
            +
                        if fFmt == TextFmt.B_B:
         
     | 
| 
      
 393 
     | 
    
         
            +
                            xFmt |= X_BLD
         
     | 
| 
      
 394 
     | 
    
         
            +
                        elif fFmt == TextFmt.B_E:
         
     | 
| 
      
 395 
     | 
    
         
            +
                            xFmt &= M_BLD
         
     | 
| 
      
 396 
     | 
    
         
            +
                        elif fFmt == TextFmt.I_B:
         
     | 
| 
      
 397 
     | 
    
         
            +
                            xFmt |= X_ITA
         
     | 
| 
      
 398 
     | 
    
         
            +
                        elif fFmt == TextFmt.I_E:
         
     | 
| 
      
 399 
     | 
    
         
            +
                            xFmt &= M_ITA
         
     | 
| 
      
 400 
     | 
    
         
            +
                        elif fFmt == TextFmt.D_B:
         
     | 
| 
      
 401 
     | 
    
         
            +
                            xFmt |= X_DEL
         
     | 
| 
      
 402 
     | 
    
         
            +
                        elif fFmt == TextFmt.D_E:
         
     | 
| 
      
 403 
     | 
    
         
            +
                            xFmt &= M_DEL
         
     | 
| 
      
 404 
     | 
    
         
            +
                        elif fFmt == TextFmt.U_B:
         
     | 
| 
      
 405 
     | 
    
         
            +
                            xFmt |= X_UND
         
     | 
| 
      
 406 
     | 
    
         
            +
                        elif fFmt == TextFmt.U_E:
         
     | 
| 
      
 407 
     | 
    
         
            +
                            xFmt &= M_UND
         
     | 
| 
      
 408 
     | 
    
         
            +
                        elif fFmt == TextFmt.M_B:
         
     | 
| 
      
 409 
     | 
    
         
            +
                            xFmt |= X_MRK
         
     | 
| 
      
 410 
     | 
    
         
            +
                        elif fFmt == TextFmt.M_E:
         
     | 
| 
      
 411 
     | 
    
         
            +
                            xFmt &= M_MRK
         
     | 
| 
      
 412 
     | 
    
         
            +
                        elif fFmt == TextFmt.SUP_B:
         
     | 
| 
      
 413 
     | 
    
         
            +
                            xFmt |= X_SUP
         
     | 
| 
      
 414 
     | 
    
         
            +
                        elif fFmt == TextFmt.SUP_E:
         
     | 
| 
      
 415 
     | 
    
         
            +
                            xFmt &= M_SUP
         
     | 
| 
      
 416 
     | 
    
         
            +
                        elif fFmt == TextFmt.SUB_B:
         
     | 
| 
      
 417 
     | 
    
         
            +
                            xFmt |= X_SUB
         
     | 
| 
      
 418 
     | 
    
         
            +
                        elif fFmt == TextFmt.SUB_E:
         
     | 
| 
      
 419 
     | 
    
         
            +
                            xFmt &= M_SUB
         
     | 
| 
      
 420 
     | 
    
         
            +
                        elif fFmt == TextFmt.COL_B:
         
     | 
| 
      
 421 
     | 
    
         
            +
                            xFmt |= X_COL
         
     | 
| 
      
 422 
     | 
    
         
            +
                            fClass = fData
         
     | 
| 
      
 423 
     | 
    
         
            +
                        elif fFmt == TextFmt.COL_E:
         
     | 
| 
      
 424 
     | 
    
         
            +
                            xFmt &= M_COL
         
     | 
| 
      
 425 
     | 
    
         
            +
                            fClass = ""
         
     | 
| 
      
 426 
     | 
    
         
            +
                        elif fFmt == TextFmt.HRF_B:
         
     | 
| 
      
 427 
     | 
    
         
            +
                            xFmt |= X_HRF
         
     | 
| 
      
 428 
     | 
    
         
            +
                            fLink = fData
         
     | 
| 
      
 429 
     | 
    
         
            +
                        elif fFmt == TextFmt.HRF_E:
         
     | 
| 
      
 430 
     | 
    
         
            +
                            xFmt &= M_HRF
         
     | 
| 
      
 431 
     | 
    
         
            +
                            fLink = ""
         
     | 
| 
      
 432 
     | 
    
         
            +
                        elif fFmt == TextFmt.FNOTE:
         
     | 
| 
      
 433 
     | 
    
         
            +
                            xNode = self._generateFootnote(fData)
         
     | 
| 
      
 434 
     | 
    
         
            +
                        elif fFmt == TextFmt.FIELD:
         
     | 
| 
      
 435 
     | 
    
         
            +
                            xNode = self._generateField(fData, xFmt)
         
     | 
| 
      
 436 
     | 
    
         
            +
                        elif fFmt == TextFmt.STRIP:
         
     | 
| 
      
 437 
     | 
    
         
            +
                            pass
         
     | 
| 
      
 438 
     | 
    
         
            +
             
     | 
| 
      
 439 
     | 
    
         
            +
                        # Move pos for next pass
         
     | 
| 
      
 440 
     | 
    
         
            +
                        fStart = fPos
         
     | 
| 
      
 441 
     | 
    
         
            +
             
     | 
| 
      
 442 
     | 
    
         
            +
                    if xNode is not None:
         
     | 
| 
      
 443 
     | 
    
         
            +
                        par.addContent(xNode)
         
     | 
| 
      
 444 
     | 
    
         
            +
             
     | 
| 
      
 445 
     | 
    
         
            +
                    if temp := text[fStart:]:
         
     | 
| 
      
 446 
     | 
    
         
            +
                        par.addContent(self._textRunToXml(temp, xFmt, fClass, fLink))
         
     | 
| 
      
 447 
     | 
    
         
            +
             
     | 
| 
      
 448 
     | 
    
         
            +
                    return
         
     | 
| 
      
 449 
     | 
    
         
            +
             
     | 
| 
      
 450 
     | 
    
         
            +
                def _textRunToXml(self, text: str | None, fmt: int, fClass: str, fLink: str) -> ET.Element:
         
     | 
| 
      
 451 
     | 
    
         
            +
                    """Encode the text run into XML."""
         
     | 
| 
      
 452 
     | 
    
         
            +
                    xR = xmlElement(_wTag("r"))
         
     | 
| 
      
 453 
     | 
    
         
            +
                    rPr = xmlSubElem(xR, _wTag("rPr"))
         
     | 
| 
      
 454 
     | 
    
         
            +
                    if fmt & X_BLD:
         
     | 
| 
      
 455 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("b"))
         
     | 
| 
      
 456 
     | 
    
         
            +
                    if fmt & X_ITA:
         
     | 
| 
      
 457 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("i"))
         
     | 
| 
      
 458 
     | 
    
         
            +
                    if fmt & X_UND:
         
     | 
| 
      
 459 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("u"), attrib={W_VAL: "single"})
         
     | 
| 
      
 460 
     | 
    
         
            +
                    if fmt & X_MRK:
         
     | 
| 
      
 461 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("shd"), attrib={
         
     | 
| 
      
 462 
     | 
    
         
            +
                            _wTag("fill"): _docXCol(self._theme.highlight), W_VAL: "clear",
         
     | 
| 
      
 463 
     | 
    
         
            +
                        })
         
     | 
| 
      
 464 
     | 
    
         
            +
                    if fmt & X_DEL:
         
     | 
| 
      
 465 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("strike"))
         
     | 
| 
      
 466 
     | 
    
         
            +
                    if fmt & X_SUP:
         
     | 
| 
      
 467 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("vertAlign"), attrib={W_VAL: "superscript"})
         
     | 
| 
      
 468 
     | 
    
         
            +
                    if fmt & X_SUB:
         
     | 
| 
      
 469 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("vertAlign"), attrib={W_VAL: "subscript"})
         
     | 
| 
      
 470 
     | 
    
         
            +
                    if fmt & X_COL and (color := self._classes.get(fClass)):
         
     | 
| 
      
 471 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("color"), attrib={W_VAL: _docXCol(color)})
         
     | 
| 
      
 472 
     | 
    
         
            +
             
     | 
| 
      
 473 
     | 
    
         
            +
                    if isinstance(text, str):
         
     | 
| 
      
 474 
     | 
    
         
            +
                        for segment in RX_TEXT.split(text):
         
     | 
| 
      
 475 
     | 
    
         
            +
                            if segment == "\n":
         
     | 
| 
      
 476 
     | 
    
         
            +
                                xmlSubElem(xR, _wTag("br"))
         
     | 
| 
      
 477 
     | 
    
         
            +
                            elif segment == "\t":
         
     | 
| 
      
 478 
     | 
    
         
            +
                                xmlSubElem(xR, _wTag("tab"))
         
     | 
| 
      
 479 
     | 
    
         
            +
                            elif segment:
         
     | 
| 
      
 480 
     | 
    
         
            +
                                _wText(xR, segment)
         
     | 
| 
      
 481 
     | 
    
         
            +
             
     | 
| 
      
 482 
     | 
    
         
            +
                    if fmt & X_HRF and fLink:
         
     | 
| 
      
 483 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("rStyle"), attrib={W_VAL: "InternetLink"})
         
     | 
| 
      
 484 
     | 
    
         
            +
                        xH = xmlElement(_wTag("hyperlink"), attrib={
         
     | 
| 
      
 485 
     | 
    
         
            +
                            _mkTag("r", "id"): self._appendExternalRel(fLink),
         
     | 
| 
      
 486 
     | 
    
         
            +
                        })
         
     | 
| 
      
 487 
     | 
    
         
            +
                        xH.append(xR)
         
     | 
| 
      
 488 
     | 
    
         
            +
                        return xH
         
     | 
| 
      
 489 
     | 
    
         
            +
             
     | 
| 
      
 490 
     | 
    
         
            +
                    return xR
         
     | 
| 
      
 491 
     | 
    
         
            +
             
     | 
| 
      
 492 
     | 
    
         
            +
                ##
         
     | 
| 
      
 493 
     | 
    
         
            +
                #  DocX Content
         
     | 
| 
      
 494 
     | 
    
         
            +
                ##
         
     | 
| 
      
 495 
     | 
    
         
            +
             
     | 
| 
      
 496 
     | 
    
         
            +
                def _generateFootnote(self, key: str) -> ET.Element | None:
         
     | 
| 
      
 497 
     | 
    
         
            +
                    """Generate a footnote XML object."""
         
     | 
| 
      
 498 
     | 
    
         
            +
                    if key in self._footnotes:
         
     | 
| 
      
 499 
     | 
    
         
            +
                        idx = len(self._usedNotes) + 1
         
     | 
| 
      
 500 
     | 
    
         
            +
                        xR = xmlElement(_wTag("r"))
         
     | 
| 
      
 501 
     | 
    
         
            +
                        rPr = xmlSubElem(xR, _wTag("rPr"))
         
     | 
| 
      
 502 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("vertAlign"), attrib={W_VAL: "superscript"})
         
     | 
| 
      
 503 
     | 
    
         
            +
                        xmlSubElem(xR, _wTag("footnoteReference"), attrib={_wTag("id"): str(idx)})
         
     | 
| 
      
 504 
     | 
    
         
            +
                        self._usedNotes[key] = idx
         
     | 
| 
      
 505 
     | 
    
         
            +
                        return xR
         
     | 
| 
      
 506 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 507 
     | 
    
         
            +
             
     | 
| 
      
 508 
     | 
    
         
            +
                def _generateField(self, key: str, fmt: int) -> ET.Element | None:
         
     | 
| 
      
 509 
     | 
    
         
            +
                    """Generate a data field XML object."""
         
     | 
| 
      
 510 
     | 
    
         
            +
                    if key and (field := key.partition(":")[2]):
         
     | 
| 
      
 511 
     | 
    
         
            +
                        xR = self._textRunToXml(None, fmt, "", "")
         
     | 
| 
      
 512 
     | 
    
         
            +
                        xT = _wText(xR, "0")
         
     | 
| 
      
 513 
     | 
    
         
            +
                        self._usedFields.append((xT, field))
         
     | 
| 
      
 514 
     | 
    
         
            +
                        return xR
         
     | 
| 
      
 515 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 516 
     | 
    
         
            +
             
     | 
| 
      
 517 
     | 
    
         
            +
                def _generateStyles(self) -> None:
         
     | 
| 
      
 518 
     | 
    
         
            +
                    """Generate usable styles."""
         
     | 
| 
      
 519 
     | 
    
         
            +
                    styles: list[DocXParStyle] = []
         
     | 
| 
      
 520 
     | 
    
         
            +
             
     | 
| 
      
 521 
     | 
    
         
            +
                    hScale = self._scaleHeads
         
     | 
| 
      
 522 
     | 
    
         
            +
                    hColor = _docXCol(self._theme.head) if self._colorHeads else None
         
     | 
| 
      
 523 
     | 
    
         
            +
                    fSz = self._fontSize
         
     | 
| 
      
 524 
     | 
    
         
            +
                    fnSz = 0.8 * self._fontSize
         
     | 
| 
      
 525 
     | 
    
         
            +
                    fSz0 = (nwStyles.H_SIZES[0] * fSz) if hScale else fSz
         
     | 
| 
      
 526 
     | 
    
         
            +
                    fSz1 = (nwStyles.H_SIZES[1] * fSz) if hScale else fSz
         
     | 
| 
      
 527 
     | 
    
         
            +
                    fSz2 = (nwStyles.H_SIZES[2] * fSz) if hScale else fSz
         
     | 
| 
      
 528 
     | 
    
         
            +
                    fSz3 = (nwStyles.H_SIZES[3] * fSz) if hScale else fSz
         
     | 
| 
      
 529 
     | 
    
         
            +
                    fSz4 = (nwStyles.H_SIZES[4] * fSz) if hScale else fSz
         
     | 
| 
      
 530 
     | 
    
         
            +
             
     | 
| 
      
 531 
     | 
    
         
            +
                    # Add Normal Style
         
     | 
| 
      
 532 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 533 
     | 
    
         
            +
                        name="Normal",
         
     | 
| 
      
 534 
     | 
    
         
            +
                        styleId=S_NORM,
         
     | 
| 
      
 535 
     | 
    
         
            +
                        size=fSz,
         
     | 
| 
      
 536 
     | 
    
         
            +
                        default=True,
         
     | 
| 
      
 537 
     | 
    
         
            +
                        before=fSz * self._marginText[0],
         
     | 
| 
      
 538 
     | 
    
         
            +
                        after=fSz * self._marginText[1],
         
     | 
| 
      
 539 
     | 
    
         
            +
                        line=fSz * self._lineHeight,
         
     | 
| 
      
 540 
     | 
    
         
            +
                        indentFirst=fSz * self._firstWidth,
         
     | 
| 
      
 541 
     | 
    
         
            +
                        align=self._defaultAlign,
         
     | 
| 
      
 542 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 543 
     | 
    
         
            +
             
     | 
| 
      
 544 
     | 
    
         
            +
                    # Add Title
         
     | 
| 
      
 545 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 546 
     | 
    
         
            +
                        name="Title",
         
     | 
| 
      
 547 
     | 
    
         
            +
                        styleId=S_TITLE,
         
     | 
| 
      
 548 
     | 
    
         
            +
                        size=fSz0,
         
     | 
| 
      
 549 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 550 
     | 
    
         
            +
                        nextStyle=S_NORM,
         
     | 
| 
      
 551 
     | 
    
         
            +
                        before=fSz * self._marginTitle[0],
         
     | 
| 
      
 552 
     | 
    
         
            +
                        after=fSz * self._marginTitle[1],
         
     | 
| 
      
 553 
     | 
    
         
            +
                        line=fSz0 * self._lineHeight,
         
     | 
| 
      
 554 
     | 
    
         
            +
                        level=0,
         
     | 
| 
      
 555 
     | 
    
         
            +
                        bold=self._boldHeads,
         
     | 
| 
      
 556 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 557 
     | 
    
         
            +
             
     | 
| 
      
 558 
     | 
    
         
            +
                    # Add Heading 1
         
     | 
| 
      
 559 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 560 
     | 
    
         
            +
                        name="Heading 1",
         
     | 
| 
      
 561 
     | 
    
         
            +
                        styleId=S_HEAD1,
         
     | 
| 
      
 562 
     | 
    
         
            +
                        size=fSz1,
         
     | 
| 
      
 563 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 564 
     | 
    
         
            +
                        nextStyle=S_NORM,
         
     | 
| 
      
 565 
     | 
    
         
            +
                        before=fSz * self._marginHead1[0],
         
     | 
| 
      
 566 
     | 
    
         
            +
                        after=fSz * self._marginHead1[1],
         
     | 
| 
      
 567 
     | 
    
         
            +
                        line=fSz1 * self._lineHeight,
         
     | 
| 
      
 568 
     | 
    
         
            +
                        level=0,
         
     | 
| 
      
 569 
     | 
    
         
            +
                        color=hColor,
         
     | 
| 
      
 570 
     | 
    
         
            +
                        bold=self._boldHeads,
         
     | 
| 
      
 571 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 572 
     | 
    
         
            +
             
     | 
| 
      
 573 
     | 
    
         
            +
                    # Add Heading 2
         
     | 
| 
      
 574 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 575 
     | 
    
         
            +
                        name="Heading 2",
         
     | 
| 
      
 576 
     | 
    
         
            +
                        styleId=S_HEAD2,
         
     | 
| 
      
 577 
     | 
    
         
            +
                        size=fSz2,
         
     | 
| 
      
 578 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 579 
     | 
    
         
            +
                        nextStyle=S_NORM,
         
     | 
| 
      
 580 
     | 
    
         
            +
                        before=fSz * self._marginHead2[0],
         
     | 
| 
      
 581 
     | 
    
         
            +
                        after=fSz * self._marginHead2[1],
         
     | 
| 
      
 582 
     | 
    
         
            +
                        line=fSz2 * self._lineHeight,
         
     | 
| 
      
 583 
     | 
    
         
            +
                        level=1,
         
     | 
| 
      
 584 
     | 
    
         
            +
                        color=hColor,
         
     | 
| 
      
 585 
     | 
    
         
            +
                        bold=self._boldHeads,
         
     | 
| 
      
 586 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 587 
     | 
    
         
            +
             
     | 
| 
      
 588 
     | 
    
         
            +
                    # Add Heading 3
         
     | 
| 
      
 589 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 590 
     | 
    
         
            +
                        name="Heading 3",
         
     | 
| 
      
 591 
     | 
    
         
            +
                        styleId=S_HEAD3,
         
     | 
| 
      
 592 
     | 
    
         
            +
                        size=fSz3,
         
     | 
| 
      
 593 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 594 
     | 
    
         
            +
                        nextStyle=S_NORM,
         
     | 
| 
      
 595 
     | 
    
         
            +
                        before=fSz * self._marginHead3[0],
         
     | 
| 
      
 596 
     | 
    
         
            +
                        after=fSz * self._marginHead3[1],
         
     | 
| 
      
 597 
     | 
    
         
            +
                        line=fSz3 * self._lineHeight,
         
     | 
| 
      
 598 
     | 
    
         
            +
                        level=1,
         
     | 
| 
      
 599 
     | 
    
         
            +
                        color=hColor,
         
     | 
| 
      
 600 
     | 
    
         
            +
                        bold=self._boldHeads,
         
     | 
| 
      
 601 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 602 
     | 
    
         
            +
             
     | 
| 
      
 603 
     | 
    
         
            +
                    # Add Heading 4
         
     | 
| 
      
 604 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 605 
     | 
    
         
            +
                        name="Heading 4",
         
     | 
| 
      
 606 
     | 
    
         
            +
                        styleId=S_HEAD4,
         
     | 
| 
      
 607 
     | 
    
         
            +
                        size=fSz4,
         
     | 
| 
      
 608 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 609 
     | 
    
         
            +
                        nextStyle=S_NORM,
         
     | 
| 
      
 610 
     | 
    
         
            +
                        before=fSz * self._marginHead4[0],
         
     | 
| 
      
 611 
     | 
    
         
            +
                        after=fSz * self._marginHead4[1],
         
     | 
| 
      
 612 
     | 
    
         
            +
                        line=fSz4 * self._lineHeight,
         
     | 
| 
      
 613 
     | 
    
         
            +
                        level=1,
         
     | 
| 
      
 614 
     | 
    
         
            +
                        color=hColor,
         
     | 
| 
      
 615 
     | 
    
         
            +
                        bold=self._boldHeads,
         
     | 
| 
      
 616 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 617 
     | 
    
         
            +
             
     | 
| 
      
 618 
     | 
    
         
            +
                    # Add Separator
         
     | 
| 
      
 619 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 620 
     | 
    
         
            +
                        name="Separator",
         
     | 
| 
      
 621 
     | 
    
         
            +
                        styleId=S_SEP,
         
     | 
| 
      
 622 
     | 
    
         
            +
                        size=fSz,
         
     | 
| 
      
 623 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 624 
     | 
    
         
            +
                        nextStyle=S_NORM,
         
     | 
| 
      
 625 
     | 
    
         
            +
                        before=fSz * self._marginSep[0],
         
     | 
| 
      
 626 
     | 
    
         
            +
                        after=fSz * self._marginSep[1],
         
     | 
| 
      
 627 
     | 
    
         
            +
                        line=fSz * self._lineHeight,
         
     | 
| 
      
 628 
     | 
    
         
            +
                        align="center",
         
     | 
| 
      
 629 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 630 
     | 
    
         
            +
             
     | 
| 
      
 631 
     | 
    
         
            +
                    # Add Text Meta Style
         
     | 
| 
      
 632 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 633 
     | 
    
         
            +
                        name="Meta Text",
         
     | 
| 
      
 634 
     | 
    
         
            +
                        styleId=S_META,
         
     | 
| 
      
 635 
     | 
    
         
            +
                        size=fSz,
         
     | 
| 
      
 636 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 637 
     | 
    
         
            +
                        nextStyle=S_NORM,
         
     | 
| 
      
 638 
     | 
    
         
            +
                        before=fSz * self._marginMeta[0],
         
     | 
| 
      
 639 
     | 
    
         
            +
                        after=fSz * self._marginMeta[1],
         
     | 
| 
      
 640 
     | 
    
         
            +
                        line=fSz * self._lineHeight,
         
     | 
| 
      
 641 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 642 
     | 
    
         
            +
             
     | 
| 
      
 643 
     | 
    
         
            +
                    # Header
         
     | 
| 
      
 644 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 645 
     | 
    
         
            +
                        name="Header",
         
     | 
| 
      
 646 
     | 
    
         
            +
                        styleId=S_HEAD,
         
     | 
| 
      
 647 
     | 
    
         
            +
                        size=fSz,
         
     | 
| 
      
 648 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 649 
     | 
    
         
            +
                        align="right",
         
     | 
| 
      
 650 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 651 
     | 
    
         
            +
             
     | 
| 
      
 652 
     | 
    
         
            +
                    # Footnote
         
     | 
| 
      
 653 
     | 
    
         
            +
                    styles.append(DocXParStyle(
         
     | 
| 
      
 654 
     | 
    
         
            +
                        name="Footnote Text",
         
     | 
| 
      
 655 
     | 
    
         
            +
                        styleId=S_FNOTE,
         
     | 
| 
      
 656 
     | 
    
         
            +
                        size=fnSz,
         
     | 
| 
      
 657 
     | 
    
         
            +
                        basedOn=S_NORM,
         
     | 
| 
      
 658 
     | 
    
         
            +
                        before=0.0,
         
     | 
| 
      
 659 
     | 
    
         
            +
                        after=fnSz * self._marginFoot[1],
         
     | 
| 
      
 660 
     | 
    
         
            +
                        left=fnSz * self._marginFoot[0],
         
     | 
| 
      
 661 
     | 
    
         
            +
                        line=fnSz * self._lineHeight,
         
     | 
| 
      
 662 
     | 
    
         
            +
                    ))
         
     | 
| 
      
 663 
     | 
    
         
            +
             
     | 
| 
      
 664 
     | 
    
         
            +
                    # Add to Cache
         
     | 
| 
      
 665 
     | 
    
         
            +
                    for style in styles:
         
     | 
| 
      
 666 
     | 
    
         
            +
                        self._styles[style.styleId] = style
         
     | 
| 
      
 667 
     | 
    
         
            +
             
     | 
| 
      
 668 
     | 
    
         
            +
                    return
         
     | 
| 
      
 669 
     | 
    
         
            +
             
     | 
| 
      
 670 
     | 
    
         
            +
                def _nextRelId(self) -> str:
         
     | 
| 
      
 671 
     | 
    
         
            +
                    """Generate the next unique rId."""
         
     | 
| 
      
 672 
     | 
    
         
            +
                    return f"rId{len(self._rels) + 1}"
         
     | 
| 
      
 673 
     | 
    
         
            +
             
     | 
| 
      
 674 
     | 
    
         
            +
                def _appendExternalRel(self, target: str) -> str:
         
     | 
| 
      
 675 
     | 
    
         
            +
                    """Append external rel to the registry."""
         
     | 
| 
      
 676 
     | 
    
         
            +
                    if rel := self._rels.get(target):
         
     | 
| 
      
 677 
     | 
    
         
            +
                        return rel.rId
         
     | 
| 
      
 678 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 679 
     | 
    
         
            +
                    self._rels[target] = DocXXmlRel(
         
     | 
| 
      
 680 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 681 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/hyperlink",
         
     | 
| 
      
 682 
     | 
    
         
            +
                        targetMode="External"
         
     | 
| 
      
 683 
     | 
    
         
            +
                    )
         
     | 
| 
      
 684 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 685 
     | 
    
         
            +
             
     | 
| 
      
 686 
     | 
    
         
            +
                def _appXml(self) -> str:
         
     | 
| 
      
 687 
     | 
    
         
            +
                    """Populate app.xml."""
         
     | 
| 
      
 688 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 689 
     | 
    
         
            +
                    xRoot = xmlElement("Properties", attrib={
         
     | 
| 
      
 690 
     | 
    
         
            +
                        "xmlns": f"{OOXML_SCM}/officeDocument/2006/extended-properties"
         
     | 
| 
      
 691 
     | 
    
         
            +
                    })
         
     | 
| 
      
 692 
     | 
    
         
            +
                    self._rels["app.xml"] = DocXXmlRel(
         
     | 
| 
      
 693 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 694 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/extended-properties",
         
     | 
| 
      
 695 
     | 
    
         
            +
                    )
         
     | 
| 
      
 696 
     | 
    
         
            +
                    self._files["app.xml"] = DocXXmlFile(
         
     | 
| 
      
 697 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 698 
     | 
    
         
            +
                        path="docProps",
         
     | 
| 
      
 699 
     | 
    
         
            +
                        contentType="application/vnd.openxmlformats-officedocument.extended-properties+xml",
         
     | 
| 
      
 700 
     | 
    
         
            +
                    )
         
     | 
| 
      
 701 
     | 
    
         
            +
             
     | 
| 
      
 702 
     | 
    
         
            +
                    xmlSubElem(xRoot, "TotalTime", self._project.data.editTime // 60)
         
     | 
| 
      
 703 
     | 
    
         
            +
                    xmlSubElem(xRoot, "Application", f"novelWriter/{__version__}")
         
     | 
| 
      
 704 
     | 
    
         
            +
                    if count := self._counts.get("allWords"):
         
     | 
| 
      
 705 
     | 
    
         
            +
                        xmlSubElem(xRoot, "Words", count)
         
     | 
| 
      
 706 
     | 
    
         
            +
                    if count := self._counts.get("textWordChars"):
         
     | 
| 
      
 707 
     | 
    
         
            +
                        xmlSubElem(xRoot, "Characters", count)
         
     | 
| 
      
 708 
     | 
    
         
            +
                    if count := self._counts.get("textChars"):
         
     | 
| 
      
 709 
     | 
    
         
            +
                        xmlSubElem(xRoot, "CharactersWithSpaces", count)
         
     | 
| 
      
 710 
     | 
    
         
            +
                    if count := self._counts.get("paragraphCount"):
         
     | 
| 
      
 711 
     | 
    
         
            +
                        xmlSubElem(xRoot, "Paragraphs", count)
         
     | 
| 
      
 712 
     | 
    
         
            +
             
     | 
| 
      
 713 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 714 
     | 
    
         
            +
             
     | 
| 
      
 715 
     | 
    
         
            +
                def _coreXml(self) -> str:
         
     | 
| 
      
 716 
     | 
    
         
            +
                    """Populate app.xml."""
         
     | 
| 
      
 717 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 718 
     | 
    
         
            +
                    xRoot = xmlElement(_mkTag("cp", "coreProperties"))
         
     | 
| 
      
 719 
     | 
    
         
            +
                    self._rels["core.xml"] = DocXXmlRel(
         
     | 
| 
      
 720 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 721 
     | 
    
         
            +
                        relType=f"{OOXML_SCM}/package/2006/relationships/metadata/core-properties",
         
     | 
| 
      
 722 
     | 
    
         
            +
                    )
         
     | 
| 
      
 723 
     | 
    
         
            +
                    self._files["core.xml"] = DocXXmlFile(
         
     | 
| 
      
 724 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 725 
     | 
    
         
            +
                        path="docProps",
         
     | 
| 
      
 726 
     | 
    
         
            +
                        contentType="application/vnd.openxmlformats-package.core-properties+xml",
         
     | 
| 
      
 727 
     | 
    
         
            +
                    )
         
     | 
| 
      
 728 
     | 
    
         
            +
             
     | 
| 
      
 729 
     | 
    
         
            +
                    timeStamp = datetime.now().isoformat(sep="T", timespec="seconds")
         
     | 
| 
      
 730 
     | 
    
         
            +
                    tsAttr = {_mkTag("xsi", "type"): "dcterms:W3CDTF"}
         
     | 
| 
      
 731 
     | 
    
         
            +
                    xmlSubElem(xRoot, _mkTag("dcterms", "created"), timeStamp, attrib=tsAttr)
         
     | 
| 
      
 732 
     | 
    
         
            +
                    xmlSubElem(xRoot, _mkTag("dcterms", "modified"), timeStamp, attrib=tsAttr)
         
     | 
| 
      
 733 
     | 
    
         
            +
                    xmlSubElem(xRoot, _mkTag("dc", "creator"), self._project.data.author)
         
     | 
| 
      
 734 
     | 
    
         
            +
                    xmlSubElem(xRoot, _mkTag("dc", "title"), self._project.data.name)
         
     | 
| 
      
 735 
     | 
    
         
            +
                    xmlSubElem(xRoot, _mkTag("dc", "language"), self._dLocale.name())
         
     | 
| 
      
 736 
     | 
    
         
            +
                    xmlSubElem(xRoot, _mkTag("cp", "revision"), str(self._project.data.saveCount))
         
     | 
| 
      
 737 
     | 
    
         
            +
                    xmlSubElem(xRoot, _mkTag("cp", "lastModifiedBy"), self._project.data.author)
         
     | 
| 
      
 738 
     | 
    
         
            +
             
     | 
| 
      
 739 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 740 
     | 
    
         
            +
             
     | 
| 
      
 741 
     | 
    
         
            +
                def _stylesXml(self) -> str:
         
     | 
| 
      
 742 
     | 
    
         
            +
                    """Populate styles.xml."""
         
     | 
| 
      
 743 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 744 
     | 
    
         
            +
                    xRoot = xmlElement(_wTag("styles"))
         
     | 
| 
      
 745 
     | 
    
         
            +
                    self._rels["styles.xml"] = DocXXmlRel(
         
     | 
| 
      
 746 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 747 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/styles",
         
     | 
| 
      
 748 
     | 
    
         
            +
                    )
         
     | 
| 
      
 749 
     | 
    
         
            +
                    self._files["styles.xml"] = DocXXmlFile(
         
     | 
| 
      
 750 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 751 
     | 
    
         
            +
                        path="word",
         
     | 
| 
      
 752 
     | 
    
         
            +
                        contentType=f"{WORD_BASE}.styles+xml",
         
     | 
| 
      
 753 
     | 
    
         
            +
                    )
         
     | 
| 
      
 754 
     | 
    
         
            +
             
     | 
| 
      
 755 
     | 
    
         
            +
                    # Default Style
         
     | 
| 
      
 756 
     | 
    
         
            +
                    xStyl = xmlSubElem(xRoot, _wTag("docDefaults"))
         
     | 
| 
      
 757 
     | 
    
         
            +
                    xRDef = xmlSubElem(xStyl, _wTag("rPrDefault"))
         
     | 
| 
      
 758 
     | 
    
         
            +
                    xPDef = xmlSubElem(xStyl, _wTag("pPrDefault"))
         
     | 
| 
      
 759 
     | 
    
         
            +
                    xRPr = xmlSubElem(xRDef, _wTag("rPr"))
         
     | 
| 
      
 760 
     | 
    
         
            +
                    xPPr = xmlSubElem(xPDef, _wTag("pPr"))
         
     | 
| 
      
 761 
     | 
    
         
            +
             
     | 
| 
      
 762 
     | 
    
         
            +
                    size = str(int(2.0 * self._fontSize))
         
     | 
| 
      
 763 
     | 
    
         
            +
                    line = str(int(20.0 * self._lineHeight * self._fontSize))
         
     | 
| 
      
 764 
     | 
    
         
            +
             
     | 
| 
      
 765 
     | 
    
         
            +
                    xmlSubElem(xRPr, _wTag("rFonts"), attrib={
         
     | 
| 
      
 766 
     | 
    
         
            +
                        _wTag("ascii"): self._fontFamily,
         
     | 
| 
      
 767 
     | 
    
         
            +
                        _wTag("hAnsi"): self._fontFamily,
         
     | 
| 
      
 768 
     | 
    
         
            +
                        _wTag("cs"): self._fontFamily,
         
     | 
| 
      
 769 
     | 
    
         
            +
                    })
         
     | 
| 
      
 770 
     | 
    
         
            +
                    xmlSubElem(xRPr, _wTag("sz"), attrib={W_VAL: size})
         
     | 
| 
      
 771 
     | 
    
         
            +
                    xmlSubElem(xRPr, _wTag("szCs"), attrib={W_VAL: size})
         
     | 
| 
      
 772 
     | 
    
         
            +
                    xmlSubElem(xRPr, _wTag("lang"), attrib={W_VAL: self._dLocale.name()})
         
     | 
| 
      
 773 
     | 
    
         
            +
                    xmlSubElem(xPPr, _wTag("spacing"), attrib={_wTag("line"): line})
         
     | 
| 
      
 774 
     | 
    
         
            +
             
     | 
| 
      
 775 
     | 
    
         
            +
                    # Paragraph Styles
         
     | 
| 
      
 776 
     | 
    
         
            +
                    for style in self._styles.values():
         
     | 
| 
      
 777 
     | 
    
         
            +
                        sAttr = {}
         
     | 
| 
      
 778 
     | 
    
         
            +
                        sAttr[_wTag("type")] = "paragraph"
         
     | 
| 
      
 779 
     | 
    
         
            +
                        sAttr[_wTag("styleId")] = style.styleId
         
     | 
| 
      
 780 
     | 
    
         
            +
                        if style.default:
         
     | 
| 
      
 781 
     | 
    
         
            +
                            sAttr[_wTag("default")] = "1"
         
     | 
| 
      
 782 
     | 
    
         
            +
             
     | 
| 
      
 783 
     | 
    
         
            +
                        size = firstFloat(style.size, self._fontSize)
         
     | 
| 
      
 784 
     | 
    
         
            +
             
     | 
| 
      
 785 
     | 
    
         
            +
                        xStyl = xmlSubElem(xRoot, _wTag("style"), attrib=sAttr)
         
     | 
| 
      
 786 
     | 
    
         
            +
                        xmlSubElem(xStyl, _wTag("name"), attrib={W_VAL: style.name})
         
     | 
| 
      
 787 
     | 
    
         
            +
                        if style.basedOn:
         
     | 
| 
      
 788 
     | 
    
         
            +
                            xmlSubElem(xStyl, _wTag("basedOn"), attrib={W_VAL: style.basedOn})
         
     | 
| 
      
 789 
     | 
    
         
            +
                        if style.nextStyle:
         
     | 
| 
      
 790 
     | 
    
         
            +
                            xmlSubElem(xStyl, _wTag("next"), attrib={W_VAL: style.nextStyle})
         
     | 
| 
      
 791 
     | 
    
         
            +
             
     | 
| 
      
 792 
     | 
    
         
            +
                        # pPr Node
         
     | 
| 
      
 793 
     | 
    
         
            +
                        pPr = xmlSubElem(xStyl, _wTag("pPr"))
         
     | 
| 
      
 794 
     | 
    
         
            +
                        xmlSubElem(pPr, _wTag("spacing"), attrib={
         
     | 
| 
      
 795 
     | 
    
         
            +
                            _wTag("before"): str(int(20.0 * firstFloat(style.before))),
         
     | 
| 
      
 796 
     | 
    
         
            +
                            _wTag("after"): str(int(20.0 * firstFloat(style.after))),
         
     | 
| 
      
 797 
     | 
    
         
            +
                            _wTag("line"): str(int(20.0 * firstFloat(style.line, size))),
         
     | 
| 
      
 798 
     | 
    
         
            +
                        })
         
     | 
| 
      
 799 
     | 
    
         
            +
                        if style.left is not None:
         
     | 
| 
      
 800 
     | 
    
         
            +
                            xmlSubElem(pPr, _wTag("ind"), attrib={
         
     | 
| 
      
 801 
     | 
    
         
            +
                                _wTag("left"): str(int(20.0 * style.left)),
         
     | 
| 
      
 802 
     | 
    
         
            +
                            })
         
     | 
| 
      
 803 
     | 
    
         
            +
                        if style.align:
         
     | 
| 
      
 804 
     | 
    
         
            +
                            xmlSubElem(pPr, _wTag("jc"), attrib={W_VAL: style.align})
         
     | 
| 
      
 805 
     | 
    
         
            +
                        if style.level is not None:
         
     | 
| 
      
 806 
     | 
    
         
            +
                            xmlSubElem(pPr, _wTag("outlineLvl"), attrib={W_VAL: str(style.level)})
         
     | 
| 
      
 807 
     | 
    
         
            +
             
     | 
| 
      
 808 
     | 
    
         
            +
                        # rPr Node
         
     | 
| 
      
 809 
     | 
    
         
            +
                        rPr = xmlSubElem(xStyl, _wTag("rPr"))
         
     | 
| 
      
 810 
     | 
    
         
            +
                        if style.bold:
         
     | 
| 
      
 811 
     | 
    
         
            +
                            xmlSubElem(rPr, _wTag("b"))
         
     | 
| 
      
 812 
     | 
    
         
            +
                        if style.color:
         
     | 
| 
      
 813 
     | 
    
         
            +
                            xmlSubElem(rPr, _wTag("color"), attrib={W_VAL: style.color})
         
     | 
| 
      
 814 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("sz"), attrib={W_VAL: str(int(2.0 * size))})
         
     | 
| 
      
 815 
     | 
    
         
            +
                        xmlSubElem(rPr, _wTag("szCs"), attrib={W_VAL: str(int(2.0 * size))})
         
     | 
| 
      
 816 
     | 
    
         
            +
             
     | 
| 
      
 817 
     | 
    
         
            +
                    # Character Style
         
     | 
| 
      
 818 
     | 
    
         
            +
                    xStyl = xmlSubElem(xRoot, _wTag("style"), attrib={
         
     | 
| 
      
 819 
     | 
    
         
            +
                        _wTag("type"): "character",
         
     | 
| 
      
 820 
     | 
    
         
            +
                        _wTag("styleId"): "InternetLink"
         
     | 
| 
      
 821 
     | 
    
         
            +
                    })
         
     | 
| 
      
 822 
     | 
    
         
            +
                    xmlSubElem(xStyl, _wTag("name"), attrib={W_VAL: "Hyperlink"})
         
     | 
| 
      
 823 
     | 
    
         
            +
                    rPr = xmlSubElem(xStyl, _wTag("rPr"))
         
     | 
| 
      
 824 
     | 
    
         
            +
                    xmlSubElem(rPr, _wTag("color"), attrib={W_VAL: _docXCol(self._theme.link)})
         
     | 
| 
      
 825 
     | 
    
         
            +
                    xmlSubElem(rPr, _wTag("u"), attrib={W_VAL: "single"})
         
     | 
| 
      
 826 
     | 
    
         
            +
             
     | 
| 
      
 827 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 828 
     | 
    
         
            +
             
     | 
| 
      
 829 
     | 
    
         
            +
                def _defaultHeaderXml(self) -> str:
         
     | 
| 
      
 830 
     | 
    
         
            +
                    """Populate header1.xml."""
         
     | 
| 
      
 831 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 832 
     | 
    
         
            +
                    xRoot = xmlElement(_wTag("hdr"))
         
     | 
| 
      
 833 
     | 
    
         
            +
                    self._rels["header1.xml"] = DocXXmlRel(
         
     | 
| 
      
 834 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 835 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/header",
         
     | 
| 
      
 836 
     | 
    
         
            +
                    )
         
     | 
| 
      
 837 
     | 
    
         
            +
                    self._files["header1.xml"] = DocXXmlFile(
         
     | 
| 
      
 838 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 839 
     | 
    
         
            +
                        path="word",
         
     | 
| 
      
 840 
     | 
    
         
            +
                        contentType=f"{WORD_BASE}.header+xml",
         
     | 
| 
      
 841 
     | 
    
         
            +
                    )
         
     | 
| 
      
 842 
     | 
    
         
            +
             
     | 
| 
      
 843 
     | 
    
         
            +
                    xP = xmlSubElem(xRoot, _wTag("p"))
         
     | 
| 
      
 844 
     | 
    
         
            +
                    xPPr = xmlSubElem(xP, _wTag("pPr"))
         
     | 
| 
      
 845 
     | 
    
         
            +
                    xmlSubElem(xPPr, _wTag("pStyle"), attrib={W_VAL: S_HEAD})
         
     | 
| 
      
 846 
     | 
    
         
            +
                    xmlSubElem(xPPr, _wTag("jc"), attrib={W_VAL: "right"})
         
     | 
| 
      
 847 
     | 
    
         
            +
                    xmlSubElem(xPPr, _wTag("rPr"))
         
     | 
| 
      
 848 
     | 
    
         
            +
             
     | 
| 
      
 849 
     | 
    
         
            +
                    pre, page, post = self._headerFormat.partition(nwHeadFmt.DOC_PAGE)
         
     | 
| 
      
 850 
     | 
    
         
            +
                    pre = pre.replace(nwHeadFmt.DOC_PROJECT, self._project.data.name)
         
     | 
| 
      
 851 
     | 
    
         
            +
                    pre = pre.replace(nwHeadFmt.DOC_AUTHOR, self._project.data.author)
         
     | 
| 
      
 852 
     | 
    
         
            +
                    post = post.replace(nwHeadFmt.DOC_PROJECT, self._project.data.name)
         
     | 
| 
      
 853 
     | 
    
         
            +
                    post = post.replace(nwHeadFmt.DOC_AUTHOR, self._project.data.author)
         
     | 
| 
      
 854 
     | 
    
         
            +
             
     | 
| 
      
 855 
     | 
    
         
            +
                    wFldCT = _wTag("fldCharType")
         
     | 
| 
      
 856 
     | 
    
         
            +
                    if pre:
         
     | 
| 
      
 857 
     | 
    
         
            +
                        xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 858 
     | 
    
         
            +
                        _wText(xR, pre)
         
     | 
| 
      
 859 
     | 
    
         
            +
                    if page:
         
     | 
| 
      
 860 
     | 
    
         
            +
                        xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 861 
     | 
    
         
            +
                        xmlSubElem(xR, _wTag("fldChar"), attrib={wFldCT: "begin"})
         
     | 
| 
      
 862 
     | 
    
         
            +
                        xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 863 
     | 
    
         
            +
                        _wText(xR, " PAGE ")
         
     | 
| 
      
 864 
     | 
    
         
            +
                        xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 865 
     | 
    
         
            +
                        xmlSubElem(xR, _wTag("fldChar"), attrib={wFldCT: "separate"})
         
     | 
| 
      
 866 
     | 
    
         
            +
                        xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 867 
     | 
    
         
            +
                        _wText(xR, "0")
         
     | 
| 
      
 868 
     | 
    
         
            +
                        xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 869 
     | 
    
         
            +
                        xmlSubElem(xR, _wTag("fldChar"), attrib={wFldCT: "end"})
         
     | 
| 
      
 870 
     | 
    
         
            +
                    if post:
         
     | 
| 
      
 871 
     | 
    
         
            +
                        xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 872 
     | 
    
         
            +
                        _wText(xR, post)
         
     | 
| 
      
 873 
     | 
    
         
            +
             
     | 
| 
      
 874 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 875 
     | 
    
         
            +
             
     | 
| 
      
 876 
     | 
    
         
            +
                def _firstHeaderXml(self) -> str:
         
     | 
| 
      
 877 
     | 
    
         
            +
                    """Populate header2.xml."""
         
     | 
| 
      
 878 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 879 
     | 
    
         
            +
                    xRoot = xmlElement(_wTag("hdr"))
         
     | 
| 
      
 880 
     | 
    
         
            +
                    self._rels["header2.xml"] = DocXXmlRel(
         
     | 
| 
      
 881 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 882 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/header",
         
     | 
| 
      
 883 
     | 
    
         
            +
                    )
         
     | 
| 
      
 884 
     | 
    
         
            +
                    self._files["header2.xml"] = DocXXmlFile(
         
     | 
| 
      
 885 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 886 
     | 
    
         
            +
                        path="word",
         
     | 
| 
      
 887 
     | 
    
         
            +
                        contentType=f"{WORD_BASE}.header+xml",
         
     | 
| 
      
 888 
     | 
    
         
            +
                    )
         
     | 
| 
      
 889 
     | 
    
         
            +
             
     | 
| 
      
 890 
     | 
    
         
            +
                    xP = xmlSubElem(xRoot, _wTag("p"))
         
     | 
| 
      
 891 
     | 
    
         
            +
                    xPPr = xmlSubElem(xP, _wTag("pPr"))
         
     | 
| 
      
 892 
     | 
    
         
            +
                    xmlSubElem(xPPr, _wTag("pStyle"), attrib={W_VAL: S_HEAD})
         
     | 
| 
      
 893 
     | 
    
         
            +
                    xmlSubElem(xPPr, _wTag("jc"), attrib={W_VAL: "right"})
         
     | 
| 
      
 894 
     | 
    
         
            +
                    xmlSubElem(xPPr, _wTag("rPr"))
         
     | 
| 
      
 895 
     | 
    
         
            +
             
     | 
| 
      
 896 
     | 
    
         
            +
                    xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 897 
     | 
    
         
            +
                    xmlSubElem(xR, _wTag("rPr"))
         
     | 
| 
      
 898 
     | 
    
         
            +
             
     | 
| 
      
 899 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 900 
     | 
    
         
            +
             
     | 
| 
      
 901 
     | 
    
         
            +
                def _documentXml(self, hFirst: str | None, hDefault: str | None) -> str:
         
     | 
| 
      
 902 
     | 
    
         
            +
                    """Populate document.xml."""
         
     | 
| 
      
 903 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 904 
     | 
    
         
            +
                    xRoot = xmlElement(_wTag("document"))
         
     | 
| 
      
 905 
     | 
    
         
            +
                    xBody = xmlSubElem(xRoot, _wTag("body"))
         
     | 
| 
      
 906 
     | 
    
         
            +
                    self._rels["document.xml"] = DocXXmlRel(
         
     | 
| 
      
 907 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 908 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/officeDocument",
         
     | 
| 
      
 909 
     | 
    
         
            +
                    )
         
     | 
| 
      
 910 
     | 
    
         
            +
                    self._files["document.xml"] = DocXXmlFile(
         
     | 
| 
      
 911 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 912 
     | 
    
         
            +
                        path="word",
         
     | 
| 
      
 913 
     | 
    
         
            +
                        contentType=f"{WORD_BASE}.document.main+xml",
         
     | 
| 
      
 914 
     | 
    
         
            +
                    )
         
     | 
| 
      
 915 
     | 
    
         
            +
             
     | 
| 
      
 916 
     | 
    
         
            +
                    # Map all Page Break Before to After where possible
         
     | 
| 
      
 917 
     | 
    
         
            +
                    pars: list[DocXParagraph] = []
         
     | 
| 
      
 918 
     | 
    
         
            +
                    for i, par in enumerate(self._pars):
         
     | 
| 
      
 919 
     | 
    
         
            +
                        if i > 0 and par.pageBreakBefore:
         
     | 
| 
      
 920 
     | 
    
         
            +
                            prev = self._pars[i-1]
         
     | 
| 
      
 921 
     | 
    
         
            +
                            par.setPageBreakBefore(False)
         
     | 
| 
      
 922 
     | 
    
         
            +
                            prev.setPageBreakAfter(True)
         
     | 
| 
      
 923 
     | 
    
         
            +
             
     | 
| 
      
 924 
     | 
    
         
            +
                        pars.append(par)
         
     | 
| 
      
 925 
     | 
    
         
            +
             
     | 
| 
      
 926 
     | 
    
         
            +
                    # Replace fields if there are stats available
         
     | 
| 
      
 927 
     | 
    
         
            +
                    if self._usedFields and self._counts:
         
     | 
| 
      
 928 
     | 
    
         
            +
                        for xField, field in self._usedFields:
         
     | 
| 
      
 929 
     | 
    
         
            +
                            if (value := self._counts.get(field)) is not None:
         
     | 
| 
      
 930 
     | 
    
         
            +
                                xField.text = self._formatInt(value)
         
     | 
| 
      
 931 
     | 
    
         
            +
             
     | 
| 
      
 932 
     | 
    
         
            +
                    # Write Paragraphs
         
     | 
| 
      
 933 
     | 
    
         
            +
                    for par in pars:
         
     | 
| 
      
 934 
     | 
    
         
            +
                        par.toXml(xBody)
         
     | 
| 
      
 935 
     | 
    
         
            +
             
     | 
| 
      
 936 
     | 
    
         
            +
                    def szScale(value: float) -> str:
         
     | 
| 
      
 937 
     | 
    
         
            +
                        return str(int(value*2.0*72.0/2.54))
         
     | 
| 
      
 938 
     | 
    
         
            +
             
     | 
| 
      
 939 
     | 
    
         
            +
                    # Write Settings
         
     | 
| 
      
 940 
     | 
    
         
            +
                    xSect = xmlSubElem(xBody, _wTag("sectPr"))
         
     | 
| 
      
 941 
     | 
    
         
            +
                    if hFirst and hDefault:
         
     | 
| 
      
 942 
     | 
    
         
            +
                        xmlSubElem(xSect, _wTag("headerReference"), attrib={
         
     | 
| 
      
 943 
     | 
    
         
            +
                            _wTag("type"): "first", _mkTag("r", "id"): hFirst,
         
     | 
| 
      
 944 
     | 
    
         
            +
                        })
         
     | 
| 
      
 945 
     | 
    
         
            +
                        xmlSubElem(xSect, _wTag("headerReference"), attrib={
         
     | 
| 
      
 946 
     | 
    
         
            +
                            _wTag("type"): "default", _mkTag("r", "id"): hDefault,
         
     | 
| 
      
 947 
     | 
    
         
            +
                        })
         
     | 
| 
      
 948 
     | 
    
         
            +
             
     | 
| 
      
 949 
     | 
    
         
            +
                    xFn = xmlSubElem(xSect, _wTag("footnotePr"))
         
     | 
| 
      
 950 
     | 
    
         
            +
                    xmlSubElem(xFn, _wTag("numFmt"), attrib={
         
     | 
| 
      
 951 
     | 
    
         
            +
                        W_VAL: "decimal",
         
     | 
| 
      
 952 
     | 
    
         
            +
                    })
         
     | 
| 
      
 953 
     | 
    
         
            +
             
     | 
| 
      
 954 
     | 
    
         
            +
                    xmlSubElem(xSect, _wTag("pgSz"), attrib={
         
     | 
| 
      
 955 
     | 
    
         
            +
                        _wTag("w"): szScale(self._pageSize.width()),
         
     | 
| 
      
 956 
     | 
    
         
            +
                        _wTag("h"): szScale(self._pageSize.height()),
         
     | 
| 
      
 957 
     | 
    
         
            +
                        _wTag("orient"): "portrait",
         
     | 
| 
      
 958 
     | 
    
         
            +
                    })
         
     | 
| 
      
 959 
     | 
    
         
            +
                    xmlSubElem(xSect, _wTag("pgMar"), attrib={
         
     | 
| 
      
 960 
     | 
    
         
            +
                        _wTag("top"): szScale(self._pageMargins.top()),
         
     | 
| 
      
 961 
     | 
    
         
            +
                        _wTag("right"): szScale(self._pageMargins.right()),
         
     | 
| 
      
 962 
     | 
    
         
            +
                        _wTag("bottom"): szScale(self._pageMargins.bottom()),
         
     | 
| 
      
 963 
     | 
    
         
            +
                        _wTag("left"): szScale(self._pageMargins.left()),
         
     | 
| 
      
 964 
     | 
    
         
            +
                        _wTag("header"): szScale(self._pageMargins.top()/2.0),
         
     | 
| 
      
 965 
     | 
    
         
            +
                        _wTag("footer"): "0",
         
     | 
| 
      
 966 
     | 
    
         
            +
                        _wTag("gutter"): "0",
         
     | 
| 
      
 967 
     | 
    
         
            +
                    })
         
     | 
| 
      
 968 
     | 
    
         
            +
                    xmlSubElem(xSect, _wTag("pgNumType"), attrib={
         
     | 
| 
      
 969 
     | 
    
         
            +
                        _wTag("start"): str(1 - self._pageOffset),
         
     | 
| 
      
 970 
     | 
    
         
            +
                        _wTag("fmt"): "decimal",
         
     | 
| 
      
 971 
     | 
    
         
            +
                    })
         
     | 
| 
      
 972 
     | 
    
         
            +
                    xmlSubElem(xSect, _wTag("titlePg"))
         
     | 
| 
      
 973 
     | 
    
         
            +
             
     | 
| 
      
 974 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 975 
     | 
    
         
            +
             
     | 
| 
      
 976 
     | 
    
         
            +
                def _footnotesXml(self) -> str:
         
     | 
| 
      
 977 
     | 
    
         
            +
                    """Populate footnotes.xml."""
         
     | 
| 
      
 978 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 979 
     | 
    
         
            +
                    xRoot = xmlElement(_wTag("footnotes"))
         
     | 
| 
      
 980 
     | 
    
         
            +
                    self._rels["footnotes.xml"] = DocXXmlRel(
         
     | 
| 
      
 981 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 982 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/footnotes",
         
     | 
| 
      
 983 
     | 
    
         
            +
                    )
         
     | 
| 
      
 984 
     | 
    
         
            +
                    self._files["footnotes.xml"] = DocXXmlFile(
         
     | 
| 
      
 985 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 986 
     | 
    
         
            +
                        path="word",
         
     | 
| 
      
 987 
     | 
    
         
            +
                        contentType=f"{WORD_BASE}.footnotes+xml",
         
     | 
| 
      
 988 
     | 
    
         
            +
                    )
         
     | 
| 
      
 989 
     | 
    
         
            +
             
     | 
| 
      
 990 
     | 
    
         
            +
                    for key, idx in self._usedNotes.items():
         
     | 
| 
      
 991 
     | 
    
         
            +
                        par = DocXParagraph()
         
     | 
| 
      
 992 
     | 
    
         
            +
                        par.setIsFootnote(True)
         
     | 
| 
      
 993 
     | 
    
         
            +
                        if content := self._footnotes.get(key):
         
     | 
| 
      
 994 
     | 
    
         
            +
                            self._processFragments(par, S_FNOTE, content[0], content[1])
         
     | 
| 
      
 995 
     | 
    
         
            +
                        par.toXml(xmlSubElem(xRoot, _wTag("footnote"), attrib={_wTag("id"): str(idx)}))
         
     | 
| 
      
 996 
     | 
    
         
            +
             
     | 
| 
      
 997 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 998 
     | 
    
         
            +
             
     | 
| 
      
 999 
     | 
    
         
            +
                def _fontTableXml(self) -> str:
         
     | 
| 
      
 1000 
     | 
    
         
            +
                    """Populate fontTable.xml."""
         
     | 
| 
      
 1001 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 1002 
     | 
    
         
            +
                    xRoot = xmlElement(_wTag("fonts"))
         
     | 
| 
      
 1003 
     | 
    
         
            +
                    self._rels["fontTable.xml"] = DocXXmlRel(
         
     | 
| 
      
 1004 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 1005 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/fontTable",
         
     | 
| 
      
 1006 
     | 
    
         
            +
                    )
         
     | 
| 
      
 1007 
     | 
    
         
            +
                    self._files["fontTable.xml"] = DocXXmlFile(
         
     | 
| 
      
 1008 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 1009 
     | 
    
         
            +
                        path="word",
         
     | 
| 
      
 1010 
     | 
    
         
            +
                        contentType=f"{WORD_BASE}.fontTable+xml",
         
     | 
| 
      
 1011 
     | 
    
         
            +
                    )
         
     | 
| 
      
 1012 
     | 
    
         
            +
             
     | 
| 
      
 1013 
     | 
    
         
            +
                    xFont = xmlSubElem(xRoot, _wTag("font"), attrib={
         
     | 
| 
      
 1014 
     | 
    
         
            +
                        _wTag("name"): self._textFont.family(),
         
     | 
| 
      
 1015 
     | 
    
         
            +
                    })
         
     | 
| 
      
 1016 
     | 
    
         
            +
                    xmlSubElem(xFont, _wTag("pitch"), attrib={
         
     | 
| 
      
 1017 
     | 
    
         
            +
                        W_VAL: "fixed" if self._textFont.fixedPitch() else "variable",
         
     | 
| 
      
 1018 
     | 
    
         
            +
                    })
         
     | 
| 
      
 1019 
     | 
    
         
            +
             
     | 
| 
      
 1020 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 1021 
     | 
    
         
            +
             
     | 
| 
      
 1022 
     | 
    
         
            +
                def _settingsXml(self) -> str:
         
     | 
| 
      
 1023 
     | 
    
         
            +
                    """Populate settings.xml."""
         
     | 
| 
      
 1024 
     | 
    
         
            +
                    rId = self._nextRelId()
         
     | 
| 
      
 1025 
     | 
    
         
            +
                    xRoot = xmlElement(_wTag("settings"))
         
     | 
| 
      
 1026 
     | 
    
         
            +
                    self._rels["settings.xml"] = DocXXmlRel(
         
     | 
| 
      
 1027 
     | 
    
         
            +
                        rId=rId,
         
     | 
| 
      
 1028 
     | 
    
         
            +
                        relType=f"{RELS_BASE}/settings",
         
     | 
| 
      
 1029 
     | 
    
         
            +
                    )
         
     | 
| 
      
 1030 
     | 
    
         
            +
                    self._files["settings.xml"] = DocXXmlFile(
         
     | 
| 
      
 1031 
     | 
    
         
            +
                        xml=xRoot,
         
     | 
| 
      
 1032 
     | 
    
         
            +
                        path="word",
         
     | 
| 
      
 1033 
     | 
    
         
            +
                        contentType=f"{WORD_BASE}.settings+xml",
         
     | 
| 
      
 1034 
     | 
    
         
            +
                    )
         
     | 
| 
      
 1035 
     | 
    
         
            +
             
     | 
| 
      
 1036 
     | 
    
         
            +
                    xSet = xmlSubElem(xRoot, _wTag("footnotePr"))
         
     | 
| 
      
 1037 
     | 
    
         
            +
                    xmlSubElem(xSet, _wTag("numFmt"), attrib={W_VAL: "decimal"})
         
     | 
| 
      
 1038 
     | 
    
         
            +
             
     | 
| 
      
 1039 
     | 
    
         
            +
                    xSet = xmlSubElem(xRoot, _wTag("compat"))
         
     | 
| 
      
 1040 
     | 
    
         
            +
                    xmlSubElem(xSet, _wTag("compatSetting"), attrib={
         
     | 
| 
      
 1041 
     | 
    
         
            +
                        _wTag("name"): "compatibilityMode",
         
     | 
| 
      
 1042 
     | 
    
         
            +
                        _wTag("uri"): "http://schemas.microsoft.com/office/word",
         
     | 
| 
      
 1043 
     | 
    
         
            +
                        W_VAL: "12",
         
     | 
| 
      
 1044 
     | 
    
         
            +
                    })
         
     | 
| 
      
 1045 
     | 
    
         
            +
             
     | 
| 
      
 1046 
     | 
    
         
            +
                    if self._counts:
         
     | 
| 
      
 1047 
     | 
    
         
            +
                        xVars = xmlSubElem(xRoot, _wTag("docVars"))
         
     | 
| 
      
 1048 
     | 
    
         
            +
                        for key, value in self._counts.items():
         
     | 
| 
      
 1049 
     | 
    
         
            +
                            xmlSubElem(xVars, _wTag("docVar"), attrib={
         
     | 
| 
      
 1050 
     | 
    
         
            +
                                _wTag("name"): f"Manuscript{key[:1].upper()}{key[1:]}",
         
     | 
| 
      
 1051 
     | 
    
         
            +
                                W_VAL: str(value),
         
     | 
| 
      
 1052 
     | 
    
         
            +
                            })
         
     | 
| 
      
 1053 
     | 
    
         
            +
             
     | 
| 
      
 1054 
     | 
    
         
            +
                    return rId
         
     | 
| 
      
 1055 
     | 
    
         
            +
             
     | 
| 
      
 1056 
     | 
    
         
            +
             
     | 
| 
      
 1057 
     | 
    
         
            +
            class DocXParagraph:
         
     | 
| 
      
 1058 
     | 
    
         
            +
             
     | 
| 
      
 1059 
     | 
    
         
            +
                __slots__ = (
         
     | 
| 
      
 1060 
     | 
    
         
            +
                    "_content", "_style", "_textAlign",
         
     | 
| 
      
 1061 
     | 
    
         
            +
                    "_topMargin", "_bottomMargin", "_leftMargin", "_rightMargin",
         
     | 
| 
      
 1062 
     | 
    
         
            +
                    "_indentFirst", "_breakBefore", "_breakAfter", "_footnoteRef",
         
     | 
| 
      
 1063 
     | 
    
         
            +
                )
         
     | 
| 
      
 1064 
     | 
    
         
            +
             
     | 
| 
      
 1065 
     | 
    
         
            +
                def __init__(self) -> None:
         
     | 
| 
      
 1066 
     | 
    
         
            +
                    self._content: list[ET.Element] = []
         
     | 
| 
      
 1067 
     | 
    
         
            +
                    self._style: DocXParStyle | None = None
         
     | 
| 
      
 1068 
     | 
    
         
            +
                    self._textAlign: str | None = None
         
     | 
| 
      
 1069 
     | 
    
         
            +
                    self._topMargin: float | None = None
         
     | 
| 
      
 1070 
     | 
    
         
            +
                    self._bottomMargin: float | None = None
         
     | 
| 
      
 1071 
     | 
    
         
            +
                    self._leftMargin: float | None = None
         
     | 
| 
      
 1072 
     | 
    
         
            +
                    self._rightMargin: float | None = None
         
     | 
| 
      
 1073 
     | 
    
         
            +
                    self._indentFirst = False
         
     | 
| 
      
 1074 
     | 
    
         
            +
                    self._breakBefore = False
         
     | 
| 
      
 1075 
     | 
    
         
            +
                    self._breakAfter = False
         
     | 
| 
      
 1076 
     | 
    
         
            +
                    self._footnoteRef = False
         
     | 
| 
      
 1077 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1078 
     | 
    
         
            +
             
     | 
| 
      
 1079 
     | 
    
         
            +
                ##
         
     | 
| 
      
 1080 
     | 
    
         
            +
                #  Properties
         
     | 
| 
      
 1081 
     | 
    
         
            +
                ##
         
     | 
| 
      
 1082 
     | 
    
         
            +
             
     | 
| 
      
 1083 
     | 
    
         
            +
                @property
         
     | 
| 
      
 1084 
     | 
    
         
            +
                def pageBreakBefore(self) -> bool:
         
     | 
| 
      
 1085 
     | 
    
         
            +
                    """Has page break before."""
         
     | 
| 
      
 1086 
     | 
    
         
            +
                    return self._breakBefore
         
     | 
| 
      
 1087 
     | 
    
         
            +
             
     | 
| 
      
 1088 
     | 
    
         
            +
                ##
         
     | 
| 
      
 1089 
     | 
    
         
            +
                #  Setters
         
     | 
| 
      
 1090 
     | 
    
         
            +
                ##
         
     | 
| 
      
 1091 
     | 
    
         
            +
             
     | 
| 
      
 1092 
     | 
    
         
            +
                def setStyle(self, style: DocXParStyle | None) -> None:
         
     | 
| 
      
 1093 
     | 
    
         
            +
                    """Set the paragraph style."""
         
     | 
| 
      
 1094 
     | 
    
         
            +
                    self._style = style
         
     | 
| 
      
 1095 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1096 
     | 
    
         
            +
             
     | 
| 
      
 1097 
     | 
    
         
            +
                def setAlignment(self, value: str) -> None:
         
     | 
| 
      
 1098 
     | 
    
         
            +
                    """Set paragraph alignment."""
         
     | 
| 
      
 1099 
     | 
    
         
            +
                    if value in ("left", "center", "right", "both"):
         
     | 
| 
      
 1100 
     | 
    
         
            +
                        self._textAlign = value
         
     | 
| 
      
 1101 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1102 
     | 
    
         
            +
             
     | 
| 
      
 1103 
     | 
    
         
            +
                def setMarginTop(self, value: float) -> None:
         
     | 
| 
      
 1104 
     | 
    
         
            +
                    """Set margin above in pt."""
         
     | 
| 
      
 1105 
     | 
    
         
            +
                    self._topMargin = value
         
     | 
| 
      
 1106 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1107 
     | 
    
         
            +
             
     | 
| 
      
 1108 
     | 
    
         
            +
                def setMarginBottom(self, value: float) -> None:
         
     | 
| 
      
 1109 
     | 
    
         
            +
                    """Set margin below in pt."""
         
     | 
| 
      
 1110 
     | 
    
         
            +
                    self._bottomMargin = value
         
     | 
| 
      
 1111 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1112 
     | 
    
         
            +
             
     | 
| 
      
 1113 
     | 
    
         
            +
                def setMarginLeft(self, value: float) -> None:
         
     | 
| 
      
 1114 
     | 
    
         
            +
                    """Set margin left in pt."""
         
     | 
| 
      
 1115 
     | 
    
         
            +
                    self._leftMargin = value
         
     | 
| 
      
 1116 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1117 
     | 
    
         
            +
             
     | 
| 
      
 1118 
     | 
    
         
            +
                def setMarginRight(self, value: float) -> None:
         
     | 
| 
      
 1119 
     | 
    
         
            +
                    """Set margin right in pt."""
         
     | 
| 
      
 1120 
     | 
    
         
            +
                    self._rightMargin = value
         
     | 
| 
      
 1121 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1122 
     | 
    
         
            +
             
     | 
| 
      
 1123 
     | 
    
         
            +
                def setIndentFirst(self, state: bool) -> None:
         
     | 
| 
      
 1124 
     | 
    
         
            +
                    """Set first line indent."""
         
     | 
| 
      
 1125 
     | 
    
         
            +
                    self._indentFirst = state
         
     | 
| 
      
 1126 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1127 
     | 
    
         
            +
             
     | 
| 
      
 1128 
     | 
    
         
            +
                def setPageBreakBefore(self, state: bool) -> None:
         
     | 
| 
      
 1129 
     | 
    
         
            +
                    """Set page break before flag."""
         
     | 
| 
      
 1130 
     | 
    
         
            +
                    self._breakBefore = state
         
     | 
| 
      
 1131 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1132 
     | 
    
         
            +
             
     | 
| 
      
 1133 
     | 
    
         
            +
                def setPageBreakAfter(self, state: bool) -> None:
         
     | 
| 
      
 1134 
     | 
    
         
            +
                    """Set page break after flag."""
         
     | 
| 
      
 1135 
     | 
    
         
            +
                    self._breakAfter = state
         
     | 
| 
      
 1136 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1137 
     | 
    
         
            +
             
     | 
| 
      
 1138 
     | 
    
         
            +
                def setIsFootnote(self, state: bool) -> None:
         
     | 
| 
      
 1139 
     | 
    
         
            +
                    """Set is footnote flag."""
         
     | 
| 
      
 1140 
     | 
    
         
            +
                    self._footnoteRef = state
         
     | 
| 
      
 1141 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1142 
     | 
    
         
            +
             
     | 
| 
      
 1143 
     | 
    
         
            +
                ##
         
     | 
| 
      
 1144 
     | 
    
         
            +
                #  Methods
         
     | 
| 
      
 1145 
     | 
    
         
            +
                ##
         
     | 
| 
      
 1146 
     | 
    
         
            +
             
     | 
| 
      
 1147 
     | 
    
         
            +
                def addContent(self, run: ET.Element) -> None:
         
     | 
| 
      
 1148 
     | 
    
         
            +
                    """Add a run segment to the paragraph."""
         
     | 
| 
      
 1149 
     | 
    
         
            +
                    self._content.append(run)
         
     | 
| 
      
 1150 
     | 
    
         
            +
                    return
         
     | 
| 
      
 1151 
     | 
    
         
            +
             
     | 
| 
      
 1152 
     | 
    
         
            +
                def toXml(self, body: ET.Element) -> None:
         
     | 
| 
      
 1153 
     | 
    
         
            +
                    """Called after all content is set."""
         
     | 
| 
      
 1154 
     | 
    
         
            +
                    if style := self._style:
         
     | 
| 
      
 1155 
     | 
    
         
            +
                        xP = xmlSubElem(body, _wTag("p"))
         
     | 
| 
      
 1156 
     | 
    
         
            +
             
     | 
| 
      
 1157 
     | 
    
         
            +
                        # Values
         
     | 
| 
      
 1158 
     | 
    
         
            +
                        indent = {}
         
     | 
| 
      
 1159 
     | 
    
         
            +
                        if self._indentFirst and style.indentFirst is not None:
         
     | 
| 
      
 1160 
     | 
    
         
            +
                            indent[_wTag("firstLine")] = str(int(20.0 * style.indentFirst))
         
     | 
| 
      
 1161 
     | 
    
         
            +
                        if self._leftMargin is not None:
         
     | 
| 
      
 1162 
     | 
    
         
            +
                            indent[_wTag("left")] = str(int(20.0 * self._leftMargin))
         
     | 
| 
      
 1163 
     | 
    
         
            +
                        if self._rightMargin is not None:
         
     | 
| 
      
 1164 
     | 
    
         
            +
                            indent[_wTag("right")] = str(int(20.0 * self._rightMargin))
         
     | 
| 
      
 1165 
     | 
    
         
            +
             
     | 
| 
      
 1166 
     | 
    
         
            +
                        # Paragraph
         
     | 
| 
      
 1167 
     | 
    
         
            +
                        pPr = xmlSubElem(xP, _wTag("pPr"))
         
     | 
| 
      
 1168 
     | 
    
         
            +
                        xmlSubElem(pPr, _wTag("pStyle"), attrib={W_VAL: style.styleId})
         
     | 
| 
      
 1169 
     | 
    
         
            +
                        if self._topMargin is not None or self._bottomMargin is not None:
         
     | 
| 
      
 1170 
     | 
    
         
            +
                            xmlSubElem(pPr, _wTag("spacing"), attrib={
         
     | 
| 
      
 1171 
     | 
    
         
            +
                                _wTag("before"): str(int(20.0 * firstFloat(self._topMargin, style.before))),
         
     | 
| 
      
 1172 
     | 
    
         
            +
                                _wTag("after"): str(int(20.0 * firstFloat(self._bottomMargin, style.after))),
         
     | 
| 
      
 1173 
     | 
    
         
            +
                                _wTag("line"): str(int(20.0 * firstFloat(style.line, style.size))),
         
     | 
| 
      
 1174 
     | 
    
         
            +
                            })
         
     | 
| 
      
 1175 
     | 
    
         
            +
                        if indent:
         
     | 
| 
      
 1176 
     | 
    
         
            +
                            xmlSubElem(pPr, _wTag("ind"), attrib=indent)
         
     | 
| 
      
 1177 
     | 
    
         
            +
                        if self._textAlign:
         
     | 
| 
      
 1178 
     | 
    
         
            +
                            xmlSubElem(pPr, _wTag("jc"), attrib={W_VAL: self._textAlign})
         
     | 
| 
      
 1179 
     | 
    
         
            +
             
     | 
| 
      
 1180 
     | 
    
         
            +
                        # Text
         
     | 
| 
      
 1181 
     | 
    
         
            +
                        if self._footnoteRef:
         
     | 
| 
      
 1182 
     | 
    
         
            +
                            xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 1183 
     | 
    
         
            +
                            rPr = xmlSubElem(xR, _wTag("rPr"))
         
     | 
| 
      
 1184 
     | 
    
         
            +
                            xmlSubElem(rPr, _wTag("vertAlign"), attrib={W_VAL: "superscript"})
         
     | 
| 
      
 1185 
     | 
    
         
            +
                            xmlSubElem(xR, _wTag("footnoteRef"))
         
     | 
| 
      
 1186 
     | 
    
         
            +
                        if self._breakBefore:
         
     | 
| 
      
 1187 
     | 
    
         
            +
                            xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 1188 
     | 
    
         
            +
                            xmlSubElem(xR, _wTag("br"), attrib={_wTag("type"): "page"})
         
     | 
| 
      
 1189 
     | 
    
         
            +
                        for xR in self._content:
         
     | 
| 
      
 1190 
     | 
    
         
            +
                            xP.append(xR)
         
     | 
| 
      
 1191 
     | 
    
         
            +
                        if self._breakAfter:
         
     | 
| 
      
 1192 
     | 
    
         
            +
                            xR = xmlSubElem(xP, _wTag("r"))
         
     | 
| 
      
 1193 
     | 
    
         
            +
                            xmlSubElem(xR, _wTag("br"), attrib={_wTag("type"): "page"})
         
     | 
| 
      
 1194 
     | 
    
         
            +
             
     | 
| 
      
 1195 
     | 
    
         
            +
                    return
         
     |