Add ODT auto-style logic
This commit is contained in:
+349
-129
@@ -29,6 +29,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
from nw.core.tokenizer import Tokenizer
|
from nw.core.tokenizer import Tokenizer
|
||||||
|
|
||||||
@@ -39,7 +40,6 @@ XML_NS = {
|
|||||||
"style" : "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
"style" : "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
||||||
"text" : "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
"text" : "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
||||||
"fo" : "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible: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):
|
class ToOdt(Tokenizer):
|
||||||
@@ -51,7 +51,13 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
self._xRoot = None
|
self._xRoot = None
|
||||||
self._xStyl = None
|
self._xStyl = None
|
||||||
|
self._xBody = None
|
||||||
self._xText = None
|
self._xText = None
|
||||||
|
self._xAuto = None
|
||||||
|
|
||||||
|
self._mainPara = {}
|
||||||
|
self._autoPara = {}
|
||||||
|
self._autoText = {}
|
||||||
|
|
||||||
self._dLanguage = "en"
|
self._dLanguage = "en"
|
||||||
self._dCountry = "GB"
|
self._dCountry = "GB"
|
||||||
@@ -97,11 +103,13 @@ class ToOdt(Tokenizer):
|
|||||||
}
|
}
|
||||||
self._xRoot = etree.Element(_mkTag("office", "document"), attrib=rAttr, nsmap=XML_NS)
|
self._xRoot = etree.Element(_mkTag("office", "document"), attrib=rAttr, nsmap=XML_NS)
|
||||||
self._xStyl = etree.SubElement(self._xRoot, _mkTag("office", "styles"))
|
self._xStyl = etree.SubElement(self._xRoot, _mkTag("office", "styles"))
|
||||||
self._xText = etree.SubElement(self._xRoot, _mkTag("office", "text"))
|
self._xAuto = etree.SubElement(self._xRoot, _mkTag("office", "automatic-styles"))
|
||||||
|
self._xBody = etree.SubElement(self._xRoot, _mkTag("office", "body"))
|
||||||
|
self._xText = etree.SubElement(self._xBody, _mkTag("office", "text"))
|
||||||
|
|
||||||
# Add Styles
|
# Add Styles
|
||||||
self._styleParagraph()
|
self._defaultStyles()
|
||||||
self._styleHeaders()
|
self._useableStyles()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -113,18 +121,60 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
for tType, tLine, tText, tFormat, tStyle in self.theTokens:
|
for tType, tLine, tText, tFormat, tStyle in self.theTokens:
|
||||||
|
|
||||||
|
# Styles
|
||||||
|
oStyle = ODTParagraphStyle()
|
||||||
|
if tStyle is not None:
|
||||||
|
if tStyle & self.A_LEFT:
|
||||||
|
oStyle.setTextAlign("left")
|
||||||
|
if tStyle & self.A_RIGHT:
|
||||||
|
oStyle.setTextAlign("right")
|
||||||
|
if tStyle & self.A_CENTRE:
|
||||||
|
oStyle.setTextAlign("center")
|
||||||
|
if tStyle & self.A_JUSTIFY:
|
||||||
|
oStyle.setTextAlign("justify")
|
||||||
|
if tStyle & self.A_PBB:
|
||||||
|
oStyle.setBreakBefore("page")
|
||||||
|
if tStyle & self.A_PBA:
|
||||||
|
oStyle.setBreakAfter("page")
|
||||||
|
|
||||||
# Process Text Type
|
# Process Text Type
|
||||||
if tType == self.T_EMPTY:
|
if tType == self.T_EMPTY:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif tType == self.T_TITLE:
|
elif tType == self.T_TITLE:
|
||||||
continue
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
|
self._addTextPar("Title", oStyle, tHead)
|
||||||
|
|
||||||
|
elif tType == self.T_HEAD1:
|
||||||
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
|
self._addTextPar("Heading_1", oStyle, tHead, oLevel="1")
|
||||||
|
|
||||||
|
elif tType == self.T_HEAD2:
|
||||||
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
|
self._addTextPar("Heading_2", oStyle, tHead, oLevel="2")
|
||||||
|
|
||||||
|
elif tType == self.T_HEAD3:
|
||||||
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
|
self._addTextPar("Heading_3", oStyle, tHead, oLevel="3")
|
||||||
|
|
||||||
|
elif tType == self.T_HEAD4:
|
||||||
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
|
self._addTextPar("Heading_4", oStyle, tHead, oLevel="4")
|
||||||
|
|
||||||
|
elif tType == self.T_SEP:
|
||||||
|
self._addTextPar("Text_Body", oStyle, tText)
|
||||||
|
|
||||||
|
elif tType == self.T_SKIP:
|
||||||
|
self._addTextPar("Text_Body", oStyle, "")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def closeDocument(self):
|
def closeDocument(self):
|
||||||
"""Return the serialised XML document
|
"""Return the serialised XML document
|
||||||
"""
|
"""
|
||||||
|
for styleName, styleObj in self._autoPara.values():
|
||||||
|
styleObj.packXML(self._xAuto, styleName)
|
||||||
|
|
||||||
self.theResult = etree.tostring(
|
self.theResult = etree.tostring(
|
||||||
self._xRoot,
|
self._xRoot,
|
||||||
pretty_print = True,
|
pretty_print = True,
|
||||||
@@ -138,15 +188,51 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _addTextPar(self, styleName, oStyle, theText, oLevel=None):
|
||||||
|
"""Add a text paragraph to the text XML element.
|
||||||
|
"""
|
||||||
|
tAttr = {}
|
||||||
|
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
|
||||||
|
if oLevel is not None:
|
||||||
|
tAttr[_mkTag("text", "outline-level")] = oLevel
|
||||||
|
xElem = etree.SubElement(self._xText, _mkTag("text", "p"), attrib=tAttr)
|
||||||
|
xElem.text = theText
|
||||||
|
return
|
||||||
|
|
||||||
|
def _paraStyle(self, parName, oStyle):
|
||||||
|
"""Return a name for a style object.
|
||||||
|
"""
|
||||||
|
refStyle = self._mainPara.get(parName, None)
|
||||||
|
if refStyle is None:
|
||||||
|
logger.error("Unknown paragraph style '%s'" % parName)
|
||||||
|
return "Standard"
|
||||||
|
|
||||||
|
if not refStyle.checkNew(oStyle):
|
||||||
|
return parName
|
||||||
|
|
||||||
|
oStyle.setParentStyleName(parName)
|
||||||
|
theID = oStyle.getID()
|
||||||
|
if theID in self._autoPara:
|
||||||
|
return self._autoPara[theID][0]
|
||||||
|
|
||||||
|
newName = "P%d" % (len(self._autoPara) + 1)
|
||||||
|
self._autoPara[theID] = (newName, oStyle)
|
||||||
|
|
||||||
|
return newName
|
||||||
|
|
||||||
##
|
##
|
||||||
# Style Elements
|
# Style Elements
|
||||||
##
|
##
|
||||||
|
|
||||||
def _styleParagraph(self):
|
def _defaultStyles(self):
|
||||||
"""Set the paragraph styles.
|
"""Set the default styles.
|
||||||
"""
|
"""
|
||||||
# Add Default Paragraph Style
|
# Add Paragraph Family Style
|
||||||
# ===========================
|
# ==========================
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
theAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
@@ -165,8 +251,8 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("fo", "country")] = self._dCountry
|
theAttr[_mkTag("fo", "country")] = self._dCountry
|
||||||
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
# Add Paragraph Style
|
# Add Standard Paragraph Style
|
||||||
# ===================
|
# ============================
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = "Standard"
|
theAttr[_mkTag("style", "name")] = "Standard"
|
||||||
@@ -174,29 +260,6 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("style", "class")] = "text"
|
theAttr[_mkTag("style", "class")] = "text"
|
||||||
etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
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
|
# Add Default Heading Style
|
||||||
# =========================
|
# =========================
|
||||||
|
|
||||||
@@ -204,14 +267,13 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("style", "name")] = "Heading"
|
theAttr[_mkTag("style", "name")] = "Heading"
|
||||||
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", "next-style-name")] = "Text_body"
|
theAttr[_mkTag("style", "next-style-name")] = "Text_Body"
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
theAttr[_mkTag("style", "class")] = "text"
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("fo", "margin-top")] = "0.423cm"
|
theAttr[_mkTag("fo", "margin-top")] = "0.423cm"
|
||||||
theAttr[_mkTag("fo", "margin-bottom")] = "0.212cm"
|
theAttr[_mkTag("fo", "margin-bottom")] = "0.212cm"
|
||||||
theAttr[_mkTag("style", "contextual-spacing")] = "false"
|
|
||||||
theAttr[_mkTag("fo", "keep-with-next")] = "always"
|
theAttr[_mkTag("fo", "keep-with-next")] = "always"
|
||||||
etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
||||||
|
|
||||||
@@ -222,129 +284,287 @@ class ToOdt(Tokenizer):
|
|||||||
theAttr[_mkTag("fo", "font-size")] = "14pt"
|
theAttr[_mkTag("fo", "font-size")] = "14pt"
|
||||||
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _useableStyles(self):
|
||||||
|
"""Set the usable styles.
|
||||||
|
"""
|
||||||
|
# Add Text Body Style
|
||||||
|
# ===================
|
||||||
|
|
||||||
|
oStyle = ODTParagraphStyle()
|
||||||
|
oStyle.setDisplayName("Text Body")
|
||||||
|
oStyle.setParentStyleName("Standard")
|
||||||
|
oStyle.setClass("text")
|
||||||
|
oStyle.setMarginTop("0cm")
|
||||||
|
oStyle.setMarginBottom("0.247cm")
|
||||||
|
oStyle.setLineHeight("115%")
|
||||||
|
oStyle.packXML(self._xStyl, "Text_Body")
|
||||||
|
|
||||||
|
self._mainPara["Text_Body"] = oStyle
|
||||||
|
|
||||||
# Add Title Style
|
# Add Title Style
|
||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
theAttr = {}
|
oStyle = ODTParagraphStyle()
|
||||||
theAttr[_mkTag("style", "name")] = "Title"
|
oStyle.setDisplayName("Title")
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
oStyle.setParentStyleName("Heading")
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Heading"
|
oStyle.setNextStyleName("Text_Body")
|
||||||
theAttr[_mkTag("style", "next-style-name")] = "Text_body"
|
oStyle.setClass("chapter")
|
||||||
theAttr[_mkTag("style", "class")] = "chapter"
|
oStyle.setTextAlign("center")
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
oStyle.setFontSize("28pt")
|
||||||
|
oStyle.setFontWeight("bold")
|
||||||
|
oStyle.packXML(self._xStyl, "Title")
|
||||||
|
|
||||||
theAttr = {}
|
self._mainPara["Title"] = oStyle
|
||||||
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
|
# Add Heading 1 Style
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
theAttr = {}
|
oStyle = ODTParagraphStyle()
|
||||||
theAttr[_mkTag("style", "name")] = "Heading_1"
|
oStyle.setDisplayName("Heading 1")
|
||||||
theAttr[_mkTag("style", "display-name")] = "Heading 1"
|
oStyle.setParentStyleName("Heading")
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
oStyle.setNextStyleName("Text_Body")
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Heading"
|
oStyle.setOutlineLevel("1")
|
||||||
theAttr[_mkTag("style", "next-style-name")] = "Text_body"
|
oStyle.setClass("text")
|
||||||
theAttr[_mkTag("style", "default-outline-level")] = "1"
|
oStyle.setMarginTop("0.423cm")
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
oStyle.setMarginBottom("0.212cm")
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
oStyle.setFontSize("200%")
|
||||||
|
oStyle.setFontWeight("bold")
|
||||||
|
oStyle.packXML(self._xStyl, "Heading_1")
|
||||||
|
|
||||||
theAttr = {}
|
self._mainPara["Heading_1"] = oStyle
|
||||||
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
|
# Add Heading 2 Style
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
theAttr = {}
|
oStyle = ODTParagraphStyle()
|
||||||
theAttr[_mkTag("style", "name")] = "Heading_2"
|
oStyle.setDisplayName("Heading 2")
|
||||||
theAttr[_mkTag("style", "display-name")] = "Heading 2"
|
oStyle.setParentStyleName("Heading")
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
oStyle.setNextStyleName("Text_Body")
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Heading"
|
oStyle.setOutlineLevel("2")
|
||||||
theAttr[_mkTag("style", "next-style-name")] = "Text_body"
|
oStyle.setClass("text")
|
||||||
theAttr[_mkTag("style", "default-outline-level")] = "2"
|
oStyle.setMarginTop("0.353cm")
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
oStyle.setMarginBottom("0.212cm")
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
oStyle.setFontSize("140%")
|
||||||
|
oStyle.setFontWeight("bold")
|
||||||
|
oStyle.packXML(self._xStyl, "Heading_2")
|
||||||
|
|
||||||
theAttr = {}
|
self._mainPara["Heading_2"] = oStyle
|
||||||
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
|
# Add Heading 3 Style
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
theAttr = {}
|
oStyle = ODTParagraphStyle()
|
||||||
theAttr[_mkTag("style", "name")] = "Heading_3"
|
oStyle.setDisplayName("Heading 3")
|
||||||
theAttr[_mkTag("style", "display-name")] = "Heading 3"
|
oStyle.setParentStyleName("Heading")
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
oStyle.setNextStyleName("Text_Body")
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Heading"
|
oStyle.setOutlineLevel("3")
|
||||||
theAttr[_mkTag("style", "next-style-name")] = "Text_body"
|
oStyle.setClass("text")
|
||||||
theAttr[_mkTag("style", "default-outline-level")] = "3"
|
oStyle.setMarginTop("0.247cm")
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
oStyle.setMarginBottom("0.212cm")
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
oStyle.setFontSize("125%")
|
||||||
|
oStyle.setFontWeight("bold")
|
||||||
|
oStyle.packXML(self._xStyl, "Heading_3")
|
||||||
|
|
||||||
theAttr = {}
|
self._mainPara["Heading_3"] = oStyle
|
||||||
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
|
# Add Heading 4 Style
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
theAttr = {}
|
oStyle = ODTParagraphStyle()
|
||||||
theAttr[_mkTag("style", "name")] = "Heading_4"
|
oStyle.setDisplayName("Heading 4")
|
||||||
theAttr[_mkTag("style", "display-name")] = "Heading 4"
|
oStyle.setParentStyleName("Heading")
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
oStyle.setNextStyleName("Text_Body")
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Heading"
|
oStyle.setOutlineLevel("4")
|
||||||
theAttr[_mkTag("style", "next-style-name")] = "Text_body"
|
oStyle.setClass("text")
|
||||||
theAttr[_mkTag("style", "default-outline-level")] = "4"
|
oStyle.setMarginTop("0.247cm")
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
oStyle.setMarginBottom("0.212cm")
|
||||||
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
oStyle.setFontSize("110%")
|
||||||
|
oStyle.setFontWeight("bold")
|
||||||
|
oStyle.packXML(self._xStyl, "Heading_4")
|
||||||
|
|
||||||
theAttr = {}
|
self._mainPara["Heading_4"] = oStyle
|
||||||
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
|
return
|
||||||
|
|
||||||
# END Class ToOdt
|
# END Class ToOdt
|
||||||
|
|
||||||
# =========================================================================== #
|
# =============================================================================================== #
|
||||||
|
# Auto-Style Classes
|
||||||
|
# =============================================================================================== #
|
||||||
|
|
||||||
|
class ODTParagraphStyle():
|
||||||
|
|
||||||
|
VALID_ALIGN = ["start", "center", "end", "justify", "inside", "outside", "left", "right"]
|
||||||
|
VALID_BREAK = ["auto", "column", "page", "even-page", "odd-page", "inherit"]
|
||||||
|
VALID_LEVEL = ["1", "2", "3", "4"]
|
||||||
|
VALID_CLASS = ["text", "chapter"]
|
||||||
|
VALID_WEIGHT = ["normal", "inherit", "bold"]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
# Attributes
|
||||||
|
self._mAttr = {
|
||||||
|
"display-name": ["style", None],
|
||||||
|
"parent-style-name": ["style", None],
|
||||||
|
"next-style-name": ["style", None],
|
||||||
|
"default-outline-level": ["style", None],
|
||||||
|
"class": ["style", None],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Paragraph Attributes
|
||||||
|
self._pAttr = {
|
||||||
|
"margin-top": ["fo", None],
|
||||||
|
"margin-bottom": ["fo", None],
|
||||||
|
"line-height": ["fo", None],
|
||||||
|
"text-align": ["fo", None],
|
||||||
|
"break-before": ["fo", None],
|
||||||
|
"break-after": ["fo", None],
|
||||||
|
}
|
||||||
|
|
||||||
|
# text Attributes
|
||||||
|
self._tAttr = {
|
||||||
|
"font-size": ["fo", None],
|
||||||
|
"font-weight": ["fo", None],
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Attribute Setters
|
||||||
|
##
|
||||||
|
|
||||||
|
def setDisplayName(self, theValue):
|
||||||
|
self._mAttr["display-name"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setParentStyleName(self, theValue):
|
||||||
|
self._mAttr["parent-style-name"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setNextStyleName(self, theValue):
|
||||||
|
self._mAttr["next-style-name"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setOutlineLevel(self, theValue):
|
||||||
|
if theValue in self.VALID_LEVEL:
|
||||||
|
self._mAttr["default-outline-level"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setClass(self, theValue):
|
||||||
|
if theValue in self.VALID_CLASS:
|
||||||
|
self._mAttr["class"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Paragraph Setters
|
||||||
|
##
|
||||||
|
|
||||||
|
def setMarginTop(self, theValue):
|
||||||
|
self._pAttr["margin-top"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setMarginBottom(self, theValue):
|
||||||
|
self._pAttr["margin-bottom"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setLineHeight(self, theValue):
|
||||||
|
self._pAttr["line-height"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setTextAlign(self, theValue):
|
||||||
|
if theValue in self.VALID_ALIGN:
|
||||||
|
self._pAttr["text-align"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setBreakBefore(self, theValue):
|
||||||
|
if theValue in self.VALID_BREAK:
|
||||||
|
self._pAttr["break-before"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setBreakAfter(self, theValue):
|
||||||
|
if theValue in self.VALID_BREAK:
|
||||||
|
self._pAttr["break-after"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Text Setters
|
||||||
|
##
|
||||||
|
|
||||||
|
def setFontSize(self, theValue):
|
||||||
|
self._tAttr["font-size"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setFontWeight(self, theValue):
|
||||||
|
if theValue in self.VALID_WEIGHT:
|
||||||
|
self._tAttr["font-weight"][1] = str(theValue)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def checkNew(self, refStyle):
|
||||||
|
"""Check if there are new settings in refStyle that differ from
|
||||||
|
those in the current object.
|
||||||
|
"""
|
||||||
|
for aName, (aNm, aVal) in refStyle._mAttr.items():
|
||||||
|
if aVal is not None and aVal != self._mAttr[aName][1]:
|
||||||
|
return True
|
||||||
|
for aName, (aNm, aVal) in refStyle._pAttr.items():
|
||||||
|
if aVal is not None and aVal != self._pAttr[aName][1]:
|
||||||
|
return True
|
||||||
|
for aName, (aNm, aVal) in refStyle._tAttr.items():
|
||||||
|
if aVal is not None and aVal != self._tAttr[aName][1]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getID(self):
|
||||||
|
"""Generate a unique ID from the settings.
|
||||||
|
"""
|
||||||
|
theString = (
|
||||||
|
f"Paragraph:Main:{str(self._mAttr)}:"
|
||||||
|
f"Paragraph:Para:{str(self._pAttr)}:"
|
||||||
|
f"Paragraph:Text:{str(self._tAttr)}:"
|
||||||
|
)
|
||||||
|
return sha256(theString.encode()).hexdigest()
|
||||||
|
|
||||||
|
def packXML(self, xParent, xName):
|
||||||
|
"""Pack the content into an xml element.
|
||||||
|
"""
|
||||||
|
theAttr = {}
|
||||||
|
theAttr[_mkTag("style", "name")] = xName
|
||||||
|
theAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
|
for aName, (aNm, aVal) in self._mAttr.items():
|
||||||
|
if aVal is not None:
|
||||||
|
theAttr[_mkTag(aNm, aName)] = aVal
|
||||||
|
|
||||||
|
xEntry = etree.SubElement(xParent, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
|
theAttr = {}
|
||||||
|
for aName, (aNm, aVal) in self._pAttr.items():
|
||||||
|
if aVal is not None:
|
||||||
|
theAttr[_mkTag(aNm, aName)] = aVal
|
||||||
|
|
||||||
|
if theAttr:
|
||||||
|
etree.SubElement(xEntry, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
||||||
|
|
||||||
|
theAttr = {}
|
||||||
|
for aName, (aNm, aVal) in self._tAttr.items():
|
||||||
|
if aVal is not None:
|
||||||
|
theAttr[_mkTag(aNm, aName)] = aVal
|
||||||
|
|
||||||
|
if theAttr:
|
||||||
|
etree.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=theAttr)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class ODTParagraphStyle
|
||||||
|
|
||||||
|
# =============================================================================================== #
|
||||||
# Local Functions
|
# Local Functions
|
||||||
# =========================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def _mkTag(nsName, tagName):
|
def _mkTag(nsName, tagName):
|
||||||
"""Assemble namespace and tag name.
|
"""Assemble namespace and tag name.
|
||||||
|
|||||||
@@ -23,8 +23,25 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import nw
|
import nw
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
from nw.core import NWProject, NWIndex, ToOdt
|
from nw.core import NWProject, NWIndex, ToOdt
|
||||||
|
|
||||||
|
XML_NS = [
|
||||||
|
' 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"',
|
||||||
|
' xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"',
|
||||||
|
]
|
||||||
|
|
||||||
|
def xmlToText(xElem):
|
||||||
|
"""Get the text content of an XML element.
|
||||||
|
"""
|
||||||
|
rTxt = etree.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
||||||
|
for nSpace in XML_NS:
|
||||||
|
rTxt = rTxt.replace(nSpace, "")
|
||||||
|
return rTxt
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToOdt_Convert(tmpConf, dummyGUI):
|
def testCoreToOdt_Convert(tmpConf, dummyGUI):
|
||||||
"""Test the converter of the ToHtml class.
|
"""Test the converter of the ToHtml class.
|
||||||
@@ -46,6 +63,22 @@ def testCoreToOdt_Convert(tmpConf, dummyGUI):
|
|||||||
theDoc.initDocument()
|
theDoc.initDocument()
|
||||||
theDoc.doConvert()
|
theDoc.doConvert()
|
||||||
theDoc.closeDocument()
|
theDoc.closeDocument()
|
||||||
assert theDoc.theResult == ""
|
assert xmlToText(theDoc._xText) == (
|
||||||
|
'<office:text>'
|
||||||
|
'<text:h text:style-name="Heading_1" text:outline-level="1">Title</text:h>'
|
||||||
|
'</office:text>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Header 1
|
||||||
|
theDoc.theText = "## Chapter Title\n"
|
||||||
|
theDoc.tokenizeText()
|
||||||
|
theDoc.initDocument()
|
||||||
|
theDoc.doConvert()
|
||||||
|
theDoc.closeDocument()
|
||||||
|
assert xmlToText(theDoc._xText) == (
|
||||||
|
'<office:text>'
|
||||||
|
'<text:h text:style-name="Heading_2" text:outline-level="2">Chapter Title</text:h>'
|
||||||
|
'</office:text>'
|
||||||
|
)
|
||||||
|
|
||||||
# END Test testCoreToOdt_Convert
|
# END Test testCoreToOdt_Convert
|
||||||
|
|||||||
Reference in New Issue
Block a user