# -*- coding: utf-8 -*- """ novelWriter – ODT Text Converter ================================ Extends the Tokenizer class to generate ODT and FODT files File History: Created: 2021-01-26 [1.1rc1] This file is a part of novelWriter Copyright 2018–2021, Veronica Berglyd Olsen This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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. """ self.theResult = "" for tType, tLine, tText, tFormat, tStyle in self.theTokens: # 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