From cbbe1b4f479a4ea852f4cfac687b549ddf0b5fba Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 27 Jan 2021 13:36:09 +0100 Subject: [PATCH] Add ODT meta namespace, add font format to HTML, and merge page break settings --- nw/core/tohtml.py | 43 +++++++++---------- nw/core/tokenizer.py | 10 ++--- nw/core/toodt.py | 21 ++++++++- nw/gui/build.py | 2 +- .../guiBuild_Tool_Step1_Lorem_Ipsum.htm | 3 +- .../guiBuild_Tool_Step2_Lorem_Ipsum.htm | 3 +- .../guiBuild_Tool_Step3_Lorem_Ipsum.htm | 3 +- .../guiBuild_Tool_Step4H_Lorem_Ipsum.json | 5 ++- .../guiBuild_Tool_Step4_Lorem_Ipsum.htm | 3 +- tests/test_core/test_core_tohtml.py | 20 +++------ tests/test_core/test_core_tokenizer.py | 2 +- tests/test_core/test_core_toodt.py | 1 + 12 files changed, 62 insertions(+), 54 deletions(-) diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 46c7d8b6..9c17aeca 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -173,16 +173,12 @@ class ToHtml(Tokenizer): aStyle.append("text-align: justify;") if tStyle & self.A_PBB: aStyle.append("page-break-before: always;") - if tStyle & self.A_PBB_AV: - aStyle.append("page-break-before: avoid;") - if tStyle & self.A_PBB_NO: - aStyle.append("page-break-before: never;") + if tStyle & self.A_PBB_AUT: + aStyle.append("page-break-before: auto;") if tStyle & self.A_PBA: aStyle.append("page-break-after: always;") - if tStyle & self.A_PBA_AV: - aStyle.append("page-break-after: avoid;") - if tStyle & self.A_PBA_NO: - aStyle.append("page-break-after: never;") + if tStyle & self.A_PBA_AUT: + aStyle.append("page-break-after: auto;") if len(aStyle) > 0: hStyle = " style='%s'" % (" ".join(aStyle)) @@ -268,22 +264,23 @@ class ToHtml(Tokenizer): if not self.cssStyles: return theStyles - if self.doJustify: - theStyles.append(r"p {text-align: justify;}") - else: - theStyles.append(r"p {text-align: left;}") + textAlign = "justify" if self.doJustify else "left" - theStyles.append(r"h1, h2 {color: rgb(66, 113, 174);}") - theStyles.append(r"h3, h4 {color: rgb(50, 50, 50);}") - theStyles.append(r"h1, h2, h3, h4 {page-break-after: avoid;}") - theStyles.append(r"a {color: rgb(66, 113, 174);}") - theStyles.append(r".title {font-size: 2.5em;}") - theStyles.append(r".tags {color: rgb(245, 135, 31); font-weight: bold;}") - theStyles.append(r".break {text-align: left;}") - theStyles.append(r".sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}") - theStyles.append(r".skip {margin-top: 1em; margin-bottom: 1em;}") - theStyles.append(r".synopsis {font-style: italic;}") - theStyles.append(r".comment {font-style: italic; color: rgb(100, 100, 100);}") + theStyles.append("body {font-family: '%s'; font-size: %dpt}" % ( + self.textFont, self.textSize) + ) + theStyles.append("p {text-align: %s;}" % textAlign) + theStyles.append("h1, h2 {color: rgb(66, 113, 174);}") + theStyles.append("h3, h4 {color: rgb(50, 50, 50);}") + theStyles.append("h1, h2, h3, h4 {page-break-after: avoid;}") + theStyles.append("a {color: rgb(66, 113, 174);}") + theStyles.append(".title {font-size: 2.5em;}") + theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}") + theStyles.append(".break {text-align: left;}") + theStyles.append(".sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}") + theStyles.append(".skip {margin-top: 1em; margin-bottom: 1em;}") + theStyles.append(".synopsis {font-style: italic;}") + theStyles.append(".comment {font-style: italic; color: rgb(100, 100, 100);}") return theStyles diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 4e0c6290..04ded5c1 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -67,11 +67,9 @@ class Tokenizer(): A_CENTRE = 0x0004 # Centred A_JUSTIFY = 0x0008 # Justified A_PBB = 0x0010 # Page break before always - A_PBB_AV = 0x0020 # Page break before avoid - A_PBB_NO = 0x0040 # Page break before never - A_PBA = 0x0080 # Page break after always - A_PBA_AV = 0x0100 # Page break after avoid - A_PBA_NO = 0x0200 # Page break after avoid + A_PBB_AUT = 0x0020 # Page break before auto + A_PBA = 0x0040 # Page break after always + A_PBA_AUT = 0x0080 # Page break after auto def __init__(self, theProject, theParent): @@ -628,7 +626,7 @@ class Tokenizer(): tToken[1], tToken[2], tToken[3], - self.A_PBB_NO | self.A_CENTRE + self.A_PBB_AUT | self.A_CENTRE ) else: self.theTokens[n] = ( diff --git a/nw/core/toodt.py b/nw/core/toodt.py index 69b92157..23721e8e 100644 --- a/nw/core/toodt.py +++ b/nw/core/toodt.py @@ -30,6 +30,7 @@ import os from lxml import etree from hashlib import sha256 +from datetime import datetime from nw.core.tokenizer import Tokenizer @@ -39,6 +40,7 @@ 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", + "meta" : "urn:oasis:names:tc:opendocument:xmlns:meta:1.0", "fo" : "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0", } @@ -113,12 +115,19 @@ class ToOdt(Tokenizer): _mkTag("office", "mimetype") : "application/vnd.oasis.opendocument.text", } self._xRoot = etree.Element(_mkTag("office", "document"), attrib=rAttr, nsmap=XML_NS) + self._xMeta = etree.SubElement(self._xRoot, _mkTag("office", "meta")) self._xFont = etree.SubElement(self._xRoot, _mkTag("office", "font-face-decls")) self._xStyl = etree.SubElement(self._xRoot, _mkTag("office", "styles")) 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")) + # Meta Data + xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "creation-date")) + xMeta.text = datetime.now().isoformat() + xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "generator")) + xMeta.text = f"novelWriter/{nw.__version__}" + # Re-Init Variables self._fontFamily = self.textFont if len(self.textFont.split()) > 1: @@ -166,8 +175,12 @@ class ToOdt(Tokenizer): oStyle.setTextAlign("justify") if tStyle & self.A_PBB: oStyle.setBreakBefore("page") + if tStyle & self.A_PBB_AUT: + oStyle.setBreakBefore("auto") if tStyle & self.A_PBA: oStyle.setBreakAfter("page") + if tStyle & self.A_PBA_AUT: + oStyle.setBreakAfter("auto") # Process Text Type if tType == self.T_EMPTY: @@ -212,7 +225,7 @@ class ToOdt(Tokenizer): if parStyle is None: parStyle = oStyle # for xPos, xLen, xFmt in reversed(tFormat): - # tTemp = tTemp[:xPos]+htmlTags[xFmt]+tTemp[xPos+xLen:] + # tTemp = tTemp[:xPos] + htmlTags[xFmt] + tTemp[xPos+xLen:] if tText.endswith(" "): thisPar.append(tTemp.rstrip()+"\n") hasHardBreak = True @@ -511,6 +524,10 @@ class ToOdt(Tokenizer): # =============================================================================================== # class ODTParagraphStyle(): + """Wrapper class for the paragraph style setting used by the + exporter. Only the used settings are exposed here to keep the class + minimal and fast. + """ VALID_ALIGN = ["start", "center", "end", "justify", "inside", "outside", "left", "right"] VALID_BREAK = ["auto", "column", "page", "even-page", "odd-page", "inherit"] @@ -539,7 +556,7 @@ class ODTParagraphStyle(): "break-after": ["fo", None], } - # text Attributes + # Text Attributes self._tAttr = { "font-name": ["style", None], "font-family": ["fo", None], diff --git a/nw/gui/build.py b/nw/gui/build.py index 9e80b588..8e2b3dc7 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -573,7 +573,7 @@ class GuiBuildNovel(QDialog): self.nwdText = nwdText tEnd = int(time()) - logger.debug("Built project in %.3f ms" % (1000*(tEnd-tStart))) + logger.debug("Built project in %.3f ms" % (1000*(tEnd - tStart))) self.htmlStyle = makeHtml.getStyleSheet() self.buildTime = tEnd diff --git a/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm b/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm index a3e4432f..70b6b8ac 100644 --- a/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm +++ b/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm @@ -5,6 +5,7 @@ Lorem Ipsum
-

Lorem Ipsum

+

Lorem Ipsum

By lipsum.com

“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”

“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”

diff --git a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm index 4ab595a3..30d610db 100644 --- a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm +++ b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm @@ -5,6 +5,7 @@ Lorem Ipsum
-

Lorem Ipsum

+

Lorem Ipsum

By lipsum.com

“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”

“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”

diff --git a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm index 8e6ad4b5..2c385360 100644 --- a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm +++ b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm @@ -5,6 +5,7 @@ Lorem Ipsum
-

Lorem Ipsum

+

Lorem Ipsum

By lipsum.com

“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”

“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”

diff --git a/tests/reference/guiBuild_Tool_Step4H_Lorem_Ipsum.json b/tests/reference/guiBuild_Tool_Step4H_Lorem_Ipsum.json index c67a1321..ede728bb 100644 --- a/tests/reference/guiBuild_Tool_Step4H_Lorem_Ipsum.json +++ b/tests/reference/guiBuild_Tool_Step4H_Lorem_Ipsum.json @@ -5,10 +5,11 @@ "authors": [ "lipsum.com" ], - "buildTime": 1611662802 + "buildTime": 1611750760 }, "text": { "css": [ + "body {font-family: 'Sans'; font-size: 12pt}", "p {text-align: justify;}", "h1, h2 {color: rgb(66, 113, 174);}", "h3, h4 {color: rgb(50, 50, 50);}", @@ -24,7 +25,7 @@ ], "html": [ [ - "

Lorem Ipsum

" + "

Lorem Ipsum

" ], [ "" diff --git a/tests/reference/guiBuild_Tool_Step4_Lorem_Ipsum.htm b/tests/reference/guiBuild_Tool_Step4_Lorem_Ipsum.htm index f5360c30..fe3676c8 100644 --- a/tests/reference/guiBuild_Tool_Step4_Lorem_Ipsum.htm +++ b/tests/reference/guiBuild_Tool_Step4_Lorem_Ipsum.htm @@ -5,6 +5,7 @@ Lorem Ipsum
-

Lorem Ipsum

+

Lorem Ipsum

Prologue

Synopsis: Explanation from the lipsum.com website.

Act One

diff --git a/tests/test_core/test_core_tohtml.py b/tests/test_core/test_core_tohtml.py index be99c340..0c20dddd 100644 --- a/tests/test_core/test_core_tohtml.py +++ b/tests/test_core/test_core_tohtml.py @@ -211,12 +211,12 @@ def testCoreToHtml_Convert(dummyGUI): # Title theHtml.theTokens = [ - (theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB_NO | theHtml.A_CENTRE), + (theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB_AUT | theHtml.A_CENTRE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), ] theHtml.doConvert() assert theHtml.theResult == ( - "

" + "

" "A Title

\n" ) @@ -299,24 +299,14 @@ def testCoreToHtml_Convert(dummyGUI): "style='page-break-before: always; page-break-after: always;'>A Title\n" ) - # Page Break Avoid + # Page Break Auto theHtml.theTokens = [ - (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB_AV | theHtml.A_PBA_AV), + (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB_AUT | theHtml.A_PBA_AUT), ] theHtml.doConvert() assert theHtml.theResult == ( "

A Title

\n" - ) - - # Page Break ANever - theHtml.theTokens = [ - (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB_NO | theHtml.A_PBA_NO), - ] - theHtml.doConvert() - assert theHtml.theResult == ( - "

A Title

\n" + "style='page-break-before: auto; page-break-after: auto;'>A Title\n" ) # Preview Mode diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index 64096f99..ed5fdc07 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -623,7 +623,7 @@ def testCoreToken_Headers(dummyGUI): theToken.isPart = False theToken.doHeaders() assert theToken.theTokens == [ - (Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_PBB_NO | Tokenizer.A_CENTRE), + (Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_PBB_AUT | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_PBA | Tokenizer.A_CENTRE), ] diff --git a/tests/test_core/test_core_toodt.py b/tests/test_core/test_core_toodt.py index c0e8c797..ae3e0d6b 100644 --- a/tests/test_core/test_core_toodt.py +++ b/tests/test_core/test_core_toodt.py @@ -31,6 +31,7 @@ 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:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"', ' xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"', ]