Update test coverage
This commit is contained in:
@@ -903,7 +903,6 @@ class ToDocX(Tokenizer):
|
|||||||
"""Populate document.xml."""
|
"""Populate document.xml."""
|
||||||
rId = self._nextRelId()
|
rId = self._nextRelId()
|
||||||
xRoot = xmlElement(_wTag("document"))
|
xRoot = xmlElement(_wTag("document"))
|
||||||
xRoot.set("xmlns:w14", "http://schemas.microsoft.com/office/word/2010/wordml")
|
|
||||||
xBody = xmlSubElem(xRoot, _wTag("body"))
|
xBody = xmlSubElem(xRoot, _wTag("body"))
|
||||||
self._rels["document.xml"] = DocXXmlRel(
|
self._rels["document.xml"] = DocXXmlRel(
|
||||||
rId=rId,
|
rId=rId,
|
||||||
|
|||||||
@@ -2065,12 +2065,8 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
feature for French, Spanish, etc, so it doesn't insert a
|
feature for French, Spanish, etc, so it doesn't insert a
|
||||||
space before colons in meta data lines. See issue #1090.
|
space before colons in meta data lines. See issue #1090.
|
||||||
"""
|
"""
|
||||||
if char == ":" and len(text) > 1:
|
if char == ":" and len(text) > 1 and text[0] == "@":
|
||||||
if text[0] == "@":
|
return False
|
||||||
return False
|
|
||||||
if text[0] == "%":
|
|
||||||
if text[1:].lstrip()[:9].lower() == "synopsis:":
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _autoSelect(self) -> QTextCursor:
|
def _autoSelect(self) -> QTextCursor:
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<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:number="urn:oasis:names:tc:opendocument:xmlns:datastyle: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: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:number="urn:oasis:names:tc:opendocument:xmlns:datastyle: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" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||||
<office:meta>
|
<office:meta>
|
||||||
<meta:creation-date>2024-10-29T09:33:22</meta:creation-date>
|
<meta:creation-date>2024-10-29T23:24:19</meta:creation-date>
|
||||||
<meta:generator>novelWriter/2.6a3</meta:generator>
|
<meta:generator>novelWriter/2.6a3</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>2024-10-29T09:33:22</dc:date>
|
<dc:date>2024-10-29T23:24:19</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>
|
||||||
@@ -82,6 +82,12 @@
|
|||||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
||||||
<style:paragraph-properties fo:break-before="page" />
|
<style:paragraph-properties fo:break-before="page" />
|
||||||
</style:style>
|
</style:style>
|
||||||
|
<style:style style:name="T1" style:family="text">
|
||||||
|
<style:text-properties fo:font-weight="bold" />
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="T2" style:family="text">
|
||||||
|
<style:text-properties fo:color="#4271ae" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" />
|
||||||
|
</style:style>
|
||||||
</office:automatic-styles>
|
</office:automatic-styles>
|
||||||
<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">
|
||||||
@@ -95,10 +101,30 @@
|
|||||||
</office:master-styles>
|
</office:master-styles>
|
||||||
<office:body>
|
<office:body>
|
||||||
<office:text>
|
<office:text>
|
||||||
<text:h text:style-name="Heading_20_2" text:outline-level="2">Chapter One</text:h>
|
<text:user-field-decls>
|
||||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptTitleCount" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptParagraphCount" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="217" text:name="ManuscriptAllWords" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="211" text:name="ManuscriptTextWords" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="6" text:name="ManuscriptTitleWords" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1471" text:name="ManuscriptAllChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1441" text:name="ManuscriptTextChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="30" text:name="ManuscriptTitleChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1258" text:name="ManuscriptAllWordChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1231" text:name="ManuscriptTextWordChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="27" text:name="ManuscriptTitleWordChars" />
|
||||||
|
</text:user-field-decls>
|
||||||
|
<text:p text:style-name="Title">My Novel</text:p>
|
||||||
|
<text:p text:style-name="Text_20_body"><text:span text:style-name="T1">Word Count: </text:span><text:span text:style-name="T1"><text:user-field-get style:data-style-name="N0" text:name="ManuscriptAllWords">0</text:user-field-get></text:span><text:line-break /><text:user-field-get style:data-style-name="N0" text:name="ManuscriptParagraphCount">0</text:user-field-get> paragrphs<text:line-break />Web: <text:a xlink:type="simple" xlink:href="http://example.com" text:style-name="T2">http://example.com</text:a></text:p>
|
||||||
|
<text:h text:style-name="P1" text:outline-level="2">Chapter One</text:h>
|
||||||
|
<text:p text:style-name="Text_20_body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum.</text:p>
|
||||||
<text:h text:style-name="P1" text:outline-level="2">Chapter Two</text:h>
|
<text:h text:style-name="P1" text:outline-level="2">Chapter Two</text:h>
|
||||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
<text:p text:style-name="Text_20_body">Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.<text:note text:id="ftn1" text:note-class="footnote">
|
||||||
|
<text:note-citation>1</text:note-citation>
|
||||||
|
<text:note-body>
|
||||||
|
<text:p text:style-name="Footnote">Lorem ipsum</text:p>
|
||||||
|
</text:note-body>
|
||||||
|
</text:note></text:p>
|
||||||
</office:text>
|
</office:text>
|
||||||
</office:body>
|
</office:body>
|
||||||
</office:document>
|
</office:document>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<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: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" xmlns:xlink="http://www.w3.org/1999/xlink" 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>
|
||||||
@@ -7,13 +7,47 @@
|
|||||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
||||||
<style:paragraph-properties fo:break-before="page" />
|
<style:paragraph-properties fo:break-before="page" />
|
||||||
</style:style>
|
</style:style>
|
||||||
|
<style:style style:name="T1" style:family="text">
|
||||||
|
<style:text-properties fo:font-weight="bold" />
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="T2" style:family="text">
|
||||||
|
<style:text-properties fo:color="#4271ae" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" />
|
||||||
|
</style:style>
|
||||||
</office:automatic-styles>
|
</office:automatic-styles>
|
||||||
<office:body>
|
<office:body>
|
||||||
<office:text>
|
<office:text>
|
||||||
<text:h text:style-name="Heading_20_2" text:outline-level="2">Chapter One</text:h>
|
<text:user-field-decls>
|
||||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptTitleCount" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptParagraphCount" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="217" text:name="ManuscriptAllWords" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="211" text:name="ManuscriptTextWords" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="6" text:name="ManuscriptTitleWords" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1471" text:name="ManuscriptAllChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1441" text:name="ManuscriptTextChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="30" text:name="ManuscriptTitleChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1258" text:name="ManuscriptAllWordChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="1231" text:name="ManuscriptTextWordChars" />
|
||||||
|
<text:user-field-decl office:value-type="float" office:value="27" text:name="ManuscriptTitleWordChars" />
|
||||||
|
</text:user-field-decls>
|
||||||
|
<text:p text:style-name="Title">My Novel</text:p>
|
||||||
|
<text:p text:style-name="Text_20_body">
|
||||||
|
<text:span text:style-name="T1">Word Count: </text:span>
|
||||||
|
<text:span text:style-name="T1">
|
||||||
|
<text:user-field-get style:data-style-name="N0" text:name="ManuscriptAllWords">0</text:user-field-get>
|
||||||
|
</text:span>
|
||||||
|
<text:line-break />
|
||||||
|
<text:user-field-get style:data-style-name="N0" text:name="ManuscriptParagraphCount">0</text:user-field-get> paragrphs<text:line-break />Web: <text:a xlink:type="simple" xlink:href="http://example.com" text:style-name="T2">http://example.com</text:a>
|
||||||
|
</text:p>
|
||||||
|
<text:h text:style-name="P1" text:outline-level="2">Chapter One</text:h>
|
||||||
|
<text:p text:style-name="Text_20_body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum.</text:p>
|
||||||
<text:h text:style-name="P1" text:outline-level="2">Chapter Two</text:h>
|
<text:h text:style-name="P1" text:outline-level="2">Chapter Two</text:h>
|
||||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
<text:p text:style-name="Text_20_body">Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.<text:note text:id="ftn1" text:note-class="footnote">
|
||||||
|
<text:note-citation>1</text:note-citation>
|
||||||
|
<text:note-body>
|
||||||
|
<text:p text:style-name="Footnote">Lorem ipsum</text:p>
|
||||||
|
</text:note-body>
|
||||||
|
</text:note>
|
||||||
|
</text:p>
|
||||||
</office:text>
|
</office:text>
|
||||||
</office:body>
|
</office:body>
|
||||||
</office:document-content>
|
</office:document-content>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
%%~name: New Scene
|
%%~name: New Scene
|
||||||
%%~path: 000000000000d/000000000000f
|
%%~path: 000000000000d/000000000000f
|
||||||
%%~kind: NOVEL/DOCUMENT
|
%%~kind: NOVEL/DOCUMENT
|
||||||
%%~hash: 0566024662fb6ed7ed645717addd64dabf058915
|
%%~hash: 89b54ddaec2fddfd220e91dd438c84fb3aef9fc2
|
||||||
%%~date: 2024-10-27 13:03:13/2024-10-27 13:03:18
|
%%~date: 2024-10-30 00:07:21/2024-10-30 00:07:26
|
||||||
# Novel
|
# Novel
|
||||||
|
|
||||||
## Chapter
|
## Chapter
|
||||||
@@ -40,7 +40,9 @@ Some “ double quoted text with spaces padded ”.
|
|||||||
|
|
||||||
@object: NoSpaceAdded
|
@object: NoSpaceAdded
|
||||||
|
|
||||||
% synopsis: No space before this colon.
|
% synopsis : Space before this is OK.
|
||||||
|
|
||||||
|
%Footnote.abc : A simple footnote.
|
||||||
|
|
||||||
Add space before this colon : See?
|
Add space before this colon : See?
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.6a3" hexVersion="0x020600a3" fileVersion="1.5" fileRevision="4" timeStamp="2024-10-27 13:01:24">
|
<novelWriterXML appVersion="2.6a3" hexVersion="0x020600a3" fileVersion="1.5" fileRevision="4" timeStamp="2024-10-30 00:06:45">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="6">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="5">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
</project>
|
</project>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H1" charCount="918" wordCount="154" paraCount="17" cursorPos="1140" />
|
<meta expanded="no" heading="H1" charCount="918" wordCount="154" paraCount="17" cursorPos="1174" />
|
||||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||||
|
|||||||
@@ -1490,5 +1490,6 @@ def testCoreIndex_processComment():
|
|||||||
# Padding with term
|
# Padding with term
|
||||||
assert processComment("%note.term: Hi") == (nwComment.NOTE, "term", "Hi", 6, 11)
|
assert processComment("%note.term: Hi") == (nwComment.NOTE, "term", "Hi", 6, 11)
|
||||||
assert processComment("% note.term: Hi") == (nwComment.NOTE, "term", "Hi", 7, 12)
|
assert processComment("% note.term: Hi") == (nwComment.NOTE, "term", "Hi", 7, 12)
|
||||||
|
assert processComment("% note.term : Hi") == (nwComment.NOTE, "term", "Hi", 7, 13)
|
||||||
assert processComment("% note. term : Hi") == (nwComment.PLAIN, "", "note. term : Hi", 0, 0)
|
assert processComment("% note. term : Hi") == (nwComment.PLAIN, "", "note. term : Hi", 0, 0)
|
||||||
assert processComment("% note . term : Hi") == (nwComment.PLAIN, "", "note . term : Hi", 0, 0)
|
assert processComment("% note . term : Hi") == (nwComment.PLAIN, "", "note . term : Hi", 0, 0)
|
||||||
|
|||||||
@@ -32,28 +32,9 @@ from novelwriter.core.docbuild import NWBuildDocument
|
|||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.enum import nwBuildFmt
|
from novelwriter.enum import nwBuildFmt
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
||||||
from novelwriter.formats.todocx import ToDocX, _mkTag, _wTag
|
from novelwriter.formats.todocx import OOXML_SCM, ToDocX, _mkTag, _wTag
|
||||||
|
|
||||||
from tests.tools import DOCX_IGNORE, cmpFiles
|
from tests.tools import DOCX_IGNORE, cmpFiles, xmlToText
|
||||||
|
|
||||||
OOXML_SCM = "http://schemas.openxmlformats.org"
|
|
||||||
XML_NS = [
|
|
||||||
f' xmlns:r="{OOXML_SCM}/officeDocument/2006/relationships"',
|
|
||||||
f' xmlns:w="{OOXML_SCM}/wordprocessingml/2006/main"',
|
|
||||||
f' xmlns:cp="{OOXML_SCM}/package/2006/metadata/core-properties"',
|
|
||||||
' xmlns:dc="http://purl.org/dc/elements/1.1/"',
|
|
||||||
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
|
|
||||||
' xmlns:xml="http://www.w3.org/XML/1998/namespace"',
|
|
||||||
' xmlns:dcterms="http://purl.org/dc/terms/"',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def xmlToText(xElem):
|
|
||||||
"""Get the text content of an XML element."""
|
|
||||||
rTxt = ET.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
|
||||||
for ns in XML_NS:
|
|
||||||
rTxt = rTxt.replace(ns, "")
|
|
||||||
return rTxt
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -610,6 +591,49 @@ def testFmtToDocX_Footnotes(mockGUI):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testFmtToDocX_Fields(mockGUI):
|
||||||
|
"""Test formatting of footnotes."""
|
||||||
|
project = NWProject()
|
||||||
|
doc = ToDocX(project)
|
||||||
|
doc.initDocument()
|
||||||
|
|
||||||
|
# Field Builder
|
||||||
|
xNode = doc._generateField("a:b", 0x00)
|
||||||
|
assert isinstance(xNode, ET.Element)
|
||||||
|
assert xmlToText(xNode) == "<w:r><w:rPr /><w:t>0</w:t></w:r>"
|
||||||
|
assert doc._usedFields == [(xNode.find(_wTag("t")), "b")]
|
||||||
|
|
||||||
|
assert doc._generateField("a", 0x00) is None
|
||||||
|
|
||||||
|
# Full Processing
|
||||||
|
doc._text = (
|
||||||
|
"Word Count: [field:allWords]\n"
|
||||||
|
"Character Count: [field:allChars]\n"
|
||||||
|
"Chicken Count: [field:allChickens]\n"
|
||||||
|
)
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc.countStats()
|
||||||
|
doc._documentXml(None, None)
|
||||||
|
assert xmlToText(doc._files["document.xml"].xml) == (
|
||||||
|
'<w:document><w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
|
'<w:r><w:rPr /><w:t xml:space="preserve">Word Count: </w:t></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:t>6</w:t></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:br /><w:t xml:space="preserve">Character Count: </w:t></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:t>46</w:t></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:br /><w:t xml:space="preserve">Chicken Count: </w:t></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:t>0</w:t></w:r>'
|
||||||
|
'</w:p><w:sectPr>'
|
||||||
|
'<w:footnotePr><w:numFmt w:val="decimal" /></w:footnotePr>'
|
||||||
|
'<w:pgSz w:w="11905" w:h="16837" w:orient="portrait" />'
|
||||||
|
'<w:pgMar w:top="1133" w:right="1133" w:bottom="1133" w:left="1133" '
|
||||||
|
'w:header="566" w:footer="0" w:gutter="0" />'
|
||||||
|
'<w:pgNumType w:start="1" w:fmt="decimal" /><w:titlePg />'
|
||||||
|
'</w:sectPr></w:body></w:document>'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testFmtToDocX_SaveDocument(mockGUI, prjLipsum, fncPath, tstPaths):
|
def testFmtToDocX_SaveDocument(mockGUI, prjLipsum, fncPath, tstPaths):
|
||||||
"""Test document output."""
|
"""Test document output."""
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class BareTokenizer(Tokenizer):
|
|||||||
super().doConvert() # type: ignore (deliberate check)
|
super().doConvert() # type: ignore (deliberate check)
|
||||||
|
|
||||||
def closeDocument(self):
|
def closeDocument(self):
|
||||||
super().doConvert() # type: ignore (deliberate check)
|
super().closeDocument() # type: ignore (deliberate check)
|
||||||
|
|
||||||
def saveDocument(self, path) -> None:
|
def saveDocument(self, path) -> None:
|
||||||
super().saveDocument(path) # type: ignore (deliberate check)
|
super().saveDocument(path) # type: ignore (deliberate check)
|
||||||
@@ -60,6 +60,9 @@ def testFmtToken_Abstracts(mockGUI, tstPaths):
|
|||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
tokens.doConvert()
|
tokens.doConvert()
|
||||||
|
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
tokens.closeDocument()
|
||||||
|
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
tokens.saveDocument(tstPaths)
|
tokens.saveDocument(tstPaths)
|
||||||
|
|
||||||
|
|||||||
@@ -35,26 +35,7 @@ from novelwriter.core.project import NWProject
|
|||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt
|
||||||
from novelwriter.formats.toodt import ODTParagraphStyle, ODTTextStyle, ToOdt, XMLParagraph, _mkTag
|
from novelwriter.formats.toodt import ODTParagraphStyle, ODTTextStyle, ToOdt, XMLParagraph, _mkTag
|
||||||
|
|
||||||
from tests.tools import ODT_IGNORE, cmpFiles
|
from tests.tools import ODT_IGNORE, cmpFiles, xmlToText
|
||||||
|
|
||||||
XML_NS = [
|
|
||||||
' 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"',
|
|
||||||
' xmlns:xlink="http://www.w3.org/1999/xlink"',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def xmlToText(xElem):
|
|
||||||
"""Get the text content of an XML element."""
|
|
||||||
rTxt = ET.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
|
||||||
for ns in XML_NS:
|
|
||||||
rTxt = rTxt.replace(ns, "")
|
|
||||||
return rTxt
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -237,6 +218,46 @@ def testFmtToOdt_TextFormatting(mockGUI):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testFmtToOdt_Fields(mockGUI):
|
||||||
|
"""Test formatting of footnotes."""
|
||||||
|
project = NWProject()
|
||||||
|
odt = ToOdt(project, True)
|
||||||
|
odt.initDocument()
|
||||||
|
|
||||||
|
# Field Builder
|
||||||
|
xNode = odt._generateField("a:allWords", 0x00)
|
||||||
|
assert isinstance(xNode, ET.Element)
|
||||||
|
assert xmlToText(xNode) == (
|
||||||
|
'<text:user-field-getstyle:data-style-name="N0" text:name="ManuscriptAllWords">'
|
||||||
|
'0</text:user-field-get>'
|
||||||
|
)
|
||||||
|
# assert odt._usedFields == [(xNode.find(_wTag("t")), "b")]
|
||||||
|
|
||||||
|
assert odt._generateField("a", 0x00) is None
|
||||||
|
|
||||||
|
# Full Processing
|
||||||
|
odt._text = (
|
||||||
|
"Word Count: [field:allWords]\n"
|
||||||
|
"Character Count: [field:allChars]\n"
|
||||||
|
"Chicken Count: [field:allChickens]\n"
|
||||||
|
)
|
||||||
|
odt.tokenizeText()
|
||||||
|
odt.doConvert()
|
||||||
|
odt.countStats()
|
||||||
|
assert xmlToText(odt._xBody) == (
|
||||||
|
'<office:body><office:text>'
|
||||||
|
'<text:p text:style-name="Text_20_body">'
|
||||||
|
'Word Count: <text:user-field-get style:data-style-name="N0" '
|
||||||
|
'text:name="ManuscriptAllWords">0</text:user-field-get><text:line-break />'
|
||||||
|
'Character Count: <text:user-field-get style:data-style-name="N0" '
|
||||||
|
'text:name="ManuscriptAllChars">0</text:user-field-get><text:line-break />'
|
||||||
|
'Chicken Count: <text:user-field-get style:data-style-name="N0" '
|
||||||
|
'text:name="ManuscriptAllChickens">0</text:user-field-get></text:p>'
|
||||||
|
'</office:text></office:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testFmtToOdt_DialogueFormatting(mockGUI):
|
def testFmtToOdt_DialogueFormatting(mockGUI):
|
||||||
"""Test formatting of dialogue."""
|
"""Test formatting of dialogue."""
|
||||||
@@ -770,7 +791,7 @@ def testFmtToOdt_ConvertDirect(mockGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
|
def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths, ipsumText):
|
||||||
"""Test the document save functions."""
|
"""Test the document save functions."""
|
||||||
project = NWProject()
|
project = NWProject()
|
||||||
project.data.setAuthor("Jane Smith")
|
project.data.setAuthor("Jane Smith")
|
||||||
@@ -796,14 +817,20 @@ def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
|
|||||||
assert odt._mDocRight == "1.500cm"
|
assert odt._mDocRight == "1.500cm"
|
||||||
|
|
||||||
odt._text = (
|
odt._text = (
|
||||||
|
"#! My Novel\n\n"
|
||||||
|
"**Word Count: [field:allWords]**\n"
|
||||||
|
"[field:paragraphCount] paragrphs\n"
|
||||||
|
"Web: http://example.com\n\n"
|
||||||
"## Chapter One\n\n"
|
"## Chapter One\n\n"
|
||||||
"Text\n\n"
|
f"{ipsumText[0]}\n\n"
|
||||||
"## Chapter Two\n\n"
|
"## Chapter Two\n\n"
|
||||||
"Text\n\n"
|
f"{ipsumText[1]}[footnote:abc]\n\n"
|
||||||
|
"%Footnote.abc: Lorem ipsum\n\n"
|
||||||
)
|
)
|
||||||
odt.tokenizeText()
|
odt.tokenizeText()
|
||||||
odt.initDocument()
|
odt.initDocument()
|
||||||
odt.doConvert()
|
odt.doConvert()
|
||||||
|
odt.countStats()
|
||||||
odt.closeDocument()
|
odt.closeDocument()
|
||||||
|
|
||||||
flatFile = fncPath / "document.fodt"
|
flatFile = fncPath / "document.fodt"
|
||||||
@@ -818,7 +845,7 @@ def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths):
|
def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths, ipsumText):
|
||||||
"""Test the document save functions."""
|
"""Test the document save functions."""
|
||||||
project = NWProject()
|
project = NWProject()
|
||||||
project.data.setAuthor("Jane Smith")
|
project.data.setAuthor("Jane Smith")
|
||||||
@@ -833,14 +860,20 @@ def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths):
|
|||||||
odt.setHeaderFormat(f"{nwHeadFmt.DOC_PROJECT} - {nwHeadFmt.DOC_AUTHOR}", 0)
|
odt.setHeaderFormat(f"{nwHeadFmt.DOC_PROJECT} - {nwHeadFmt.DOC_AUTHOR}", 0)
|
||||||
|
|
||||||
odt._text = (
|
odt._text = (
|
||||||
|
"#! My Novel\n\n"
|
||||||
|
"**Word Count: [field:allWords]**\n"
|
||||||
|
"[field:paragraphCount] paragrphs\n"
|
||||||
|
"Web: http://example.com\n\n"
|
||||||
"## Chapter One\n\n"
|
"## Chapter One\n\n"
|
||||||
"Text\n\n"
|
f"{ipsumText[0]}\n\n"
|
||||||
"## Chapter Two\n\n"
|
"## Chapter Two\n\n"
|
||||||
"Text\n\n"
|
f"{ipsumText[1]}[footnote:abc]\n\n"
|
||||||
|
"%Footnote.abc: Lorem ipsum\n\n"
|
||||||
)
|
)
|
||||||
odt.tokenizeText()
|
odt.tokenizeText()
|
||||||
odt.initDocument()
|
odt.initDocument()
|
||||||
odt.doConvert()
|
odt.doConvert()
|
||||||
|
odt.countStats()
|
||||||
odt.closeDocument()
|
odt.closeDocument()
|
||||||
|
|
||||||
fullFile = fncPath / "document.odt"
|
fullFile = fncPath / "document.odt"
|
||||||
|
|||||||
@@ -2184,27 +2184,3 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
|||||||
assert docEditor.textCursor().selectedText() == ""
|
assert docEditor.textCursor().selectedText() == ""
|
||||||
|
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
|
||||||
def testGuiEditor_StaticMethods():
|
|
||||||
"""Test the document editor's static methods."""
|
|
||||||
# Check the method that decides if it is allowed to insert a space
|
|
||||||
# before a colon using the French, Spanish, etc language feature
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("", "") is True
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("", ":") is True
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("some text", ":") is True
|
|
||||||
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("@:", ":") is False
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("@>", ">") is True
|
|
||||||
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("%", ":") is True
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("%:", ":") is True
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("%synopsis:", ":") is False
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("%Synopsis:", ":") is False
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("% synopsis:", ":") is False
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("% Synopsis:", ":") is False
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("% synopsis:", ":") is False
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("% Synopsis:", ":") is False
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("%synopsis :", ":") is True
|
|
||||||
assert GuiDocEditor._allowSpaceBeforeColon("%Synopsis :", ":") is True
|
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
|||||||
# Special Formatting
|
# Special Formatting
|
||||||
# ==================
|
# ==================
|
||||||
|
|
||||||
# Insert spaces before colon, but ignore tags and synopsis
|
# Insert spaces before colon, but ignore tags
|
||||||
docEditor._typPadBefore = ":"
|
docEditor._typPadBefore = ":"
|
||||||
|
|
||||||
for c in "@object: NoSpaceAdded":
|
for c in "@object: NoSpaceAdded":
|
||||||
@@ -482,7 +482,12 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
|||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "% synopsis: No space before this colon.":
|
for c in "% synopsis: Space before this is OK.":
|
||||||
|
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||||
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
|
for c in "%Footnote.abc: A simple footnote.":
|
||||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -110,6 +111,15 @@ def cmpFiles(
|
|||||||
return not diffFound
|
return not diffFound
|
||||||
|
|
||||||
|
|
||||||
|
def xmlToText(xElem):
|
||||||
|
"""Get the text content of an XML element."""
|
||||||
|
text = ET.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
||||||
|
bits = text.partition(">")
|
||||||
|
node = bits[0].partition(" ")
|
||||||
|
rest = " ".join(x for x in node[2].split() if not x.startswith("xmlns")).replace("/", " /")
|
||||||
|
return f"{node[0]}{rest}{bits[1]}{bits[2]}"
|
||||||
|
|
||||||
|
|
||||||
def readFile(fileName: str | Path):
|
def readFile(fileName: str | Path):
|
||||||
"""Returns the content of a file as a string."""
|
"""Returns the content of a file as a string."""
|
||||||
with open(fileName, mode="r", encoding="utf-8") as inFile:
|
with open(fileName, mode="r", encoding="utf-8") as inFile:
|
||||||
|
|||||||
Reference in New Issue
Block a user