Merge branch 'dev' into feature/build_gui
This commit is contained in:
@@ -14,7 +14,7 @@ jobs:
|
|||||||
testLinux:
|
testLinux:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Python Setup
|
- name: Python Setup
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ The following libraries are dependencies of novelWriter:
|
|||||||
|
|
||||||
* [Qt5](https://www.qt.io) by Qt Company
|
* [Qt5](https://www.qt.io) by Qt Company
|
||||||
* [PyQt5](https://www.riverbankcomputing.com/software/pyqt) by Riverbank Computing
|
* [PyQt5](https://www.riverbankcomputing.com/software/pyqt) by Riverbank Computing
|
||||||
* [lxml](https://lxml.de) by Martijn Faassen
|
|
||||||
* [Enchant](https://abiword.github.io/enchant) by Dom Lachowicz
|
* [Enchant](https://abiword.github.io/enchant) by Dom Lachowicz
|
||||||
* [PyEnchant](https://pyenchant.github.io/pyenchant) by Dimitri Merejkowsky
|
* [PyEnchant](https://pyenchant.github.io/pyenchant) by Dimitri Merejkowsky
|
||||||
|
|
||||||
|
|||||||
@@ -46,13 +46,10 @@ format of the main project file. Everything else is handled with standard Python
|
|||||||
The following Python packages are needed to run novelWriter:
|
The following Python packages are needed to run novelWriter:
|
||||||
|
|
||||||
* ``PyQt5`` – needed for connecting with the Qt5 libraries.
|
* ``PyQt5`` – needed for connecting with the Qt5 libraries.
|
||||||
* ``lxml`` – needed for full XML support.
|
|
||||||
* ``PyEnchant`` – needed for spell checking (optional).
|
* ``PyEnchant`` – needed for spell checking (optional).
|
||||||
|
|
||||||
PyQt/Qt should be at least 5.10, but ideally 5.13 or higher for all features to work. For instance,
|
PyQt/Qt should be at least 5.10, but ideally 5.13 or higher for all features to work. For instance,
|
||||||
searching using regular expressions with full Unicode support requires 5.13. There is no known
|
searching using regular expressions with full Unicode support requires 5.13.
|
||||||
minimum version requirement for package ``lxml``, but the code was originally written with 4.2,
|
|
||||||
which is therefore set as the minimum. It may work on lower versions. You have to test it.
|
|
||||||
|
|
||||||
If you want spell checking, you must install the ``PyEnchant`` package. The spell check library
|
If you want spell checking, you must install the ``PyEnchant`` package. The spell check library
|
||||||
must be at least 3.0 to work with Windows. On Linux, 2.0 also works fine.
|
must be at least 3.0 to work with Windows. On Linux, 2.0 also works fine.
|
||||||
|
|||||||
@@ -185,12 +185,6 @@ def main(sysArgs=None):
|
|||||||
)
|
)
|
||||||
errorCode |= 0x10
|
errorCode |= 0x10
|
||||||
|
|
||||||
try:
|
|
||||||
import lxml # noqa: F401
|
|
||||||
except ImportError:
|
|
||||||
errorData.append("Python module 'lxml' is missing")
|
|
||||||
errorCode |= 0x20
|
|
||||||
|
|
||||||
if errorData:
|
if errorData:
|
||||||
errApp = QApplication([])
|
errApp = QApplication([])
|
||||||
errDlg = QErrorMessage()
|
errDlg = QErrorMessage()
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ more contributions are listed on the project's <a href="https://crowdin.com/proj
|
|||||||
<p>
|
<p>
|
||||||
<a href="https://www.qt.io">Qt5</a> by Qt Company<br>
|
<a href="https://www.qt.io">Qt5</a> by Qt Company<br>
|
||||||
<a href="https://www.riverbankcomputing.com/software/pyqt">PyQt5</a> by Riverbank Computing<br>
|
<a href="https://www.riverbankcomputing.com/software/pyqt">PyQt5</a> by Riverbank Computing<br>
|
||||||
<a href="https://lxml.de">lxml</a> by Martijn Faassen<br>
|
|
||||||
<a href="https://abiword.github.io/enchant">Enchant</a> by Dom Lachowicz<br>
|
<a href="https://abiword.github.io/enchant">Enchant</a> by Dom Lachowicz<br>
|
||||||
<a href="https://pyenchant.github.io/pyenchant">PyEnchant</a> by Dimitri Merejkowsky
|
<a href="https://pyenchant.github.io/pyenchant">PyEnchant</a> by Dimitri Merejkowsky
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -23,10 +23,13 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -435,6 +438,45 @@ def jsonEncode(data, n=0, nmax=0):
|
|||||||
return "".join(buffer)
|
return "".join(buffer)
|
||||||
|
|
||||||
|
|
||||||
|
def xmlIndent(tree: ET.Element | ET.ElementTree):
|
||||||
|
"""A modified version of the XML indent function in the standard
|
||||||
|
library. It behaves more closely to how the one from lxml does.
|
||||||
|
"""
|
||||||
|
if isinstance(tree, ET.ElementTree):
|
||||||
|
tree = tree.getroot()
|
||||||
|
|
||||||
|
indentations = ["\n"]
|
||||||
|
|
||||||
|
def indentChildren(elem, level):
|
||||||
|
chLevel = level + 1
|
||||||
|
try:
|
||||||
|
chIndent = indentations[chLevel]
|
||||||
|
except IndexError:
|
||||||
|
chIndent = indentations[level] + " "
|
||||||
|
indentations.append(chIndent)
|
||||||
|
|
||||||
|
if elem.text is None:
|
||||||
|
elem.text = chIndent
|
||||||
|
|
||||||
|
last = None
|
||||||
|
for child in elem:
|
||||||
|
if len(child):
|
||||||
|
indentChildren(child, chLevel)
|
||||||
|
if child.tail is None:
|
||||||
|
child.tail = chIndent
|
||||||
|
last = child
|
||||||
|
|
||||||
|
# Dedent the last child
|
||||||
|
if last is not None:
|
||||||
|
last.tail = indentations[level]
|
||||||
|
|
||||||
|
if len(tree):
|
||||||
|
indentChildren(tree, 0)
|
||||||
|
tree.tail = "\n"
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
# File and File System Functions
|
# File and File System Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|||||||
@@ -196,7 +196,6 @@ class Config:
|
|||||||
|
|
||||||
# Check Python Version
|
# Check Python Version
|
||||||
self.verPyString = sys.version.split()[0]
|
self.verPyString = sys.version.split()[0]
|
||||||
self.verPyHexVal = sys.hexversion
|
|
||||||
|
|
||||||
# Check OS Type
|
# Check OS Type
|
||||||
self.osType = sys.platform
|
self.osType = sys.platform
|
||||||
|
|||||||
@@ -26,16 +26,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from lxml import etree
|
|
||||||
from time import time
|
from time import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from novelwriter import __version__, __hexversion__
|
from novelwriter import __version__, __hexversion__
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkBool, checkInt, checkString, checkStringNone, formatTimeStamp,
|
checkBool, checkInt, checkString, checkStringNone, formatTimeStamp,
|
||||||
hexToInt, simplified, yesNo
|
hexToInt, simplified, xmlIndent, yesNo
|
||||||
)
|
)
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ class ProjectXMLReader:
|
|||||||
logger.debug("Reading project XML")
|
logger.debug("Reading project XML")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml = etree.parse(str(self._path))
|
xml = ET.parse(str(self._path))
|
||||||
self._state = XMLReadState.NO_ERROR
|
self._state = XMLReadState.NO_ERROR
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -174,7 +174,7 @@ class ProjectXMLReader:
|
|||||||
backFile = self._path.with_suffix(".bak")
|
backFile = self._path.with_suffix(".bak")
|
||||||
if backFile.is_file():
|
if backFile.is_file():
|
||||||
try:
|
try:
|
||||||
xml = etree.parse(str(backFile))
|
xml = ET.parse(str(backFile))
|
||||||
self._state = XMLReadState.PARSED_BACKUP
|
self._state = XMLReadState.PARSED_BACKUP
|
||||||
logger.info("Backup project file parsed")
|
logger.info("Backup project file parsed")
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -500,7 +500,7 @@ class ProjectXMLWriter:
|
|||||||
tStart = time()
|
tStart = time()
|
||||||
logger.debug("Writing project XML")
|
logger.debug("Writing project XML")
|
||||||
|
|
||||||
xRoot = etree.Element("novelWriterXML", attrib={
|
xRoot = ET.Element("novelWriterXML", attrib={
|
||||||
"appVersion": str(__version__),
|
"appVersion": str(__version__),
|
||||||
"hexVersion": str(__hexversion__),
|
"hexVersion": str(__hexversion__),
|
||||||
"fileVersion": FILE_VERSION,
|
"fileVersion": FILE_VERSION,
|
||||||
@@ -515,13 +515,13 @@ class ProjectXMLWriter:
|
|||||||
"editTime": str(editTime),
|
"editTime": str(editTime),
|
||||||
}
|
}
|
||||||
|
|
||||||
xProject = etree.SubElement(xRoot, "project", attrib=projAttr)
|
xProject = ET.SubElement(xRoot, "project", attrib=projAttr)
|
||||||
self._packSingleValue(xProject, "name", projData.name)
|
self._packSingleValue(xProject, "name", projData.name)
|
||||||
self._packSingleValue(xProject, "title", projData.title)
|
self._packSingleValue(xProject, "title", projData.title)
|
||||||
self._packSingleValue(xProject, "author", projData.author)
|
self._packSingleValue(xProject, "author", projData.author)
|
||||||
|
|
||||||
# Save Project Settings
|
# Save Project Settings
|
||||||
xSettings = etree.SubElement(xRoot, "settings")
|
xSettings = ET.SubElement(xRoot, "settings")
|
||||||
self._packSingleValue(xSettings, "doBackup", yesNo(projData.doBackup))
|
self._packSingleValue(xSettings, "doBackup", yesNo(projData.doBackup))
|
||||||
self._packSingleValue(xSettings, "language", projData.language)
|
self._packSingleValue(xSettings, "language", projData.language)
|
||||||
self._packSingleValue(xSettings, "spellChecking", projData.spellLang, attrib={
|
self._packSingleValue(xSettings, "spellChecking", projData.spellLang, attrib={
|
||||||
@@ -532,11 +532,11 @@ class ProjectXMLWriter:
|
|||||||
self._packDictKeyValue(xSettings, "titleFormat", projData.titleFormat)
|
self._packDictKeyValue(xSettings, "titleFormat", projData.titleFormat)
|
||||||
|
|
||||||
# Save Status/Importance
|
# Save Status/Importance
|
||||||
xStatus = etree.SubElement(xSettings, "status")
|
xStatus = ET.SubElement(xSettings, "status")
|
||||||
for label, attrib in projData.itemStatus.pack():
|
for label, attrib in projData.itemStatus.pack():
|
||||||
self._packSingleValue(xStatus, "entry", label, attrib=attrib)
|
self._packSingleValue(xStatus, "entry", label, attrib=attrib)
|
||||||
|
|
||||||
xImport = etree.SubElement(xSettings, "importance")
|
xImport = ET.SubElement(xSettings, "importance")
|
||||||
for label, attrib in projData.itemImport.pack():
|
for label, attrib in projData.itemImport.pack():
|
||||||
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
||||||
|
|
||||||
@@ -547,11 +547,11 @@ class ProjectXMLWriter:
|
|||||||
"notesWords": str(projData.currCounts[1]),
|
"notesWords": str(projData.currCounts[1]),
|
||||||
}
|
}
|
||||||
|
|
||||||
xContent = etree.SubElement(xRoot, "content", attrib=contAttr)
|
xContent = ET.SubElement(xRoot, "content", attrib=contAttr)
|
||||||
for item in projContent:
|
for item in projContent:
|
||||||
xItem = etree.SubElement(xContent, "item", attrib=item.get("itemAttr", {}))
|
xItem = ET.SubElement(xContent, "item", attrib=item.get("itemAttr", {}))
|
||||||
etree.SubElement(xItem, "meta", attrib=item.get("metaAttr", {}))
|
ET.SubElement(xItem, "meta", attrib=item.get("metaAttr", {}))
|
||||||
xName = etree.SubElement(xItem, "name", attrib=item.get("nameAttr", {}))
|
xName = ET.SubElement(xItem, "name", attrib=item.get("nameAttr", {}))
|
||||||
xName.text = item["name"]
|
xName.text = item["name"]
|
||||||
|
|
||||||
# Write the XML tree to file
|
# Write the XML tree to file
|
||||||
@@ -559,9 +559,9 @@ class ProjectXMLWriter:
|
|||||||
tempFile = saveFile.with_suffix(".tmp")
|
tempFile = saveFile.with_suffix(".tmp")
|
||||||
backFile = saveFile.with_suffix(".bak")
|
backFile = saveFile.with_suffix(".bak")
|
||||||
try:
|
try:
|
||||||
tempFile.write_bytes(etree.tostring(
|
xml = ET.ElementTree(xRoot)
|
||||||
xRoot, pretty_print=True, encoding="utf-8", xml_declaration=True
|
xmlIndent(xml)
|
||||||
))
|
xml.write(tempFile, encoding="utf-8", xml_declaration=True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._error = exc
|
self._error = exc
|
||||||
return False
|
return False
|
||||||
@@ -587,17 +587,17 @@ class ProjectXMLWriter:
|
|||||||
def _packSingleValue(self, xParent, name, value, attrib=None):
|
def _packSingleValue(self, xParent, name, value, attrib=None):
|
||||||
"""Pack a single value into an XML element.
|
"""Pack a single value into an XML element.
|
||||||
"""
|
"""
|
||||||
xItem = etree.SubElement(xParent, name, attrib=attrib)
|
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
||||||
xItem.text = str(value) or ""
|
xItem.text = str(value) or ""
|
||||||
return
|
return
|
||||||
|
|
||||||
def _packDictKeyValue(self, xParent, name, data):
|
def _packDictKeyValue(self, xParent, name, data):
|
||||||
"""Pack the entries of a dictionary into an XML element.
|
"""Pack the entries of a dictionary into an XML element.
|
||||||
"""
|
"""
|
||||||
xItem = etree.SubElement(xParent, name)
|
xItem = ET.SubElement(xParent, name)
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if len(key) > 0:
|
if len(key) > 0:
|
||||||
xEntry = etree.SubElement(xItem, "entry", attrib={"key": key})
|
xEntry = ET.SubElement(xItem, "entry", attrib={"key": key})
|
||||||
xEntry.text = str(value) or ""
|
xEntry.text = str(value) or ""
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
+122
-133
@@ -27,13 +27,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from novelwriter import __version__
|
from novelwriter import __version__
|
||||||
|
from novelwriter.common import xmlIndent
|
||||||
from novelwriter.constants import nwKeyWords, nwLabels
|
from novelwriter.constants import nwKeyWords, nwLabels
|
||||||
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Main XML NameSpaces
|
# Main XML NameSpaces
|
||||||
XML_NS = {
|
XML_NS = {
|
||||||
|
"manifest": "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0",
|
||||||
"office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
|
"office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
|
||||||
"style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
"style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
||||||
"loext": "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0",
|
"loext": "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0",
|
||||||
@@ -49,22 +51,18 @@ XML_NS = {
|
|||||||
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||||
"dc": "http://purl.org/dc/elements/1.1/",
|
"dc": "http://purl.org/dc/elements/1.1/",
|
||||||
}
|
}
|
||||||
MANI_NS = {
|
for ns, uri in XML_NS.items():
|
||||||
"manifest": "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
|
ET.register_namespace(ns, uri)
|
||||||
}
|
|
||||||
OFFICE_NS = {
|
|
||||||
"office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _mkTag(nsName, tagName, nsMap=XML_NS):
|
def _mkTag(ns, tag):
|
||||||
"""Assemble namespace and tag name.
|
"""Assemble namespace and tag name.
|
||||||
"""
|
"""
|
||||||
theNS = nsMap.get(nsName, "")
|
uri = XML_NS.get(ns, "")
|
||||||
if theNS:
|
if uri:
|
||||||
return f"{{{theNS}}}{tagName}"
|
return f"{{{uri}}}{tag}"
|
||||||
logger.warning("Missing xml namespace '%s'", nsName)
|
logger.warning("Missing xml namespace '%s'", ns)
|
||||||
return tagName
|
return tag
|
||||||
|
|
||||||
|
|
||||||
# Mimetype and Version
|
# Mimetype and Version
|
||||||
@@ -97,20 +95,20 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
||||||
|
|
||||||
self._dFlat = None # FODT file XML root
|
self._dFlat = ET.Element("") # FODT file XML root
|
||||||
self._dCont = None # ODT content.xml root
|
self._dCont = ET.Element("") # ODT content.xml root
|
||||||
self._dMeta = None # ODT meta.xml root
|
self._dMeta = ET.Element("") # ODT meta.xml root
|
||||||
self._dStyl = None # ODT styles.xml root
|
self._dStyl = ET.Element("") # ODT styles.xml root
|
||||||
|
|
||||||
self._xMeta = None # Office meta root
|
self._xMeta = ET.Element("") # Office meta root
|
||||||
self._xFont = None # Office font face declaration
|
self._xFont = ET.Element("") # Office font face declaration
|
||||||
self._xFnt2 = None # Office font face declaration, secondary
|
self._xFnt2 = ET.Element("") # Office font face declaration, secondary
|
||||||
self._xStyl = None # Office styles root
|
self._xStyl = ET.Element("") # Office styles root
|
||||||
self._xAuto = None # Office auto-styles root
|
self._xAuto = ET.Element("") # Office auto-styles root
|
||||||
self._xAut2 = None # Office auto-styles root, secondary
|
self._xAut2 = ET.Element("") # Office auto-styles root, secondary
|
||||||
self._xMast = None # Office master-styles root
|
self._xMast = ET.Element("") # Office master-styles root
|
||||||
self._xBody = None # Office body root
|
self._xBody = ET.Element("") # Office body root
|
||||||
self._xText = None # Office text root
|
self._xText = ET.Element("") # Office text root
|
||||||
|
|
||||||
self._mainPara = {} # User-accessible paragraph styles
|
self._mainPara = {} # User-accessible paragraph styles
|
||||||
self._autoPara = {} # Auto-generated paragraph styles
|
self._autoPara = {} # Auto-generated paragraph styles
|
||||||
@@ -287,16 +285,16 @@ class ToOdt(Tokenizer):
|
|||||||
tAttr[_mkTag("office", "mimetype")] = X_MIME
|
tAttr[_mkTag("office", "mimetype")] = X_MIME
|
||||||
|
|
||||||
tFlat = _mkTag("office", "document")
|
tFlat = _mkTag("office", "document")
|
||||||
self._dFlat = etree.Element(tFlat, attrib=tAttr, nsmap=XML_NS)
|
self._dFlat = ET.Element(tFlat, attrib=tAttr)
|
||||||
|
|
||||||
self._xMeta = etree.SubElement(self._dFlat, _mkTag("office", "meta"))
|
self._xMeta = ET.SubElement(self._dFlat, _mkTag("office", "meta"))
|
||||||
self._xFont = etree.SubElement(self._dFlat, _mkTag("office", "font-face-decls"))
|
self._xFont = ET.SubElement(self._dFlat, _mkTag("office", "font-face-decls"))
|
||||||
self._xStyl = etree.SubElement(self._dFlat, _mkTag("office", "styles"))
|
self._xStyl = ET.SubElement(self._dFlat, _mkTag("office", "styles"))
|
||||||
self._xAuto = etree.SubElement(self._dFlat, _mkTag("office", "automatic-styles"))
|
self._xAuto = ET.SubElement(self._dFlat, _mkTag("office", "automatic-styles"))
|
||||||
self._xMast = etree.SubElement(self._dFlat, _mkTag("office", "master-styles"))
|
self._xMast = ET.SubElement(self._dFlat, _mkTag("office", "master-styles"))
|
||||||
self._xBody = etree.SubElement(self._dFlat, _mkTag("office", "body"))
|
self._xBody = ET.SubElement(self._dFlat, _mkTag("office", "body"))
|
||||||
|
|
||||||
etree.SubElement(self._xFont, _mkTag("style", "font-face"), attrib=fAttr)
|
ET.SubElement(self._xFont, _mkTag("style", "font-face"), attrib=fAttr)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
@@ -308,59 +306,59 @@ class ToOdt(Tokenizer):
|
|||||||
tStyl = _mkTag("office", "document-styles")
|
tStyl = _mkTag("office", "document-styles")
|
||||||
|
|
||||||
# content.xml
|
# content.xml
|
||||||
self._dCont = etree.Element(tCont, attrib=tAttr, nsmap=XML_NS)
|
self._dCont = ET.Element(tCont, attrib=tAttr)
|
||||||
self._xFont = etree.SubElement(self._dCont, _mkTag("office", "font-face-decls"))
|
self._xFont = ET.SubElement(self._dCont, _mkTag("office", "font-face-decls"))
|
||||||
self._xAuto = etree.SubElement(self._dCont, _mkTag("office", "automatic-styles"))
|
self._xAuto = ET.SubElement(self._dCont, _mkTag("office", "automatic-styles"))
|
||||||
self._xBody = etree.SubElement(self._dCont, _mkTag("office", "body"))
|
self._xBody = ET.SubElement(self._dCont, _mkTag("office", "body"))
|
||||||
|
|
||||||
# meta.xml
|
# meta.xml
|
||||||
self._dMeta = etree.Element(tMeta, attrib=tAttr, nsmap=XML_NS)
|
self._dMeta = ET.Element(tMeta, attrib=tAttr)
|
||||||
self._xMeta = etree.SubElement(self._dMeta, _mkTag("office", "meta"))
|
self._xMeta = ET.SubElement(self._dMeta, _mkTag("office", "meta"))
|
||||||
|
|
||||||
# styles.xml
|
# styles.xml
|
||||||
self._dStyl = etree.Element(tStyl, attrib=tAttr, nsmap=XML_NS)
|
self._dStyl = ET.Element(tStyl, attrib=tAttr)
|
||||||
self._xFnt2 = etree.SubElement(self._dStyl, _mkTag("office", "font-face-decls"))
|
self._xFnt2 = ET.SubElement(self._dStyl, _mkTag("office", "font-face-decls"))
|
||||||
self._xStyl = etree.SubElement(self._dStyl, _mkTag("office", "styles"))
|
self._xStyl = ET.SubElement(self._dStyl, _mkTag("office", "styles"))
|
||||||
self._xAut2 = etree.SubElement(self._dStyl, _mkTag("office", "automatic-styles"))
|
self._xAut2 = ET.SubElement(self._dStyl, _mkTag("office", "automatic-styles"))
|
||||||
self._xMast = etree.SubElement(self._dStyl, _mkTag("office", "master-styles"))
|
self._xMast = ET.SubElement(self._dStyl, _mkTag("office", "master-styles"))
|
||||||
|
|
||||||
etree.SubElement(self._xFont, _mkTag("style", "font-face"), attrib=fAttr)
|
ET.SubElement(self._xFont, _mkTag("style", "font-face"), attrib=fAttr)
|
||||||
etree.SubElement(self._xFnt2, _mkTag("style", "font-face"), attrib=fAttr)
|
ET.SubElement(self._xFnt2, _mkTag("style", "font-face"), attrib=fAttr)
|
||||||
|
|
||||||
# Finalise
|
# Finalise
|
||||||
# ========
|
# ========
|
||||||
|
|
||||||
self._xText = etree.SubElement(self._xBody, _mkTag("office", "text"))
|
self._xText = ET.SubElement(self._xBody, _mkTag("office", "text"))
|
||||||
|
|
||||||
timeStamp = datetime.now().isoformat(sep="T", timespec="seconds")
|
timeStamp = datetime.now().isoformat(sep="T", timespec="seconds")
|
||||||
|
|
||||||
# Office Meta Data
|
# Office Meta Data
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "creation-date"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "creation-date"))
|
||||||
xMeta.text = timeStamp
|
xMeta.text = timeStamp
|
||||||
|
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "generator"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "generator"))
|
||||||
xMeta.text = f"novelWriter/{__version__}"
|
xMeta.text = f"novelWriter/{__version__}"
|
||||||
|
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "initial-creator"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "initial-creator"))
|
||||||
xMeta.text = self.theProject.data.author
|
xMeta.text = self.theProject.data.author
|
||||||
|
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "editing-cycles"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "editing-cycles"))
|
||||||
xMeta.text = str(self.theProject.data.saveCount)
|
xMeta.text = str(self.theProject.data.saveCount)
|
||||||
|
|
||||||
# Format is: PnYnMnDTnHnMnS
|
# Format is: PnYnMnDTnHnMnS
|
||||||
# https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#duration
|
# https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#duration
|
||||||
eT = self.theProject.data.editTime
|
eT = self.theProject.data.editTime
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "editing-duration"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "editing-duration"))
|
||||||
xMeta.text = f"P{eT//86400:d}DT{eT%86400//3600:d}H{eT%3600//60:d}M{eT%60:d}S"
|
xMeta.text = f"P{eT//86400:d}DT{eT%86400//3600:d}H{eT%3600//60:d}M{eT%60:d}S"
|
||||||
|
|
||||||
# Dublin Core Meta Data
|
# Dublin Core Meta Data
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("dc", "title"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "title"))
|
||||||
xMeta.text = self.theProject.data.title or self.theProject.data.name
|
xMeta.text = self.theProject.data.title or self.theProject.data.name
|
||||||
|
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("dc", "date"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "date"))
|
||||||
xMeta.text = timeStamp
|
xMeta.text = timeStamp
|
||||||
|
|
||||||
xMeta = etree.SubElement(self._xMeta, _mkTag("dc", "creator"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "creator"))
|
||||||
xMeta.text = self.theProject.data.author
|
xMeta.text = self.theProject.data.author
|
||||||
|
|
||||||
self._pageStyles()
|
self._pageStyles()
|
||||||
@@ -506,51 +504,43 @@ class ToOdt(Tokenizer):
|
|||||||
"""Save the data to an .fodt file.
|
"""Save the data to an .fodt file.
|
||||||
"""
|
"""
|
||||||
with open(savePath, mode="wb") as outFile:
|
with open(savePath, mode="wb") as outFile:
|
||||||
outFile.write(etree.tostring(
|
xml = ET.ElementTree(self._dFlat)
|
||||||
self._dFlat,
|
xmlIndent(xml)
|
||||||
pretty_print=True,
|
xml.write(outFile, encoding="utf-8", xml_declaration=True)
|
||||||
encoding="utf-8",
|
|
||||||
xml_declaration=True
|
|
||||||
))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveOpenDocText(self, savePath):
|
def saveOpenDocText(self, savePath):
|
||||||
"""Save the data to an .odt file.
|
"""Save the data to an .odt file.
|
||||||
"""
|
"""
|
||||||
mMani = _mkTag("manifest", "manifest", nsMap=MANI_NS)
|
mMani = _mkTag("manifest", "manifest")
|
||||||
mVers = _mkTag("manifest", "version", nsMap=MANI_NS)
|
mVers = _mkTag("manifest", "version")
|
||||||
mPath = _mkTag("manifest", "full-path", nsMap=MANI_NS)
|
mPath = _mkTag("manifest", "full-path")
|
||||||
mType = _mkTag("manifest", "media-type", nsMap=MANI_NS)
|
mType = _mkTag("manifest", "media-type")
|
||||||
mFile = _mkTag("manifest", "file-entry", nsMap=MANI_NS)
|
mFile = _mkTag("manifest", "file-entry")
|
||||||
|
|
||||||
xMani = etree.Element(mMani, attrib={mVers: X_VERS}, nsmap=MANI_NS)
|
xMani = ET.Element(mMani, attrib={mVers: X_VERS})
|
||||||
etree.SubElement(xMani, mFile, attrib={mPath: "/", mVers: X_VERS, mType: X_MIME})
|
ET.SubElement(xMani, mFile, attrib={mPath: "/", mVers: X_VERS, mType: X_MIME})
|
||||||
etree.SubElement(xMani, mFile, attrib={mPath: "settings.xml", mType: "text/xml"})
|
ET.SubElement(xMani, mFile, attrib={mPath: "settings.xml", mType: "text/xml"})
|
||||||
etree.SubElement(xMani, mFile, attrib={mPath: "content.xml", mType: "text/xml"})
|
ET.SubElement(xMani, mFile, attrib={mPath: "content.xml", mType: "text/xml"})
|
||||||
etree.SubElement(xMani, mFile, attrib={mPath: "meta.xml", mType: "text/xml"})
|
ET.SubElement(xMani, mFile, attrib={mPath: "meta.xml", mType: "text/xml"})
|
||||||
etree.SubElement(xMani, mFile, attrib={mPath: "styles.xml", mType: "text/xml"})
|
ET.SubElement(xMani, mFile, attrib={mPath: "styles.xml", mType: "text/xml"})
|
||||||
|
|
||||||
oRoot = _mkTag("office", "document-settings", nsMap=OFFICE_NS)
|
oRoot = _mkTag("office", "document-settings")
|
||||||
oVers = _mkTag("office", "version", nsMap=OFFICE_NS)
|
oVers = _mkTag("office", "version")
|
||||||
xSett = etree.Element(oRoot, nsmap=OFFICE_NS, attrib={oVers: X_VERS})
|
xSett = ET.Element(oRoot, attrib={oVers: X_VERS})
|
||||||
|
|
||||||
with ZipFile(savePath, mode="w") as outFile:
|
def putInZip(name, xObj, zipObj):
|
||||||
outFile.writestr("mimetype", X_MIME)
|
with zipObj.open(name, mode="w") as fObj:
|
||||||
outFile.writestr("META-INF/manifest.xml", etree.tostring(
|
xml = ET.ElementTree(xObj)
|
||||||
xMani, pretty_print=False, encoding="utf-8", xml_declaration=True
|
xml.write(fObj, encoding="utf-8", xml_declaration=True)
|
||||||
))
|
|
||||||
outFile.writestr("settings.xml", etree.tostring(
|
with ZipFile(savePath, mode="w") as outZip:
|
||||||
xSett, pretty_print=False, encoding="utf-8", xml_declaration=True
|
outZip.writestr("mimetype", X_MIME)
|
||||||
))
|
putInZip("META-INF/manifest.xml", xMani, outZip)
|
||||||
outFile.writestr("content.xml", etree.tostring(
|
putInZip("settings.xml", xSett, outZip)
|
||||||
self._dCont, pretty_print=False, encoding="utf-8", xml_declaration=True
|
putInZip("content.xml", self._dCont, outZip)
|
||||||
))
|
putInZip("meta.xml", self._dMeta, outZip)
|
||||||
outFile.writestr("meta.xml", etree.tostring(
|
putInZip("styles.xml", self._dStyl, outZip)
|
||||||
self._dMeta, pretty_print=False, encoding="utf-8", xml_declaration=True
|
|
||||||
))
|
|
||||||
outFile.writestr("styles.xml", etree.tostring(
|
|
||||||
self._dStyl, pretty_print=False, encoding="utf-8", xml_declaration=True
|
|
||||||
))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -607,10 +597,10 @@ class ToOdt(Tokenizer):
|
|||||||
tAttr[_mkTag("text", "outline-level")] = oLevel
|
tAttr[_mkTag("text", "outline-level")] = oLevel
|
||||||
|
|
||||||
pTag = "h" if isHead else "p"
|
pTag = "h" if isHead else "p"
|
||||||
xElem = etree.SubElement(self._xText, _mkTag("text", pTag), attrib=tAttr)
|
xElem = ET.SubElement(self._xText, _mkTag("text", pTag), attrib=tAttr)
|
||||||
|
|
||||||
# It's important to set the initial text field to empty, otherwise
|
# It's important to set the initial text field to empty, otherwise
|
||||||
# lxml will add a line break if the first subelement is a span.
|
# xmlIndent will add a line break if the first subelement is a span.
|
||||||
xElem.text = ""
|
xElem.text = ""
|
||||||
|
|
||||||
if not theText:
|
if not theText:
|
||||||
@@ -735,25 +725,25 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = "PM1"
|
theAttr[_mkTag("style", "name")] = "PM1"
|
||||||
if self._isFlat:
|
if self._isFlat:
|
||||||
xPage = etree.SubElement(self._xAuto, _mkTag("style", "page-layout"), attrib=theAttr)
|
xPage = ET.SubElement(self._xAuto, _mkTag("style", "page-layout"), attrib=theAttr)
|
||||||
else:
|
else:
|
||||||
xPage = etree.SubElement(self._xAut2, _mkTag("style", "page-layout"), attrib=theAttr)
|
xPage = ET.SubElement(self._xAut2, _mkTag("style", "page-layout"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("fo", "margin-top")] = self._mDocTop
|
theAttr[_mkTag("fo", "margin-top")] = self._mDocTop
|
||||||
theAttr[_mkTag("fo", "margin-bottom")] = self._mDocBtm
|
theAttr[_mkTag("fo", "margin-bottom")] = self._mDocBtm
|
||||||
theAttr[_mkTag("fo", "margin-left")] = self._mDocLeft
|
theAttr[_mkTag("fo", "margin-left")] = self._mDocLeft
|
||||||
theAttr[_mkTag("fo", "margin-right")] = self._mDocRight
|
theAttr[_mkTag("fo", "margin-right")] = self._mDocRight
|
||||||
etree.SubElement(xPage, _mkTag("style", "page-layout-properties"), attrib=theAttr)
|
ET.SubElement(xPage, _mkTag("style", "page-layout-properties"), attrib=theAttr)
|
||||||
|
|
||||||
xHead = etree.SubElement(xPage, _mkTag("style", "header-style"))
|
xHead = ET.SubElement(xPage, _mkTag("style", "header-style"))
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("fo", "min-height")] = "0.600cm"
|
theAttr[_mkTag("fo", "min-height")] = "0.600cm"
|
||||||
theAttr[_mkTag("fo", "margin-left")] = "0.000cm"
|
theAttr[_mkTag("fo", "margin-left")] = "0.000cm"
|
||||||
theAttr[_mkTag("fo", "margin-right")] = "0.000cm"
|
theAttr[_mkTag("fo", "margin-right")] = "0.000cm"
|
||||||
theAttr[_mkTag("fo", "margin-bottom")] = "0.500cm"
|
theAttr[_mkTag("fo", "margin-bottom")] = "0.500cm"
|
||||||
etree.SubElement(xHead, _mkTag("style", "header-footer-properties"), attrib=theAttr)
|
ET.SubElement(xHead, _mkTag("style", "header-footer-properties"), attrib=theAttr)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -765,13 +755,13 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
theAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib=theAttr)
|
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "line-break")] = "strict"
|
theAttr[_mkTag("style", "line-break")] = "strict"
|
||||||
theAttr[_mkTag("style", "tab-stop-distance")] = "1.251cm"
|
theAttr[_mkTag("style", "tab-stop-distance")] = "1.251cm"
|
||||||
theAttr[_mkTag("style", "writing-mode")] = "page"
|
theAttr[_mkTag("style", "writing-mode")] = "page"
|
||||||
etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "font-name")] = self._textFont
|
theAttr[_mkTag("style", "font-name")] = self._textFont
|
||||||
@@ -779,7 +769,7 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
theAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
||||||
theAttr[_mkTag("fo", "language")] = self._dLanguage
|
theAttr[_mkTag("fo", "language")] = self._dLanguage
|
||||||
theAttr[_mkTag("fo", "country")] = self._dCountry
|
theAttr[_mkTag("fo", "country")] = self._dCountry
|
||||||
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
# Add Standard Paragraph Style
|
# Add Standard Paragraph Style
|
||||||
# ============================
|
# ============================
|
||||||
@@ -788,13 +778,13 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("style", "name")] = "Standard"
|
theAttr[_mkTag("style", "name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
theAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
theAttr[_mkTag("style", "class")] = "text"
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "font-name")] = self._textFont
|
theAttr[_mkTag("style", "font-name")] = self._textFont
|
||||||
theAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
theAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
||||||
theAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
theAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
||||||
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
# Add Default Heading Style
|
# Add Default Heading Style
|
||||||
# =========================
|
# =========================
|
||||||
@@ -805,19 +795,19 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
theAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "next-style-name")] = "Text_20_body"
|
theAttr[_mkTag("style", "next-style-name")] = "Text_20_body"
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
theAttr[_mkTag("style", "class")] = "text"
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("fo", "margin-top")] = self._mTopHead
|
theAttr[_mkTag("fo", "margin-top")] = self._mTopHead
|
||||||
theAttr[_mkTag("fo", "margin-bottom")] = self._mBotHead
|
theAttr[_mkTag("fo", "margin-bottom")] = self._mBotHead
|
||||||
theAttr[_mkTag("fo", "keep-with-next")] = "always"
|
theAttr[_mkTag("fo", "keep-with-next")] = "always"
|
||||||
etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "font-name")] = self._textFont
|
theAttr[_mkTag("style", "font-name")] = self._textFont
|
||||||
theAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
theAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
||||||
theAttr[_mkTag("fo", "font-size")] = self._fSizeHead
|
theAttr[_mkTag("fo", "font-size")] = self._fSizeHead
|
||||||
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
# Add Header and Footer Styles
|
# Add Header and Footer Styles
|
||||||
# ============================
|
# ============================
|
||||||
@@ -827,7 +817,7 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
theAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
theAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "class")] = "extra"
|
theAttr[_mkTag("style", "class")] = "extra"
|
||||||
etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -992,23 +982,24 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = "Standard"
|
theAttr[_mkTag("style", "name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "page-layout-name")] = "PM1"
|
theAttr[_mkTag("style", "page-layout-name")] = "PM1"
|
||||||
xPage = etree.SubElement(self._xMast, _mkTag("style", "master-page"), attrib=theAttr)
|
xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib=theAttr)
|
||||||
|
|
||||||
# Standard Page Header
|
# Standard Page Header
|
||||||
xHead = etree.SubElement(xPage, _mkTag("style", "header"))
|
xHead = ET.SubElement(xPage, _mkTag("style", "header"))
|
||||||
xPar = etree.SubElement(xHead, _mkTag("text", "p"), attrib={
|
xPar = ET.SubElement(xHead, _mkTag("text", "p"), attrib={
|
||||||
_mkTag("text", "style-name"): "Header"
|
_mkTag("text", "style-name"): "Header"
|
||||||
})
|
})
|
||||||
xPar.text = self._headerText.strip() + " "
|
xPar.text = self._headerText.strip() + " "
|
||||||
|
|
||||||
xTail = etree.SubElement(xPar, _mkTag("text", "page-number"), attrib={
|
xTail = ET.SubElement(xPar, _mkTag("text", "page-number"), attrib={
|
||||||
_mkTag("text", "select-page"): "current"
|
_mkTag("text", "select-page"): "current"
|
||||||
})
|
})
|
||||||
xTail.text = "2"
|
xTail.text = "2"
|
||||||
|
xTail.tail = "" # Prevent line break in indented XML
|
||||||
|
|
||||||
# First Page Header
|
# First Page Header
|
||||||
xHead = etree.SubElement(xPage, _mkTag("style", "header-first"))
|
xHead = ET.SubElement(xPage, _mkTag("style", "header-first"))
|
||||||
xPar = etree.SubElement(xHead, _mkTag("text", "p"), attrib={
|
xPar = ET.SubElement(xHead, _mkTag("text", "p"), attrib={
|
||||||
_mkTag("text", "style-name"): "Header"
|
_mkTag("text", "style-name"): "Header"
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1212,7 +1203,7 @@ class ODTParagraphStyle:
|
|||||||
if aVal is not None:
|
if aVal is not None:
|
||||||
theAttr[_mkTag(aNm, aName)] = aVal
|
theAttr[_mkTag(aNm, aName)] = aVal
|
||||||
|
|
||||||
xEntry = etree.SubElement(xParent, _mkTag("style", "style"), attrib=theAttr)
|
xEntry = ET.SubElement(xParent, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
for aName, (aNm, aVal) in self._pAttr.items():
|
for aName, (aNm, aVal) in self._pAttr.items():
|
||||||
@@ -1220,7 +1211,7 @@ class ODTParagraphStyle:
|
|||||||
theAttr[_mkTag(aNm, aName)] = aVal
|
theAttr[_mkTag(aNm, aName)] = aVal
|
||||||
|
|
||||||
if theAttr:
|
if theAttr:
|
||||||
etree.SubElement(xEntry, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
ET.SubElement(xEntry, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
for aName, (aNm, aVal) in self._tAttr.items():
|
for aName, (aNm, aVal) in self._tAttr.items():
|
||||||
@@ -1228,7 +1219,7 @@ class ODTParagraphStyle:
|
|||||||
theAttr[_mkTag(aNm, aName)] = aVal
|
theAttr[_mkTag(aNm, aName)] = aVal
|
||||||
|
|
||||||
if theAttr:
|
if theAttr:
|
||||||
etree.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1299,7 +1290,7 @@ class ODTTextStyle:
|
|||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = xName
|
theAttr[_mkTag("style", "name")] = xName
|
||||||
theAttr[_mkTag("style", "family")] = "text"
|
theAttr[_mkTag("style", "family")] = "text"
|
||||||
xEntry = etree.SubElement(xParent, _mkTag("style", "style"), attrib=theAttr)
|
xEntry = ET.SubElement(xParent, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
for aName, (aNm, aVal) in self._tAttr.items():
|
for aName, (aNm, aVal) in self._tAttr.items():
|
||||||
@@ -1307,7 +1298,7 @@ class ODTTextStyle:
|
|||||||
theAttr[_mkTag(aNm, aName)] = aVal
|
theAttr[_mkTag(aNm, aName)] = aVal
|
||||||
|
|
||||||
if theAttr:
|
if theAttr:
|
||||||
etree.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1328,8 +1319,6 @@ class XMLParagraph:
|
|||||||
"""This is a helper class to manage the text content of a single
|
"""This is a helper class to manage the text content of a single
|
||||||
XML element using mixed content tags.
|
XML element using mixed content tags.
|
||||||
|
|
||||||
See: https://lxml.de/tutorial.html#the-element-class
|
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
* The root tag can only have text set, never tail.
|
* The root tag can only have text set, never tail.
|
||||||
* Any span must be under root, and the text in the span is set in
|
* Any span must be under root, and the text in the span is set in
|
||||||
@@ -1349,8 +1338,8 @@ class XMLParagraph:
|
|||||||
def __init__(self, xRoot):
|
def __init__(self, xRoot):
|
||||||
|
|
||||||
self._xRoot = xRoot
|
self._xRoot = xRoot
|
||||||
self._xTail = None
|
self._xTail = ET.Element("")
|
||||||
self._xSing = None
|
self._xSing = ET.Element("")
|
||||||
|
|
||||||
self._nState = X_ROOT_TEXT
|
self._nState = X_ROOT_TEXT
|
||||||
self._chrPos = 0
|
self._chrPos = 0
|
||||||
@@ -1380,26 +1369,26 @@ class XMLParagraph:
|
|||||||
|
|
||||||
if c == "\n":
|
if c == "\n":
|
||||||
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
||||||
self._xTail = etree.SubElement(self._xRoot, TAG_BR)
|
self._xTail = ET.SubElement(self._xRoot, TAG_BR)
|
||||||
self._xTail.tail = ""
|
self._xTail.tail = ""
|
||||||
self._nState = X_ROOT_TAIL
|
self._nState = X_ROOT_TAIL
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
|
|
||||||
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
||||||
self._xSing = etree.SubElement(self._xTail, TAG_BR)
|
self._xSing = ET.SubElement(self._xTail, TAG_BR)
|
||||||
self._xSing.tail = ""
|
self._xSing.tail = ""
|
||||||
self._nState = X_SPAN_SING
|
self._nState = X_SPAN_SING
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
|
|
||||||
elif c == "\t":
|
elif c == "\t":
|
||||||
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
||||||
self._xTail = etree.SubElement(self._xRoot, TAG_TAB)
|
self._xTail = ET.SubElement(self._xRoot, TAG_TAB)
|
||||||
self._xTail.tail = ""
|
self._xTail.tail = ""
|
||||||
self._nState = X_ROOT_TAIL
|
self._nState = X_ROOT_TAIL
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
|
|
||||||
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
||||||
self._xSing = etree.SubElement(self._xTail, TAG_TAB)
|
self._xSing = ET.SubElement(self._xTail, TAG_TAB)
|
||||||
self._xSing.tail = ""
|
self._xSing.tail = ""
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
self._nState = X_SPAN_SING
|
self._nState = X_SPAN_SING
|
||||||
@@ -1429,7 +1418,7 @@ class XMLParagraph:
|
|||||||
Therefore we return to the root element level when we're done
|
Therefore we return to the root element level when we're done
|
||||||
processing the text of the span.
|
processing the text of the span.
|
||||||
"""
|
"""
|
||||||
self._xTail = etree.SubElement(self._xRoot, TAG_SPAN, attrib={
|
self._xTail = ET.SubElement(self._xRoot, TAG_SPAN, attrib={
|
||||||
TAG_STNM: tFmt
|
TAG_STNM: tFmt
|
||||||
})
|
})
|
||||||
self._xTail.text = "" # Defaults to None
|
self._xTail.text = "" # Defaults to None
|
||||||
@@ -1482,20 +1471,20 @@ class XMLParagraph:
|
|||||||
|
|
||||||
if nSpaces == 2:
|
if nSpaces == 2:
|
||||||
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
||||||
self._xTail = etree.SubElement(self._xRoot, TAG_SPC)
|
self._xTail = ET.SubElement(self._xRoot, TAG_SPC)
|
||||||
self._xTail.tail = ""
|
self._xTail.tail = ""
|
||||||
self._nState = X_ROOT_TAIL
|
self._nState = X_ROOT_TAIL
|
||||||
self._chrPos += nSpaces - 1
|
self._chrPos += nSpaces - 1
|
||||||
|
|
||||||
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
||||||
self._xSing = etree.SubElement(self._xTail, TAG_SPC)
|
self._xSing = ET.SubElement(self._xTail, TAG_SPC)
|
||||||
self._xSing.tail = ""
|
self._xSing.tail = ""
|
||||||
self._nState = X_SPAN_SING
|
self._nState = X_SPAN_SING
|
||||||
self._chrPos += nSpaces - 1
|
self._chrPos += nSpaces - 1
|
||||||
|
|
||||||
elif nSpaces > 2:
|
elif nSpaces > 2:
|
||||||
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL):
|
||||||
self._xTail = etree.SubElement(self._xRoot, TAG_SPC, attrib={
|
self._xTail = ET.SubElement(self._xRoot, TAG_SPC, attrib={
|
||||||
TAG_NSPC: str(nSpaces - 1)
|
TAG_NSPC: str(nSpaces - 1)
|
||||||
})
|
})
|
||||||
self._xTail.tail = ""
|
self._xTail.tail = ""
|
||||||
@@ -1503,7 +1492,7 @@ class XMLParagraph:
|
|||||||
self._chrPos += nSpaces - 1
|
self._chrPos += nSpaces - 1
|
||||||
|
|
||||||
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
elif self._nState in (X_SPAN_TEXT, X_SPAN_SING):
|
||||||
self._xSing = etree.SubElement(self._xTail, TAG_SPC, attrib={
|
self._xSing = ET.SubElement(self._xTail, TAG_SPC, attrib={
|
||||||
TAG_NSPC: str(nSpaces - 1)
|
TAG_NSPC: str(nSpaces - 1)
|
||||||
})
|
})
|
||||||
self._xSing.tail = ""
|
self._xSing.tail = ""
|
||||||
|
|||||||
@@ -128,12 +128,6 @@ class NWErrorMessage(QDialog):
|
|||||||
except Exception:
|
except Exception:
|
||||||
kernelVersion = "Unknown"
|
kernelVersion = "Unknown"
|
||||||
|
|
||||||
try:
|
|
||||||
import lxml
|
|
||||||
lxmlVersion = lxml.__version__ # type: ignore
|
|
||||||
except Exception:
|
|
||||||
lxmlVersion = "Unknown"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import enchant
|
import enchant
|
||||||
enchantVersion = enchant.__version__
|
enchantVersion = enchant.__version__
|
||||||
@@ -148,7 +142,6 @@ class NWErrorMessage(QDialog):
|
|||||||
f"Host OS: {sys.platform} ({kernelVersion})\n"
|
f"Host OS: {sys.platform} ({kernelVersion})\n"
|
||||||
f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n"
|
f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n"
|
||||||
f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n"
|
f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n"
|
||||||
f"lxml: {lxmlVersion}\n"
|
|
||||||
f"enchant: {enchantVersion}\n\n"
|
f"enchant: {enchantVersion}\n\n"
|
||||||
f"{exType.__name__}:\n{str(exValue)}\n\n"
|
f"{exType.__name__}:\n{str(exValue)}\n\n"
|
||||||
f"Traceback:\n{exTrace}\n"
|
f"Traceback:\n{exTrace}\n"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@@ -31,10 +32,10 @@ from pathlib import Path
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot
|
from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot
|
||||||
from PyQt5.QtGui import QIcon, QKeySequence, QCursor
|
from PyQt5.QtGui import QCursor, QIcon, QKeySequence
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QMainWindow, QVBoxLayout, QWidget, QSplitter, QFileDialog, QShortcut,
|
qApp, QDialog, QFileDialog, QMainWindow, QMessageBox, QShortcut, QSplitter,
|
||||||
QMessageBox, QDialog, QStackedWidget
|
QStackedWidget, QVBoxLayout, QWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, __hexversion__
|
from novelwriter import CONFIG, __hexversion__
|
||||||
@@ -89,7 +90,7 @@ class GuiMain(QMainWindow):
|
|||||||
logger.info("Host: %s", CONFIG.hostName)
|
logger.info("Host: %s", CONFIG.hostName)
|
||||||
logger.info("Qt5: %s (0x%06x)", CONFIG.verQtString, CONFIG.verQtValue)
|
logger.info("Qt5: %s (0x%06x)", CONFIG.verQtString, CONFIG.verQtValue)
|
||||||
logger.info("PyQt5: %s (0x%06x)", CONFIG.verPyQtString, CONFIG.verPyQtValue)
|
logger.info("PyQt5: %s (0x%06x)", CONFIG.verPyQtString, CONFIG.verPyQtValue)
|
||||||
logger.info("Python: %s (0x%08x)", CONFIG.verPyString, CONFIG.verPyHexVal)
|
logger.info("Python: %s (0x%08x)", CONFIG.verPyString, sys.hexversion)
|
||||||
logger.info("GUI Language: %s", CONFIG.guiLocale)
|
logger.info("GUI Language: %s", CONFIG.guiLocale)
|
||||||
|
|
||||||
# Core Classes
|
# Core Classes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-04-15 20:52:06">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 21:29:35">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1475" autoCount="237" editTime="74579">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1505" autoCount="237" editTime="74902">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<entry key="chapter">Chapter %chw%: %title%</entry>
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
<meta expanded="no" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="1369" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ include_package_data = True
|
|||||||
packages = find_namespace:
|
packages = find_namespace:
|
||||||
install_requires =
|
install_requires =
|
||||||
pyqt5>=5.10
|
pyqt5>=5.10
|
||||||
lxml>=4.2.0
|
|
||||||
pyenchant>=3.0.0
|
pyenchant>=3.0.0
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ Source: novelwriter
|
|||||||
Maintainer: Veronica Berglyd Olsen <code@vkbo.net>
|
Maintainer: Veronica Berglyd Olsen <code@vkbo.net>
|
||||||
Section: text
|
Section: text
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: dh-python, python3-setuptools, python3-all, debhelper (>= 9), python3 (>=3.7), python3-pyqt5 (>= 5.10), python3-lxml (>= 4.0), python3-enchant (>= 2.0)
|
Build-Depends: dh-python, python3-setuptools, python3-all, debhelper (>= 9), python3 (>=3.7), python3-pyqt5 (>= 5.10), python3-enchant (>= 2.0)
|
||||||
Standards-Version: 4.5.1
|
Standards-Version: 4.5.1
|
||||||
Homepage: https://novelwriter.io
|
Homepage: https://novelwriter.io
|
||||||
X-Python3-Version: >= 3.7
|
X-Python3-Version: >= 3.7
|
||||||
|
|
||||||
Package: novelwriter
|
Package: novelwriter
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${misc:Depends}, ${python3:Depends}, python3 (>=3.7), python3-pyqt5 (>= 5.10), python3-lxml (>= 4.0), python3-enchant (>= 2.0)
|
Depends: ${misc:Depends}, ${python3:Depends}, python3 (>=3.7), python3-pyqt5 (>= 5.10), python3-enchant (>= 2.0)
|
||||||
Description: A markdown-like text editor for planning and writing novels
|
Description: A markdown-like text editor for planning and writing novels
|
||||||
novelWriter is a plain text editor designed for writing novels assembled from
|
novelWriter is a plain text editor designed for writing novels assembled from
|
||||||
many smaller text documents. It uses a minimal formatting syntax inspired by
|
many smaller text documents. It uses a minimal formatting syntax inspired by
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ if exist setup.py (
|
|||||||
)
|
)
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
:: Install the PyQt5, lxml and pyenchant dependencies
|
:: Install the PyQt5 and pyenchant dependencies
|
||||||
pip install --user pywin32 -r requirements.txt
|
pip install --user pywin32 -r requirements.txt
|
||||||
|
|
||||||
:: Create the desktop and start menu icons
|
:: Create the desktop and start menu icons
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ goto afterUninst
|
|||||||
:: Remove the desktop and start menu icons
|
:: Remove the desktop and start menu icons
|
||||||
python setup.py win-uninstall
|
python setup.py win-uninstall
|
||||||
|
|
||||||
:: Remove the PyQt5, lxml and pyenchant dependencies
|
:: Remove the PyQt5 and pyenchant dependencies
|
||||||
pip uninstall --yes -r requirements.txt
|
pip uninstall --yes -r requirements.txt
|
||||||
|
|
||||||
:afterUninst
|
:afterUninst
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<entry key="chapter">Chapter %chw%: %title%</entry>
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:00:43">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:10:19">
|
||||||
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="38" autoCount="24" editTime="1900">
|
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="40" autoCount="24" editTime="1905">
|
||||||
<name>Lorem Ipsum</name>
|
<name>Lorem Ipsum</name>
|
||||||
<title>Lorem Ipsum</title>
|
<title>Lorem Ipsum</title>
|
||||||
<author>lipsum.com</author>
|
<author>lipsum.com</author>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<entry key="chapter">Chapter %ch%: %title%</entry>
|
<entry key="chapter">Chapter %ch%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sbaa94f" count="3" red="100" green="100" blue="100">New</entry>
|
<entry key="sbaa94f" count="3" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:05:22">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:13:44">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<entry key="chapter">%title%</entry>
|
<entry key="chapter">%title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:05:22">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:13:44">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<entry key="chapter">%title%</entry>
|
<entry key="chapter">%title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||||
<office:meta>
|
<office:meta>
|
||||||
<meta:creation-date>2023-02-12T15:10:11</meta:creation-date>
|
<meta:creation-date>2023-05-29T22:10:15</meta:creation-date>
|
||||||
<meta:generator>novelWriter/2.0.4</meta:generator>
|
<meta:generator>novelWriter/2.0.7</meta:generator>
|
||||||
<meta:initial-creator>Jane Smith</meta:initial-creator>
|
<meta:initial-creator>Jane Smith</meta:initial-creator>
|
||||||
<meta:editing-cycles>1234</meta:editing-cycles>
|
<meta:editing-cycles>1234</meta:editing-cycles>
|
||||||
<meta:editing-duration>P42DT12H34M56S</meta:editing-duration>
|
<meta:editing-duration>P42DT12H34M56S</meta:editing-duration>
|
||||||
<dc:title>Test Project</dc:title>
|
<dc:title>Test Project</dc:title>
|
||||||
<dc:date>2023-02-12T15:10:11</dc:date>
|
<dc:date>2023-05-29T22:10:15</dc:date>
|
||||||
<dc:creator>Jane Smith</dc:creator>
|
<dc:creator>Jane Smith</dc:creator>
|
||||||
</office:meta>
|
</office:meta>
|
||||||
<office:font-face-decls>
|
<office:font-face-decls>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3">
|
<office:document-content xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3">
|
||||||
<office:font-face-decls>
|
<office:font-face-decls>
|
||||||
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
|
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
|
||||||
</office:font-face-decls>
|
</office:font-face-decls>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3">
|
<office:document-meta xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:version="1.3">
|
||||||
<office:meta>
|
<office:meta>
|
||||||
<meta:creation-date>2023-02-12T15:09:03</meta:creation-date>
|
<meta:creation-date>2023-05-29T19:48:33</meta:creation-date>
|
||||||
<meta:generator>novelWriter/2.0.4</meta:generator>
|
<meta:generator>novelWriter/2.0.7</meta:generator>
|
||||||
<meta:initial-creator>Jane Smith</meta:initial-creator>
|
<meta:initial-creator>Jane Smith</meta:initial-creator>
|
||||||
<meta:editing-cycles>1234</meta:editing-cycles>
|
<meta:editing-cycles>1234</meta:editing-cycles>
|
||||||
<meta:editing-duration>P42DT12H34M56S</meta:editing-duration>
|
<meta:editing-duration>P42DT12H34M56S</meta:editing-duration>
|
||||||
<dc:title>Test Project</dc:title>
|
<dc:title>Test Project</dc:title>
|
||||||
<dc:date>2023-02-12T15:09:03</dc:date>
|
<dc:date>2023-05-29T19:48:33</dc:date>
|
||||||
<dc:creator>Jane Smith</dc:creator>
|
<dc:creator>Jane Smith</dc:creator>
|
||||||
</office:meta>
|
</office:meta>
|
||||||
</office:document-meta>
|
</office:document-meta>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3">
|
<office:document-styles xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3">
|
||||||
<office:font-face-decls>
|
<office:font-face-decls>
|
||||||
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
|
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
|
||||||
</office:font-face-decls>
|
</office:font-face-decls>
|
||||||
@@ -59,7 +59,8 @@
|
|||||||
<office:master-styles>
|
<office:master-styles>
|
||||||
<style:master-page style:name="Standard" style:page-layout-name="PM1">
|
<style:master-page style:name="Standard" style:page-layout-name="PM1">
|
||||||
<style:header>
|
<style:header>
|
||||||
<text:p text:style-name="Header">Test Project / Jane Smith / <text:page-number text:select-page="current">2</text:page-number></text:p>
|
<text:p text:style-name="Header">Test Project / Jane Smith / <text:page-number text:select-page="current">2</text:page-number>
|
||||||
|
</text:p>
|
||||||
</style:header>
|
</style:header>
|
||||||
<style:header-first>
|
<style:header-first>
|
||||||
<text:p text:style-name="Header" />
|
<text:p text:style-name="Header" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc2" hexVersion="0x020000c2" fileVersion="1.5" timeStamp="2022-11-27 16:39:14">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:01:04">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<entry key="chapter">%title%</entry>
|
<entry key="chapter">%title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="15" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="15" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc2" hexVersion="0x020000c2" fileVersion="1.5" timeStamp="2022-11-27 16:39:14">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:01:04">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<entry key="chapter">%title%</entry>
|
<entry key="chapter">%title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="9" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="9" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc2" hexVersion="0x020000c2" fileVersion="1.5" timeStamp="2022-11-27 16:39:14">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:01:04">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Project</title>
|
<title>New Project</title>
|
||||||
<author></author>
|
<author />
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>yes</doBackup>
|
<doBackup>yes</doBackup>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<entry key="chapter">%title%</entry>
|
<entry key="chapter">%title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||||
<office:meta>
|
<office:meta>
|
||||||
<meta:creation-date>2023-02-12T14:52:03</meta:creation-date>
|
<meta:creation-date>2023-05-29T22:10:29</meta:creation-date>
|
||||||
<meta:generator>novelWriter/2.0.4</meta:generator>
|
<meta:generator>novelWriter/2.0.7</meta:generator>
|
||||||
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
||||||
<meta:editing-cycles>38</meta:editing-cycles>
|
<meta:editing-cycles>40</meta:editing-cycles>
|
||||||
<meta:editing-duration>P0DT0H31M40S</meta:editing-duration>
|
<meta:editing-duration>P0DT0H31M45S</meta:editing-duration>
|
||||||
<dc:title>Lorem Ipsum</dc:title>
|
<dc:title>Lorem Ipsum</dc:title>
|
||||||
<dc:date>2023-02-12T14:52:03</dc:date>
|
<dc:date>2023-05-29T22:10:29</dc:date>
|
||||||
<dc:creator>lipsum.com</dc:creator>
|
<dc:creator>lipsum.com</dc:creator>
|
||||||
</office:meta>
|
</office:meta>
|
||||||
<office:font-face-decls>
|
<office:font-face-decls>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||||
<office:meta>
|
<office:meta>
|
||||||
<meta:creation-date>2023-02-12T15:00:22</meta:creation-date>
|
<meta:creation-date>2023-05-29T22:11:22</meta:creation-date>
|
||||||
<meta:generator>novelWriter/2.0.4</meta:generator>
|
<meta:generator>novelWriter/2.0.7</meta:generator>
|
||||||
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
||||||
<meta:editing-cycles>38</meta:editing-cycles>
|
<meta:editing-cycles>40</meta:editing-cycles>
|
||||||
<meta:editing-duration>P0DT0H31M40S</meta:editing-duration>
|
<meta:editing-duration>P0DT0H31M45S</meta:editing-duration>
|
||||||
<dc:title>Lorem Ipsum</dc:title>
|
<dc:title>Lorem Ipsum</dc:title>
|
||||||
<dc:date>2023-02-12T15:00:22</dc:date>
|
<dc:date>2023-05-29T22:11:22</dc:date>
|
||||||
<dc:creator>lipsum.com</dc:creator>
|
<dc:creator>lipsum.com</dc:creator>
|
||||||
</office:meta>
|
</office:meta>
|
||||||
<office:font-face-decls>
|
<office:font-face-decls>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||||
<office:meta>
|
<office:meta>
|
||||||
<meta:creation-date>2023-02-12T15:01:25</meta:creation-date>
|
<meta:creation-date>2023-05-29T22:11:58</meta:creation-date>
|
||||||
<meta:generator>novelWriter/2.0.4</meta:generator>
|
<meta:generator>novelWriter/2.0.7</meta:generator>
|
||||||
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
||||||
<meta:editing-cycles>38</meta:editing-cycles>
|
<meta:editing-cycles>40</meta:editing-cycles>
|
||||||
<meta:editing-duration>P0DT0H31M40S</meta:editing-duration>
|
<meta:editing-duration>P0DT0H31M45S</meta:editing-duration>
|
||||||
<dc:title>Lorem Ipsum</dc:title>
|
<dc:title>Lorem Ipsum</dc:title>
|
||||||
<dc:date>2023-02-12T15:01:25</dc:date>
|
<dc:date>2023-05-29T22:11:58</dc:date>
|
||||||
<dc:creator>lipsum.com</dc:creator>
|
<dc:creator>lipsum.com</dc:creator>
|
||||||
</office:meta>
|
</office:meta>
|
||||||
<office:font-face-decls>
|
<office:font-face-decls>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc2" hexVersion="0x020000c2" fileVersion="1.5" timeStamp="2022-11-15 15:14:31">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:09:41">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="3">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="3">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<entry key="chapter">%title%</entry>
|
<entry key="chapter">%title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 12:57:21">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:01:12">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="2" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="2" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<entry key="chapter">%title%</entry>
|
<entry key="chapter">%title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">* * *</entry>
|
<entry key="scene">* * *</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<entry key="chapter">Chapter %ch%: %title%</entry>
|
<entry key="chapter">Chapter %ch%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<entry key="chapter">Chapter %ch%: %title%</entry>
|
<entry key="chapter">Chapter %ch%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<entry key="chapter">Chapter %chw%: %title%</entry>
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<entry key="chapter">Chapter %chw%: %title%</entry>
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<entry key="chapter">Chapter %chw%: %title%</entry>
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
<entry key="unnumbered">%title%</entry>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<entry key="section"></entry>
|
<entry key="section" />
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
|
|||||||
@@ -154,7 +154,6 @@ def testBaseInit_Imports(caplog, monkeypatch, fncPath):
|
|||||||
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.__init__", lambda *a: None)
|
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.__init__", lambda *a: None)
|
||||||
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.resize", lambda *a: None)
|
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.resize", lambda *a: None)
|
||||||
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.showMessage", lambda *a: None)
|
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.showMessage", lambda *a: None)
|
||||||
monkeypatch.setitem(sys.modules, "lxml", None)
|
|
||||||
monkeypatch.setattr("sys.hexversion", 0x0)
|
monkeypatch.setattr("sys.hexversion", 0x0)
|
||||||
monkeypatch.setattr("novelwriter.CONFIG.verQtValue", 0x050000)
|
monkeypatch.setattr("novelwriter.CONFIG.verQtValue", 0x050000)
|
||||||
monkeypatch.setattr("novelwriter.CONFIG.verPyQtValue", 0x050000)
|
monkeypatch.setattr("novelwriter.CONFIG.verPyQtValue", 0x050000)
|
||||||
@@ -167,11 +166,9 @@ def testBaseInit_Imports(caplog, monkeypatch, fncPath):
|
|||||||
assert ex.value.code & 4 == 4 # Python version not satisfied
|
assert ex.value.code & 4 == 4 # Python version not satisfied
|
||||||
assert ex.value.code & 8 == 8 # Qt version not satisfied
|
assert ex.value.code & 8 == 8 # Qt version not satisfied
|
||||||
assert ex.value.code & 16 == 16 # PyQt version not satisfied
|
assert ex.value.code & 16 == 16 # PyQt version not satisfied
|
||||||
assert ex.value.code & 32 == 32 # lxml package missing
|
|
||||||
|
|
||||||
assert "At least Python" in caplog.messages[0]
|
assert "At least Python" in caplog.messages[0]
|
||||||
assert "At least Qt5" in caplog.messages[1]
|
assert "At least Qt5" in caplog.messages[1]
|
||||||
assert "At least PyQt5" in caplog.messages[2]
|
assert "At least PyQt5" in caplog.messages[2]
|
||||||
assert "lxml" in caplog.messages[3]
|
|
||||||
|
|
||||||
# END Test testBaseInit_Imports
|
# END Test testBaseInit_Imports
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, tstPaths, fncPath):
|
|||||||
|
|
||||||
# Fail saving
|
# Fail saving
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("pathlib.Path.write_bytes", causeOSError)
|
mp.setattr("xml.etree.ElementTree.ElementTree.write", causeOSError)
|
||||||
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is False
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is False
|
||||||
assert str(xmlWriter.error) == "Mock OSError"
|
assert str(xmlWriter.error) == "Mock OSError"
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
from tools import ODT_IGNORE, cmpFiles
|
from tools import ODT_IGNORE, cmpFiles
|
||||||
|
|
||||||
|
from novelwriter.common import xmlIndent
|
||||||
from novelwriter.core.toodt import ToOdt, ODTParagraphStyle, ODTTextStyle, XMLParagraph, _mkTag
|
from novelwriter.core.toodt import ToOdt, ODTParagraphStyle, ODTTextStyle, XMLParagraph, _mkTag
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ XML_NS = [
|
|||||||
def xmlToText(xElem):
|
def xmlToText(xElem):
|
||||||
"""Get the text content of an XML element.
|
"""Get the text content of an XML element.
|
||||||
"""
|
"""
|
||||||
rTxt = etree.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
rTxt = ET.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
||||||
for nSpace in XML_NS:
|
for nSpace in XML_NS:
|
||||||
rTxt = rTxt.replace(nSpace, "")
|
rTxt = rTxt.replace(nSpace, "")
|
||||||
return rTxt
|
return rTxt
|
||||||
@@ -63,21 +64,21 @@ def testCoreToOdt_Init(mockGUI):
|
|||||||
theDoc.initDocument()
|
theDoc.initDocument()
|
||||||
|
|
||||||
# Document XML
|
# Document XML
|
||||||
assert theDoc._dFlat is not None
|
assert theDoc._dFlat.tag == _mkTag("office", "document")
|
||||||
assert theDoc._dCont is None
|
assert theDoc._dCont.tag == ""
|
||||||
assert theDoc._dMeta is None
|
assert theDoc._dMeta.tag == ""
|
||||||
assert theDoc._dStyl is None
|
assert theDoc._dStyl.tag == ""
|
||||||
|
|
||||||
# Content XML
|
# Content XML
|
||||||
assert theDoc._xMeta is not None
|
assert theDoc._xMeta.tag == _mkTag("office", "meta")
|
||||||
assert theDoc._xFont is not None
|
assert theDoc._xFont.tag == _mkTag("office", "font-face-decls")
|
||||||
assert theDoc._xFnt2 is None
|
assert theDoc._xFnt2.tag == ""
|
||||||
assert theDoc._xStyl is not None
|
assert theDoc._xStyl.tag == _mkTag("office", "styles")
|
||||||
assert theDoc._xAuto is not None
|
assert theDoc._xAuto.tag == _mkTag("office", "automatic-styles")
|
||||||
assert theDoc._xAut2 is None
|
assert theDoc._xAut2.tag == ""
|
||||||
assert theDoc._xMast is not None
|
assert theDoc._xMast.tag == _mkTag("office", "master-styles")
|
||||||
assert theDoc._xBody is not None
|
assert theDoc._xBody.tag == _mkTag("office", "body")
|
||||||
assert theDoc._xText is not None
|
assert theDoc._xText.tag == _mkTag("office", "text")
|
||||||
|
|
||||||
# ODT Doc
|
# ODT Doc
|
||||||
# =======
|
# =======
|
||||||
@@ -86,21 +87,22 @@ def testCoreToOdt_Init(mockGUI):
|
|||||||
theDoc.initDocument()
|
theDoc.initDocument()
|
||||||
|
|
||||||
# Document XML
|
# Document XML
|
||||||
assert theDoc._dFlat is None
|
assert theDoc._dFlat.tag == ""
|
||||||
assert theDoc._dCont is not None
|
assert theDoc._dCont.tag == _mkTag("office", "document-content")
|
||||||
assert theDoc._dMeta is not None
|
assert theDoc._dMeta.tag == _mkTag("office", "document-meta")
|
||||||
assert theDoc._dStyl is not None
|
assert theDoc._dStyl.tag == _mkTag("office", "document-styles")
|
||||||
|
|
||||||
# Content XML
|
# Content XML
|
||||||
assert theDoc._xMeta is not None
|
assert theDoc._xMeta.tag == _mkTag("office", "meta")
|
||||||
assert theDoc._xFont is not None
|
assert theDoc._xFont.tag == _mkTag("office", "font-face-decls")
|
||||||
assert theDoc._xFnt2 is not None
|
assert theDoc._xFnt2.tag == _mkTag("office", "font-face-decls")
|
||||||
assert theDoc._xStyl is not None
|
assert theDoc._xStyl.tag == _mkTag("office", "styles")
|
||||||
assert theDoc._xAuto is not None
|
assert theDoc._xAuto.tag == _mkTag("office", "automatic-styles")
|
||||||
assert theDoc._xAut2 is not None
|
assert theDoc._xAut2.tag == _mkTag("office", "automatic-styles")
|
||||||
assert theDoc._xMast is not None
|
assert theDoc._xMast.tag == _mkTag("office", "master-styles")
|
||||||
assert theDoc._xBody is not None
|
assert theDoc._xBody.tag == _mkTag("office", "body")
|
||||||
assert theDoc._xText is not None
|
assert theDoc._xText.tag == _mkTag("office", "text")
|
||||||
|
|
||||||
|
|
||||||
# END Test testCoreToOdt_Init
|
# END Test testCoreToOdt_Init
|
||||||
|
|
||||||
@@ -147,7 +149,7 @@ def testCoreToOdt_TextFormatting(mockGUI):
|
|||||||
theDoc._addTextPar("Standard", oStyle, "")
|
theDoc._addTextPar("Standard", oStyle, "")
|
||||||
assert xmlToText(theDoc._xText) == (
|
assert xmlToText(theDoc._xText) == (
|
||||||
"<office:text>"
|
"<office:text>"
|
||||||
"<text:p text:style-name=\"Standard\"></text:p>"
|
"<text:p text:style-name=\"Standard\" />"
|
||||||
"</office:text>"
|
"</office:text>"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -474,9 +476,9 @@ def testCoreToOdt_Convert(mockGUI):
|
|||||||
assert theDoc.getErrors() == []
|
assert theDoc.getErrors() == []
|
||||||
assert xmlToText(theDoc._xText) == (
|
assert xmlToText(theDoc._xText) == (
|
||||||
'<office:text>'
|
'<office:text>'
|
||||||
'<text:p text:style-name="Text_20_body"></text:p>'
|
'<text:p text:style-name="Text_20_body" />'
|
||||||
'<text:p text:style-name="Text_20_body">Text</text:p>'
|
'<text:p text:style-name="Text_20_body">Text</text:p>'
|
||||||
'<text:p text:style-name="Text_20_body"></text:p>'
|
'<text:p text:style-name="Text_20_body" />'
|
||||||
'<text:p text:style-name="Text_20_body">Text</text:p>'
|
'<text:p text:style-name="Text_20_body">Text</text:p>'
|
||||||
'</office:text>'
|
'</office:text>'
|
||||||
)
|
)
|
||||||
@@ -723,15 +725,10 @@ def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths):
|
|||||||
stylOut = tstPaths.outDir / "coreToOdt_SaveFull" / "styles.xml"
|
stylOut = tstPaths.outDir / "coreToOdt_SaveFull" / "styles.xml"
|
||||||
|
|
||||||
def prettifyXml(inFile, outFile):
|
def prettifyXml(inFile, outFile):
|
||||||
with open(outFile, mode="wb") as fileStream:
|
with open(outFile, mode="wb") as fStream:
|
||||||
fileStream.write(
|
xml = ET.parse(inFile)
|
||||||
etree.tostring(
|
xmlIndent(xml)
|
||||||
etree.parse(str(inFile)),
|
xml.write(fStream, encoding="utf-8", xml_declaration=True)
|
||||||
pretty_print=True,
|
|
||||||
encoding="utf-8",
|
|
||||||
xml_declaration=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
prettifyXml(maniOut, maniFile)
|
prettifyXml(maniOut, maniFile)
|
||||||
prettifyXml(settOut, settFile)
|
prettifyXml(settOut, settFile)
|
||||||
@@ -949,11 +946,7 @@ def testCoreToOdt_ODTParagraphStyle():
|
|||||||
|
|
||||||
# Pack XML
|
# Pack XML
|
||||||
# ========
|
# ========
|
||||||
xStyle = etree.Element("test", nsmap={
|
xStyle = ET.Element("test")
|
||||||
"style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
|
||||||
"loext": "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0",
|
|
||||||
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
|
||||||
})
|
|
||||||
parStyle.packXML(xStyle, "test")
|
parStyle.packXML(xStyle, "test")
|
||||||
assert xmlToText(xStyle) == (
|
assert xmlToText(xStyle) == (
|
||||||
'<test>'
|
'<test>'
|
||||||
@@ -1059,10 +1052,7 @@ def testCoreToOdt_ODTTextStyle():
|
|||||||
txtStyle.setFontStyle("italic")
|
txtStyle.setFontStyle("italic")
|
||||||
txtStyle.setStrikeStyle("solid")
|
txtStyle.setStrikeStyle("solid")
|
||||||
txtStyle.setStrikeType("single")
|
txtStyle.setStrikeType("single")
|
||||||
xStyle = etree.Element("test", nsmap={
|
xStyle = ET.Element("test")
|
||||||
"style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
|
||||||
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
|
||||||
})
|
|
||||||
txtStyle.packXML(xStyle, "test")
|
txtStyle.packXML(xStyle, "test")
|
||||||
assert xmlToText(xStyle) == (
|
assert xmlToText(xStyle) == (
|
||||||
'<test><style:style style:name="test" style:family="text"><style:text-properties '
|
'<test><style:style style:name="test" style:family="text"><style:text-properties '
|
||||||
@@ -1077,20 +1067,11 @@ def testCoreToOdt_ODTTextStyle():
|
|||||||
def testCoreToOdt_XMLParagraph():
|
def testCoreToOdt_XMLParagraph():
|
||||||
"""Test XML encoding of paragraph.
|
"""Test XML encoding of paragraph.
|
||||||
"""
|
"""
|
||||||
nsMap = {
|
|
||||||
"office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
|
|
||||||
"style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
|
||||||
"loext": "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0",
|
|
||||||
"text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
|
||||||
"meta": "urn:oasis:names:tc:opendocument:xmlns:meta:1.0",
|
|
||||||
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Stage 1 : Text
|
# Stage 1 : Text
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
xRoot = etree.Element("root", nsmap=nsMap)
|
xRoot = ET.Element("root")
|
||||||
xElem = etree.SubElement(xRoot, "{%s}p" % nsMap["text"])
|
xElem = ET.SubElement(xRoot, _mkTag("text", "p"))
|
||||||
xmlPar = XMLParagraph(xElem)
|
xmlPar = XMLParagraph(xElem)
|
||||||
|
|
||||||
# Plain Text
|
# Plain Text
|
||||||
@@ -1126,8 +1107,8 @@ def testCoreToOdt_XMLParagraph():
|
|||||||
# Stage 2 : Line Breaks
|
# Stage 2 : Line Breaks
|
||||||
# =====================
|
# =====================
|
||||||
|
|
||||||
xRoot = etree.Element("root", nsmap=nsMap)
|
xRoot = ET.Element("root")
|
||||||
xElem = etree.SubElement(xRoot, "{%s}p" % nsMap["text"])
|
xElem = ET.SubElement(xRoot, _mkTag("text", "p"))
|
||||||
xmlPar = XMLParagraph(xElem)
|
xmlPar = XMLParagraph(xElem)
|
||||||
|
|
||||||
# Plain Text w/Line Break
|
# Plain Text w/Line Break
|
||||||
@@ -1162,8 +1143,8 @@ def testCoreToOdt_XMLParagraph():
|
|||||||
# Stage 3 : Tabs
|
# Stage 3 : Tabs
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
xRoot = etree.Element("root", nsmap=nsMap)
|
xRoot = ET.Element("root")
|
||||||
xElem = etree.SubElement(xRoot, "{%s}p" % nsMap["text"])
|
xElem = ET.SubElement(xRoot, _mkTag("text", "p"))
|
||||||
xmlPar = XMLParagraph(xElem)
|
xmlPar = XMLParagraph(xElem)
|
||||||
|
|
||||||
# Plain Text w/Line Break
|
# Plain Text w/Line Break
|
||||||
@@ -1198,8 +1179,8 @@ def testCoreToOdt_XMLParagraph():
|
|||||||
# Stage 4 : Spaces
|
# Stage 4 : Spaces
|
||||||
# ================
|
# ================
|
||||||
|
|
||||||
xRoot = etree.Element("root", nsmap=nsMap)
|
xRoot = ET.Element("root")
|
||||||
xElem = etree.SubElement(xRoot, "{%s}p" % nsMap["text"])
|
xElem = ET.SubElement(xRoot, _mkTag("text", "p"))
|
||||||
xmlPar = XMLParagraph(xElem)
|
xmlPar = XMLParagraph(xElem)
|
||||||
|
|
||||||
# Plain Text w/Spaces
|
# Plain Text w/Spaces
|
||||||
@@ -1234,8 +1215,8 @@ def testCoreToOdt_XMLParagraph():
|
|||||||
# Stage 5 : Lots of Spaces
|
# Stage 5 : Lots of Spaces
|
||||||
# ========================
|
# ========================
|
||||||
|
|
||||||
xRoot = etree.Element("root", nsmap=nsMap)
|
xRoot = ET.Element("root")
|
||||||
xElem = etree.SubElement(xRoot, "{%s}p" % nsMap["text"])
|
xElem = ET.SubElement(xRoot, _mkTag("text", "p"))
|
||||||
xmlPar = XMLParagraph(xElem)
|
xmlPar = XMLParagraph(xElem)
|
||||||
|
|
||||||
# Plain Text w/Many Spaces
|
# Plain Text w/Many Spaces
|
||||||
@@ -1261,8 +1242,8 @@ def testCoreToOdt_XMLParagraph():
|
|||||||
# Check Error
|
# Check Error
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
xRoot = etree.Element("root", nsmap=nsMap)
|
xRoot = ET.Element("root")
|
||||||
xElem = etree.SubElement(xRoot, "{%s}p" % nsMap["text"])
|
xElem = ET.SubElement(xRoot, _mkTag("text", "p"))
|
||||||
xmlPar = XMLParagraph(xElem)
|
xmlPar = XMLParagraph(xElem)
|
||||||
|
|
||||||
xmlPar.appendText("A")
|
xmlPar.appendText("A")
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ from pathlib import Path
|
|||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import qApp
|
||||||
|
|
||||||
XML_IGNORE = ("<novelWriterXML", "<project")
|
XML_IGNORE = ("<novelWriterXML", "<project")
|
||||||
ODT_IGNORE = ("<meta:generator", "<meta:creation-date", "<dc:date")
|
ODT_IGNORE = ("<meta:generator", "<meta:creation-date", "<dc:date", "<meta:editing")
|
||||||
|
|
||||||
|
|
||||||
class C:
|
class C:
|
||||||
|
|||||||
Reference in New Issue
Block a user