From e3c37f003be2eb6466a1a192e121de415ade20b6 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Tue, 26 Jan 2021 20:22:32 +0100
Subject: [PATCH] Add main ODT style XML
---
nw/core/toodt.py | 303 ++++++++++++++++++++++++++++-
tests/test_core/test_core_toodt.py | 18 +-
2 files changed, 319 insertions(+), 2 deletions(-)
diff --git a/nw/core/toodt.py b/nw/core/toodt.py
index 5bad6615..09fbb91f 100644
--- a/nw/core/toodt.py
+++ b/nw/core/toodt.py
@@ -24,23 +24,87 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
+import nw
import logging
+import os
+
+from lxml import etree
from nw.core.tokenizer import Tokenizer
logger = logging.getLogger(__name__)
+XML_NS = {
+ "office" : "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
+ "style" : "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
+ "text" : "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
+ "fo" : "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
+ "loext" : "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0",
+}
+
class ToOdt(Tokenizer):
def __init__(self, theProject, theParent):
Tokenizer.__init__(self, theProject, theParent)
+ self.mainConf = nw.CONFIG
+
+ self._xRoot = None
+ self._xStyl = None
+ self._xText = None
+
+ self._dLanguage = "en"
+ self._dCountry = "GB"
+ self._dFontFace = "Liberation Serif"
+ self._dFontSize = 12
+
+ return
+
+ ##
+ # Setters
+ ##
+
+ def setLanguage(self, theLang):
+ """Set language for the document.
+ """
+ if theLang is None:
+ return False
+
+ langBits = theLang.split("_")
+ self._dLanguage = langBits[0]
+ if len(langBits) > 1:
+ self._dCountry = langBits[1]
+
+ return True
+
+ def setFont(self, fontFace, fontSize):
+ """Set font and font size.
+ """
+ self._dFontFace = fontFace
+ self._dFontSize = fontSize
return
##
# Class Methods
##
+ def initDocument(self):
+ """Initialises a new open document XML tree.
+ """
+ rAttr = {
+ _mkTag("office", "version") : "1.3",
+ _mkTag("office", "mimetype") : "application/vnd.oasis.opendocument.text",
+ }
+ self._xRoot = etree.Element(_mkTag("office", "document"), attrib=rAttr, nsmap=XML_NS)
+ self._xStyl = etree.SubElement(self._xRoot, _mkTag("office", "styles"))
+ self._xText = etree.SubElement(self._xRoot, _mkTag("office", "text"))
+
+ # Add Styles
+ self._styleParagraph()
+ self._styleHeaders()
+
+ return
+
def doConvert(self):
"""Convert the list of text tokens into a HTML document saved
to theResult.
@@ -48,8 +112,245 @@ class ToOdt(Tokenizer):
self.theResult = ""
for tType, tLine, tText, tFormat, tStyle in self.theTokens:
- continue
+
+ # Process Text Type
+ if tType == self.T_EMPTY:
+ continue
+
+ elif tType == self.T_TITLE:
+ continue
+
+ return
+
+ def closeDocument(self):
+ """Return the serialised XML document
+ """
+ self.theResult = etree.tostring(
+ self._xRoot,
+ pretty_print = True,
+ encoding = "utf-8",
+ xml_declaration = True
+ )
+
+ cacheFile = os.path.join(os.path.expanduser("~"), "Temp", "odtGen.fodt")
+ with open(cacheFile, mode="wb") as outFile:
+ outFile.write(self.theResult)
+
+ return
+
+ ##
+ # Style Elements
+ ##
+
+ def _styleParagraph(self):
+ """Set the paragraph styles.
+ """
+ # Add Default Paragraph Style
+ # ===========================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("style", "line-break")] = "strict"
+ theAttr[_mkTag("style", "tab-stop-distance")] = "1.251cm"
+ theAttr[_mkTag("style", "writing-mode")] = "page"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("style", "font-name")] = self._dFontFace
+ theAttr[_mkTag("fo", "font-size")] = "%dpt" % self._dFontSize
+ theAttr[_mkTag("fo", "language")] = self._dLanguage
+ theAttr[_mkTag("fo", "country")] = self._dCountry
+ etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
+
+ # Add Paragraph Style
+ # ===================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Standard"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "class")] = "text"
+ etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ # Add Text Body Style
+ # ===================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Text_body"
+ theAttr[_mkTag("style", "display-name")] = "Text Body"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "parent-style-name")] = "Standard"
+ theAttr[_mkTag("style", "class")] = "text"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "margin-top")] = "0cm"
+ theAttr[_mkTag("fo", "margin-bottom")] = "0.247cm"
+ theAttr[_mkTag("style", "contextual-spacing")] = "false"
+ theAttr[_mkTag("fo", "line-height")] = "115%"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ return
+
+ def _styleHeaders(self):
+ """Set the header styles.
+ """
+ # Add Default Heading Style
+ # =========================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Heading"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "parent-style-name")] = "Standard"
+ theAttr[_mkTag("style", "next-style-name")] = "Text_body"
+ theAttr[_mkTag("style", "class")] = "text"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "margin-top")] = "0.423cm"
+ theAttr[_mkTag("fo", "margin-bottom")] = "0.212cm"
+ theAttr[_mkTag("style", "contextual-spacing")] = "false"
+ theAttr[_mkTag("fo", "keep-with-next")] = "always"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("style", "font-name")] = self._dFontFace
+ theAttr[_mkTag("fo", "font-family")] = "'%s'" % self._dFontFace
+ theAttr[_mkTag("style", "font-pitch")] = "variable"
+ theAttr[_mkTag("fo", "font-size")] = "14pt"
+ etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
+
+ # Add Title Style
+ # ===============
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Title"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "parent-style-name")] = "Heading"
+ theAttr[_mkTag("style", "next-style-name")] = "Text_body"
+ theAttr[_mkTag("style", "class")] = "chapter"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "text-align")] = "center"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "font-size")] = "28pt"
+ theAttr[_mkTag("fo", "font-weight")] = "bold"
+ etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
+
+ # Add Heading 1 Style
+ # ===================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Heading_1"
+ theAttr[_mkTag("style", "display-name")] = "Heading 1"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "parent-style-name")] = "Heading"
+ theAttr[_mkTag("style", "next-style-name")] = "Text_body"
+ theAttr[_mkTag("style", "default-outline-level")] = "1"
+ theAttr[_mkTag("style", "class")] = "text"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "margin-top")] = "0.423cm"
+ theAttr[_mkTag("fo", "margin-bottom")] = "0.212cm"
+ theAttr[_mkTag("style", "contextual-spacing")] = "false"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "font-size")] = "130%"
+ theAttr[_mkTag("fo", "font-weight")] = "bold"
+ etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
+
+ # Add Heading 2 Style
+ # ===================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Heading_2"
+ theAttr[_mkTag("style", "display-name")] = "Heading 2"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "parent-style-name")] = "Heading"
+ theAttr[_mkTag("style", "next-style-name")] = "Text_body"
+ theAttr[_mkTag("style", "default-outline-level")] = "2"
+ theAttr[_mkTag("style", "class")] = "text"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "margin-top")] = "0.353cm"
+ theAttr[_mkTag("fo", "margin-bottom")] = "0.212cm"
+ theAttr[_mkTag("style", "contextual-spacing")] = "false"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "font-size")] = "120%"
+ theAttr[_mkTag("fo", "font-weight")] = "bold"
+ etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
+
+ # Add Heading 3 Style
+ # ===================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Heading_3"
+ theAttr[_mkTag("style", "display-name")] = "Heading 3"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "parent-style-name")] = "Heading"
+ theAttr[_mkTag("style", "next-style-name")] = "Text_body"
+ theAttr[_mkTag("style", "default-outline-level")] = "3"
+ theAttr[_mkTag("style", "class")] = "text"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "margin-top")] = "0.247cm"
+ theAttr[_mkTag("fo", "margin-bottom")] = "0.212cm"
+ theAttr[_mkTag("style", "contextual-spacing")] = "false"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "font-size")] = "110%"
+ theAttr[_mkTag("fo", "font-weight")] = "bold"
+ etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
+
+ # Add Heading 4 Style
+ # ===================
+
+ theAttr = {}
+ theAttr[_mkTag("style", "name")] = "Heading_4"
+ theAttr[_mkTag("style", "display-name")] = "Heading 4"
+ theAttr[_mkTag("style", "family")] = "paragraph"
+ theAttr[_mkTag("style", "parent-style-name")] = "Heading"
+ theAttr[_mkTag("style", "next-style-name")] = "Text_body"
+ theAttr[_mkTag("style", "default-outline-level")] = "4"
+ theAttr[_mkTag("style", "class")] = "text"
+ xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "margin-top")] = "0.247cm"
+ theAttr[_mkTag("fo", "margin-bottom")] = "0.212cm"
+ theAttr[_mkTag("style", "contextual-spacing")] = "false"
+ etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
+
+ theAttr = {}
+ theAttr[_mkTag("fo", "font-size")] = "100%"
+ theAttr[_mkTag("fo", "font-weight")] = "bold"
+ etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
return
# END Class ToOdt
+
+# =========================================================================== #
+# Local Functions
+# =========================================================================== #
+
+def _mkTag(nsName, tagName):
+ """Assemble namespace and tag name.
+ """
+ theNS = XML_NS.get(nsName, "")
+ if theNS:
+ return "{%s}%s" % (theNS, tagName)
+ logger.warning("Missing xml namespace '%s'" % nsName)
+ return tagName
diff --git a/tests/test_core/test_core_toodt.py b/tests/test_core/test_core_toodt.py
index c9fe5b10..6159929a 100644
--- a/tests/test_core/test_core_toodt.py
+++ b/tests/test_core/test_core_toodt.py
@@ -20,16 +20,32 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
+import nw
import pytest
from nw.core import NWProject, NWIndex, ToOdt
@pytest.mark.core
-def testCoreToOdt_Convert(dummyGUI):
+def testCoreToOdt_Convert(tmpConf, dummyGUI):
"""Test the converter of the ToHtml class.
"""
+ nw.CONFIG = tmpConf
+
theProject = NWProject(dummyGUI)
dummyGUI.theIndex = NWIndex(theProject, dummyGUI)
theDoc = ToOdt(theProject, dummyGUI)
+ # Export Mode
+ # ===========
+
+ theDoc.isNovel = True
+
+ # Header 1
+ theDoc.theText = "# Title\n"
+ theDoc.tokenizeText()
+ theDoc.initDocument()
+ theDoc.doConvert()
+ theDoc.closeDocument()
+ assert theDoc.theResult == ""
+
# END Test testCoreToOdt_Convert