From d5d81b82ad3b64761daaf782f5a17217cb28bd49 Mon Sep 17 00:00:00 2001 From: Eliot Kimber Date: Sun, 26 Apr 2020 11:38:48 -0500 Subject: [PATCH 1/5] Release 1.0.4: Start of development for 1.0.4 --- pom.xml | 2 +- version.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 23df95c..acb03d4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.wordinator wordinator jar - 1.0.3 + 1.0.4 wordinator.org https://wordinator.org diff --git a/version.properties b/version.properties index f7b14fb..89429d7 100644 --- a/version.properties +++ b/version.properties @@ -1 +1 @@ -version=1.0.3 +version=1.0.4 From 3d41fc9d36c12e62073158aef2181f1644d0d8bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2020 18:22:29 +0000 Subject: [PATCH 2/5] Bump log4j-core from 2.13.1 to 2.13.2 Bumps log4j-core from 2.13.1 to 2.13.2. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 23df95c..e167617 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ arbitrary XML, JSON, etc. org.apache.logging.log4j log4j-core - 2.13.1 + 2.13.2 From 2e55c0a9d71ce18f6f49c8da4268649965a24314 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 06:39:02 +0000 Subject: [PATCH 3/5] Bump junit from 4.13 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e167617..a7541f9 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ arbitrary XML, JSON, etc. junit junit - 4.13 + 4.13.1 test From 89e69590d7d2fb12c9d68339a96bfbb7962e1ea9 Mon Sep 17 00:00:00 2001 From: Eliot Kimber Date: Wed, 20 Jan 2021 17:11:05 -0600 Subject: [PATCH 4/5] Feature/issue 29 explicit footnote callouts (#33) Implements #29: Added new callout and reference-callout attributes to fn element, when present generate literal callouts for footnotes. --- README.md | 6 +- pom.xml | 4 +- .../footnotes/footnotes-custom-callouts.docx | Bin 0 -> 15873 bytes src/main/doctypes/simplewpml/simplewpml.rng | 22 ++++++- .../xml2docx/generator/DocxConstants.java | 21 +++++-- .../xml2docx/generator/DocxGenerator.java | 57 +++++++++++++++++- .../xml2docx/TestDocxGenerator.java | 52 ++++++++++++++++ .../simplewp/simplewpml-issue-29.swpx | 48 +++++++++++++++ 8 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 sample-data/footnotes/footnotes-custom-callouts.docx create mode 100644 src/test/resources/simplewp/simplewpml-issue-29.swpx diff --git a/README.md b/README.md index d14987f..7e2cb83 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # The Wordinator -Version 1.0.2 +Version 1.0.4 Generate high-quality Microsoft Word DOCX files using a simplified XML format (simple word processing XML). @@ -26,6 +26,10 @@ If you need to go from Word documents back to XML, you may find the DITA for Pub ## Release Notes +* 1.0.4 + + * Issue 29: Support literal callouts and reference callouts for footnotes. Added new attributes to fn element for specifying the callout and, optionally, reference callout text. + * 1.0.3 * Issue 11: Added support for catalog resolution with Saxon. Added new command-line option -k/-catalog that specifies a list of catalog files as for Saxon's -catalog option. diff --git a/pom.xml b/pom.xml index acb03d4..126e4db 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ arbitrary XML, JSON, etc. junit junit - 4.13 + 4.13.1 test @@ -83,7 +83,7 @@ arbitrary XML, JSON, etc. net.sf.saxon Saxon-HE - 10.0 + 10.3 diff --git a/sample-data/footnotes/footnotes-custom-callouts.docx b/sample-data/footnotes/footnotes-custom-callouts.docx new file mode 100644 index 0000000000000000000000000000000000000000..928e161960a6d094037c0a7125107324a90d9b9c GIT binary patch literal 15873 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 P1W^1qY?jLY>)ZbW{TYC< literal 0 HcmV?d00001 diff --git a/src/main/doctypes/simplewpml/simplewpml.rng b/src/main/doctypes/simplewpml/simplewpml.rng index cd3e432..0b6a857 100644 --- a/src/main/doctypes/simplewpml/simplewpml.rng +++ b/src/main/doctypes/simplewpml/simplewpml.rng @@ -855,7 +855,7 @@

A footnote within a paragraph or run. Represents the point of reference of the footnote as well as the content of the footnote itself.

A footnote has one or more paragraphs.

- +

Issue #29: Added @callout attribute.

@@ -881,6 +881,26 @@ + + + +

Specifies a literal callout to use in place of the normal generated callout. + If only @callout is specified, this value is used for both the reference and + the footnote in the footnote area.

+

@since: 1.0.4

+
+
+
+ + + +

Specifies the callout to use for the footnot reference when @callout is also + specified. If not specified, the value of @callout is used for the footnote reference + callout.

+

@since: 1.0.4

+
+
+
diff --git a/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java b/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java index a09f34d..2a22bcf 100644 --- a/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java +++ b/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java @@ -16,10 +16,11 @@ @SuppressWarnings("unused") public final class DocxConstants { + // Namespace names: public static final String OO_WPML_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; public static final String SIMPLE_WP_NS = "urn:ns:wordinator:simplewpml"; - public static final QName QNAME_INSTR_ATT = new QName(OO_WPML_NS, "instr"); + // Attributes: public static final QName QNAME_ALIGN_ATT = new QName("", "align"); public static final QName QNAME_BOLD_ATT = new QName("", "bold"); public static final QName QNAME_BORDER_STYLE_ATT = new QName("", "borderstyle"); @@ -31,6 +32,7 @@ public final class DocxConstants { public static final QName QNAME_BORDER_STYLE_TOP_ATT = new QName("", "borderstyletop"); public static final QName QNAME_BOTTOM_ATT = new QName("", "bottom"); public static final QName QNAME_CALCULATEDWIDTH_ATT = new QName("", "calculatedWidth"); + public static final QName QNAME_CALLOUT_ATT = new QName("", "callout"); public static final QName QNAME_CAPS_ATT = new QName("", "caps"); public static final QName QNAME_CHAPTER_SEPARATOR_ATT = new QName("", "chapter-separator"); public static final QName QNAME_CHAPTER_STYLE_ATT = new QName("", "chapter-style"); @@ -38,6 +40,7 @@ public final class DocxConstants { public static final QName QNAME_COLSEP_ATT = new QName("", "colsep"); public static final QName QNAME_COLSPAN_ATT = new QName("", "colspan"); public static final QName QNAME_COLWIDTH_ATT = new QName("", "colwidth"); + public static final QName QNAME_CUSTOMMARKFOLLOWS_ATT = new QName(OO_WPML_NS, "customMarkFollows"); public static final QName QNAME_DOUBLE_STRIKETHROUGH_ATT = new QName("", "double-strikethrough"); public static final QName QNAME_EMBOSS_ATT = new QName("", "emboss"); public static final QName QNAME_EMPHASIS_MARK_ATT = new QName("", "emphasis-mark"); @@ -59,6 +62,7 @@ public final class DocxConstants { public static final QName QNAME_ID_ATT = new QName("", "id"); public static final QName QNAME_IMPRINT_ATT = new QName("", "imprint"); public static final QName QNAME_INSIDEINDENT_ATT = new QName("", "insideindent"); + public static final QName QNAME_INSTR_ATT = new QName(OO_WPML_NS, "instr"); public static final QName QNAME_ITALIC_ATT = new QName("", "italic"); public static final QName QNAME_LEFT_ATT = new QName("", "left"); public static final QName QNAME_LEFTINDENT_ATT = new QName("", "leftindent"); @@ -69,6 +73,7 @@ public final class DocxConstants { public static final QName QNAME_OUTSIDEINDENT_ATT = new QName("", "outsideindent"); public static final QName QNAME_PAGE_BREAK_BEFORE_ATT = new QName("", "page-break-before"); public static final QName QNAME_POSITION_ATT = new QName("", "position"); + public static final QName QNAME_REFERENCE_CALLOUT_ATT = new QName("", "reference-callout"); public static final QName QNAME_RIGHT_ATT = new QName("", "right"); public static final QName QNAME_RIGHTINDENT_ATT = new QName("", "rightindent"); public static final QName QNAME_ROWSEP_ATT = new QName("", "rowsep"); @@ -91,16 +96,24 @@ public final class DocxConstants { public static final QName QNAME_VERTICAL_ALIGNMENT_ATT = new QName("", "vertical-alignment"); public static final QName QNAME_WIDTH_ATT = new QName("", "width"); public static final QName QNAME_XSLT_FORMAT_ATT = new QName("", "xslt-format"); + + // Elements: public static final QName QNAME_COLS_ELEM = new QName(SIMPLE_WP_NS, "cols"); public static final QName QNAME_COL_ELEM = new QName(SIMPLE_WP_NS, "col"); + public static final QName QNAME_FOOTNOTEREF_ELEM = new QName(OO_WPML_NS, "footnoteRef"); + public static final QName QNAME_FOOTNOTEREFEREMCE_ELEM = new QName(OO_WPML_NS, "footnoteReference"); + public static final QName QNAME_P_ELEM = new QName(SIMPLE_WP_NS, "p"); + public static final QName QNAME_W_P_ELEM = new QName(OO_WPML_NS, "p"); + public static final QName QNAME_R_ELEM = new QName(OO_WPML_NS, "r"); + public static final QName QNAME_ROW_ELEM = new QName(SIMPLE_WP_NS, "row"); + public static final QName QNAME_T_ELEM = new QName(OO_WPML_NS, "t"); // w:t -- text element public static final QName QNAME_THEAD_ELEM = new QName(SIMPLE_WP_NS, "thead"); public static final QName QNAME_TBODY_ELEM = new QName(SIMPLE_WP_NS, "tbody"); public static final QName QNAME_TR_ELEM = new QName(SIMPLE_WP_NS, "tr"); public static final QName QNAME_TD_ELEM = new QName(SIMPLE_WP_NS, "td"); - public static final QName QNAME_P_ELEM = new QName(SIMPLE_WP_NS, "p"); - public static final QName QNAME_ROW_ELEM = new QName(SIMPLE_WP_NS, "row"); public static final QName QNAME_VSPAN_ELEM = new QName(SIMPLE_WP_NS, "vspan"); - public static final String PROPERTY_VALUE_CONTINUOUS = "continuous"; + + public static final String PROPERTY_VALUE_CONTINUOUS = "continuous"; public static final String PROPERTY_PAGEBREAK = "pagebreak"; } diff --git a/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java b/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java index 728a0e9..1fefcdf 100644 --- a/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java +++ b/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java @@ -57,6 +57,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHyperlink; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTMarkupRange; @@ -1137,6 +1138,8 @@ private void makeFootnote(XWPFParagraph para, XmlObject xml) throws DocxGenerati XmlCursor cursor = xml.newCursor(); String type = cursor.getAttributeText(DocxConstants.QNAME_TYPE_ATT); + String callout = cursor.getAttributeText(DocxConstants.QNAME_CALLOUT_ATT); + String referenceCallout = cursor.getAttributeText(DocxConstants.QNAME_REFERENCE_CALLOUT_ATT); XWPFAbstractFootnoteEndnote note = null; if ("endnote".equals(type)) { @@ -1145,7 +1148,7 @@ private void makeFootnote(XWPFParagraph para, XmlObject xml) throws DocxGenerati note = para.getDocument().createFootnote(); } - // NOTE: The paragraph is not created with any initial paragraph. + // NOTE: The footnote is not created with any initial paragraph. if (cursor.toFirstChild()) { do { @@ -1165,7 +1168,52 @@ private void makeFootnote(XWPFParagraph para, XmlObject xml) throws DocxGenerati } while (cursor.toNextSibling()); } - para.addFootnoteReference(note); + para.addFootnoteReference(note); + + // Issue #29: For footnotes with explict callouts, have to replace the markup for generated + // refs with the literal callout from the input XML. + + if (callout != null) { + if (referenceCallout == null) { + referenceCallout = callout; + } + + XmlCursor paraCursor = para.getCTP().newCursor(); + + if (paraCursor.toLastChild()) { + // Should be the run created for the footnote reference. + if (paraCursor.toChild(DocxConstants.QNAME_FOOTNOTEREFEREMCE_ELEM)) { + paraCursor.setAttributeText(DocxConstants.QNAME_CUSTOMMARKFOLLOWS_ATT, "on"); + paraCursor.toParent(); + paraCursor.toEndToken(); + paraCursor.insertElementWithText(DocxConstants.QNAME_T_ELEM, referenceCallout); + } + + } + + // Set literal callout on the footnote itself: + CTFtnEdn ctfNote = note.getCTFtnEdn(); + + XmlCursor noteCursor = ctfNote.newCursor(); + + // Find the first run. This should have a element as it's content. + // Remove that and replace it with a w:t with the callout. + if (noteCursor.toChild(DocxConstants.QNAME_W_P_ELEM)) { + if (noteCursor.toChild(DocxConstants.QNAME_R_ELEM)) { + cursor.push(); + if (noteCursor.toChild(DocxConstants.QNAME_FOOTNOTEREF_ELEM)) { + noteCursor.removeXml(); + } + cursor.pop(); + // Now construct a literal footnote reference callout. + noteCursor.insertElementWithText(DocxConstants.QNAME_T_ELEM, callout); + } + } + + noteCursor.dispose(); + + } + cursor.pop(); } @@ -2165,11 +2213,13 @@ private void setupNumbering(XWPFDocument doc, XWPFDocument templateDoc) throws D try { XWPFNumbering templateNumbering = templateDoc.getNumbering(); XWPFNumbering numbering = doc.createNumbering(); - // There is no method to just get all the abstract and concrete + // In 4.1.2 There is no method to just get all the abstract and concrete // numbers or their IDs so we just iterate until we don't get any more + // Trunk has new methods for this as of 4/26/2020 // Abstract numbers: int i = 1; + XWPFAbstractNum abstractNum = null; // Number IDs appear to always be integers starting at 1 // so we're really just guessing. @@ -2281,3 +2331,4 @@ else if ("jpeg".equals(imgExtension) || } + diff --git a/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java b/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java index ea074b7..08ef0e8 100644 --- a/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java +++ b/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java @@ -257,4 +257,56 @@ public void testCopyNumberingDefinitions() throws Exception { } + @Test + public void testFootnoteGeneration() throws Exception { + ClassLoader classLoader = getClass().getClassLoader(); + File inFile = new File(classLoader.getResource("simplewp/simplewpml-issue-29.swpx").getFile()); + File templateFile = new File(classLoader.getResource(DOTX_TEMPLATE_PATH).getFile()); + File outFile = new File("out/output-issue-29.docx"); + File outDir = outFile.getParentFile(); + System.out.println("Input file: " + inFile.getAbsolutePath()); + System.out.println("Output file: " + outFile.getAbsolutePath()); + if (!outDir.exists()) { + assertTrue("Failed to create directories for output file " + outFile.getAbsolutePath(), outFile.mkdirs()); + } + if (outFile.exists()) { + assertTrue("Failed to delete output file " + outFile.getAbsolutePath(), outFile.delete()); + } + + XWPFDocument templateDoc = new XWPFDocument(new FileInputStream(templateFile)); + + DocxGenerator maker = new DocxGenerator(inFile, outFile, templateDoc); + // Generate the DOCX file: + + try { + XmlObject xml = XmlObject.Factory.parse(inFile); + + maker.generate(xml); + assertTrue("DOCX file does not exist", outFile.exists()); + FileInputStream inStream = new FileInputStream(outFile); + XWPFDocument doc = new XWPFDocument(inStream); + assertNotNull(doc); + Iterator iterator = doc.getParagraphsIterator(); + XWPFParagraph p = iterator.next(); + assertNotNull("Expected a paragraph", p); + assertEquals("Issue #29: Test of Literal Footnote Callouts", p.getText()); + // Normal footnote with generated ref + p = iterator.next(); + assertEquals(" [1: This is a normal footnote. It should have a callout of 1] ", p.getFootnoteText()); + // Custom footnote with literal callout with same value for ref and footnote: + p = iterator.next(); + String fnText = p.getFootnoteText(); + assertEquals(" [2: FN-1This is a custom footnote. It specifies a literal callout of \"FN-1\".] ", fnText); + // Custom footnote with literal callout with different values for ref and footnote: + p = iterator.next(); + fnText = p.getFootnoteText(); + assertEquals(" [3: FN-2This is a custom footnote. It specifies a literal callout of \"FN-2\" and a reference callout of \"Ref-2\".] ", p.getFootnoteText()); + + } catch (Exception e) { + e.printStackTrace(); + fail("Got unexpected " + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + + } + } diff --git a/src/test/resources/simplewp/simplewpml-issue-29.swpx b/src/test/resources/simplewp/simplewpml-issue-29.swpx new file mode 100644 index 0000000..6cd926b --- /dev/null +++ b/src/test/resources/simplewp/simplewpml-issue-29.swpx @@ -0,0 +1,48 @@ + + + + + + + +
+

Odd Header Paragraph 1

+

Odd Header Paragraph 2

+
+
+

Even Header Paragraph 1

+
+
+

+ Odd Footer: + + After page-number-ref +

+
+
+

Even Footer

+
+
+
+ +

+ Issue #29: Test of Literal Footnote Callouts +

+

+ This paragraph has a normal footnote. It should have a generated callout of "1". +

This is a normal footnote. It should have a callout of 1

+

+

+ This paragraph has a custom footnote. It specifies a literal callout of "FN-1". +

This is a custom footnote. It specifies a literal callout of "FN-1".

+

+

+ This paragraph has a custom footnote. It specifies a literal callout of "FN-2" and a reference callout of "Ref-2". +

This is a custom footnote. It specifies a literal callout of "FN-2" and a reference callout of "Ref-2".

+

+ +
From 20c044de84f181d95551d309c221f8064d85f35a Mon Sep 17 00:00:00 2001 From: Eliot Kimber Date: Wed, 20 Jan 2021 18:56:43 -0600 Subject: [PATCH 5/5] Issue #30: Implemented border colors on table cells --- src/main/doctypes/simplewpml/simplewpml.rng | 44 +++++- .../xml2docx/generator/DocxConstants.java | 13 ++ .../xml2docx/generator/DocxGenerator.java | 94 +++++++++++- .../xml2docx/TestDocxGenerator.java | 124 ++++++++++++++++ .../simplewp/simplewpml-issue-30.swpx | 138 ++++++++++++++++++ 5 files changed, 409 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/simplewp/simplewpml-issue-30.swpx diff --git a/src/main/doctypes/simplewpml/simplewpml.rng b/src/main/doctypes/simplewpml/simplewpml.rng index 0b6a857..90e7d6b 100644 --- a/src/main/doctypes/simplewpml/simplewpml.rng +++ b/src/main/doctypes/simplewpml/simplewpml.rng @@ -816,9 +816,7 @@

Set foreground color

For now must be an RGB color value, e.g. "C0C0C0". May add support for color names at some point.

- - [0-9A-Fa-f]{6} - + @@ -1345,6 +1343,46 @@ + + + +

Specifies the border color for the cell's borders.

+
+ +
+
+ + + +

Specifies the bottom border color.

+
+ +
+
+ + + +

Specifies the top border color.

+
+ +
+
+ + + +

Specifies the left border color.

+
+ +
+
+ + + +

Specifies the right border color.

+
+ +
+
diff --git a/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java b/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java index 2a22bcf..2f71efd 100644 --- a/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java +++ b/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java @@ -23,6 +23,11 @@ public final class DocxConstants { // Attributes: public static final QName QNAME_ALIGN_ATT = new QName("", "align"); public static final QName QNAME_BOLD_ATT = new QName("", "bold"); + public static final QName QNAME_BORDER_COLOR_ATT = new QName("", "bordercolor"); + public static final QName QNAME_BORDER_COLOR_TOP_ATT = new QName("", "bordercolortop"); + public static final QName QNAME_BORDER_COLOR_LEFT_ATT = new QName("", "bordercolorleft"); + public static final QName QNAME_BORDER_COLOR_BOTTOM_ATT = new QName("", "bordercolorbottom"); + public static final QName QNAME_BORDER_COLOR_RIGHT_ATT = new QName("", "bordercolorright"); public static final QName QNAME_BORDER_STYLE_ATT = new QName("", "borderstyle"); public static final QName QNAME_BORDER_STYLE_BOTTOM_ATT = new QName("", "borderstylebottom"); public static final QName QNAME_BORDER_STYLE_LEFT_ATT = new QName("", "borderstyleleft"); @@ -37,6 +42,7 @@ public final class DocxConstants { public static final QName QNAME_CHAPTER_SEPARATOR_ATT = new QName("", "chapter-separator"); public static final QName QNAME_CHAPTER_STYLE_ATT = new QName("", "chapter-style"); public static final QName QNAME_CODE_ATT = new QName(SIMPLE_WP_NS, "code"); + public static final QName QNAME_COLOR_ATT = new QName(OO_WPML_NS, "color"); public static final QName QNAME_COLSEP_ATT = new QName("", "colsep"); public static final QName QNAME_COLSPAN_ATT = new QName("", "colspan"); public static final QName QNAME_COLWIDTH_ATT = new QName("", "colwidth"); @@ -115,5 +121,12 @@ public final class DocxConstants { public static final String PROPERTY_VALUE_CONTINUOUS = "continuous"; public static final String PROPERTY_PAGEBREAK = "pagebreak"; + public static final QName QNAME_TCPR_ELEM = new QName(OO_WPML_NS, "tcPr"); + public static final QName QNAME_TCBORDERS_ELEM = new QName(OO_WPML_NS, "tcBorders"); + public static final QName QNAME_TOP_ELEM = new QName(OO_WPML_NS, "top"); + public static final QName QNAME_VAL_ATT = new QName(OO_WPML_NS, "val"); + public static final QName QNAME_LEFT_ELEM = new QName(OO_WPML_NS, "left"); + public static final QName QNAME_RIGHT_ELEM = new QName(OO_WPML_NS, "right"); + public static final QName QNAME_BOTTOM_ELEM = new QName(OO_WPML_NS, "bottom"); } diff --git a/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java b/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java index 1fefcdf..9e1601b 100644 --- a/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java +++ b/src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java @@ -113,6 +113,12 @@ protected class TableBorderStyles { XWPFBorderType rowSepBorder = null; XWPFBorderType colSepBorder = null; + String defaultColor = null; + String topColor = null; + String leftColor = null; + String bottomColor = null; + String rightColor = null; + public TableBorderStyles( XWPFBorderType defaultBorderType, XWPFBorderType topBorder, @@ -134,6 +140,8 @@ public TableBorderStyles(TableBorderStyles parentBorderStyles) { rightBorder = parentBorderStyles.getRightBorder(); rowSepBorder = parentBorderStyles.getRowSepBorder(); colSepBorder = parentBorderStyles.getColSepBorder(); + + // Get default border colors from parent? } /** @@ -151,6 +159,14 @@ public TableBorderStyles(XmlObject borderStyleSpecifier) { String styleLeftValue= null; String styleRightValue= null; + String colorValue = null; + String colorBottomValue= null; + String colorTopValue= null; + String colorLeftValue= null; + String colorRightValue= null; + + // Issue 30: Also get the border color values. + if ("table".equals(tagname)) { styleValue = cursor.getAttributeText(DocxConstants.QNAME_FRAMESTYLE_ATT); styleBottomValue= cursor.getAttributeText(DocxConstants.QNAME_FRAMESTYLE_BOTTOM_ATT); @@ -163,6 +179,12 @@ public TableBorderStyles(XmlObject borderStyleSpecifier) { styleTopValue= cursor.getAttributeText(DocxConstants.QNAME_BORDER_STYLE_TOP_ATT); styleLeftValue= cursor.getAttributeText(DocxConstants.QNAME_BORDER_STYLE_LEFT_ATT); styleRightValue= cursor.getAttributeText(DocxConstants.QNAME_BORDER_STYLE_RIGHT_ATT); + + colorValue = cursor.getAttributeText(DocxConstants.QNAME_BORDER_COLOR_ATT); + colorBottomValue = cursor.getAttributeText(DocxConstants.QNAME_BORDER_COLOR_BOTTOM_ATT); + colorTopValue = cursor.getAttributeText(DocxConstants.QNAME_BORDER_COLOR_TOP_ATT); + colorLeftValue = cursor.getAttributeText(DocxConstants.QNAME_BORDER_COLOR_LEFT_ATT); + colorRightValue = cursor.getAttributeText(DocxConstants.QNAME_BORDER_COLOR_RIGHT_ATT); } if (styleValue != null) { @@ -181,6 +203,64 @@ public TableBorderStyles(XmlObject borderStyleSpecifier) { if (styleRightValue != null) { setRightBorder(xwpfBorderType(styleRightValue)); } + + if (colorValue != null) { + setDefaultBorderColor(colorValue); + } + + if (colorBottomValue != null) { + setBottomColor(colorBottomValue); + } + if (colorTopValue != null) { + setTopColor(colorTopValue); + } + if (colorLeftValue != null) { + setLeftColor(colorLeftValue); + } + if (colorRightValue != null) { + setRightColor(colorRightValue); + } + } + + public void setDefaultBorderColor(String colorValue) { + this.defaultColor = colorValue; + if (this.getBottomColor() == null) this.setBottomColor(colorValue); + if (this.getTopColor() == null) this.setTopColor(colorValue); + if (this.getLeftColor() == null) this.setLeftColor(colorValue); + if (this.getRightColor() == null) this.setRightColor(colorValue); + + } + + public String getBottomColor() { + return this.bottomColor; + } + + public String getTopColor() { + return this.topColor; + } + + public String getLeftColor() { + return this.leftColor; + } + + public String getRightColor() { + return this.rightColor; + } + + public void setBottomColor(String colorValue) { + this.bottomColor = colorValue; + } + + public void setTopColor(String colorValue) { + this.topColor = colorValue; + } + + public void setLeftColor(String colorValue) { + this.leftColor = colorValue; + } + + public void setRightColor(String colorValue) { + this.rightColor = colorValue; } public XWPFBorderType getDefaultBorderType() { @@ -304,7 +384,7 @@ public DocxGenerator(File inFile, File outFile, XWPFDocument templateDoc) throws this.templateDoc = templateDoc; } - /* + /* * Generate the DOCX file from the input Simple WP ML document. * @param xml The XmlObject that holds the Simple WP XML content */ @@ -2173,18 +2253,30 @@ private void setCellBorders(XmlCursor cursor, CTTcPr ctTcPr) { } else { log.warn("setCellBorders(): Failed to get STBorder.Enum value for XWPFBorderStyle \"" + borderStyles.getBottomBorder().name() + "\""); } + if (borderStyles.getBottomColor() != null) { + bottom.setColor(borderStyles.getBottomColor()); + } } if (borderStyles.getTopBorder() != null) { CTBorder top = borders.addNewTop(); top.setVal(borderStyles.getTopBorderEnum()); + if (borderStyles.getTopColor() != null) { + top.setColor(borderStyles.getTopColor()); + } } if (borderStyles.getLeftBorder() != null) { CTBorder left = borders.addNewLeft(); left.setVal(borderStyles.getLeftBorderEnum()); + if (borderStyles.getLeftColor() != null) { + left.setColor(borderStyles.getLeftColor()); + } } if (borderStyles.getRightBorder() != null) { CTBorder right = borders.addNewRight(); right.setVal(borderStyles.getRightBorderEnum()); + if (borderStyles.getRightColor() != null) { + right.setColor(borderStyles.getRightColor()); + } } } } diff --git a/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java b/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java index 08ef0e8..0777c4d 100644 --- a/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java +++ b/src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java @@ -7,6 +7,8 @@ import java.util.List; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; +import org.apache.poi.xwpf.usermodel.BodyElementType; +import org.apache.poi.xwpf.usermodel.IBody; import org.apache.poi.xwpf.usermodel.IBodyElement; import org.apache.poi.xwpf.usermodel.XWPFAbstractNum; import org.apache.poi.xwpf.usermodel.XWPFDocument; @@ -17,9 +19,15 @@ import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFPicture; import org.apache.poi.xwpf.usermodel.XWPFRun; +import org.apache.poi.xwpf.usermodel.XWPFTable; +import org.apache.poi.xwpf.usermodel.XWPFTableCell; +import org.apache.poi.xwpf.usermodel.XWPFTableRow; +import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.junit.Test; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; +import org.wordinator.xml2docx.generator.DocxConstants; import org.wordinator.xml2docx.generator.DocxGenerator; import junit.framework.TestCase; @@ -309,4 +317,120 @@ public void testFootnoteGeneration() throws Exception { } + @Test + public void testTableGeneration() throws Exception { + ClassLoader classLoader = getClass().getClassLoader(); + File inFile = new File(classLoader.getResource("simplewp/simplewpml-issue-30.swpx").getFile()); + File templateFile = new File(classLoader.getResource(DOTX_TEMPLATE_PATH).getFile()); + File outFile = new File("out/output-issue-30.docx"); + File outDir = outFile.getParentFile(); + System.out.println("Input file: " + inFile.getAbsolutePath()); + System.out.println("Output file: " + outFile.getAbsolutePath()); + if (!outDir.exists()) { + assertTrue("Failed to create directories for output file " + outFile.getAbsolutePath(), outFile.mkdirs()); + } + if (outFile.exists()) { + assertTrue("Failed to delete output file " + outFile.getAbsolutePath(), outFile.delete()); + } + + XWPFDocument templateDoc = new XWPFDocument(new FileInputStream(templateFile)); + + DocxGenerator maker = new DocxGenerator(inFile, outFile, templateDoc); + try { + XmlObject xml = XmlObject.Factory.parse(inFile); + + maker.generate(xml); + assertTrue("DOCX file does not exist", outFile.exists()); + FileInputStream inStream = new FileInputStream(outFile); + XWPFDocument doc = new XWPFDocument(inStream); + assertNotNull(doc); + Iterator iterator = doc.getTablesIterator(); + + XWPFTable table; + table = iterator.next(); + assertNotNull("Did not find any tables", table); + + // Look for table details here. + + // System.out.println("Table rows:"); + /* + + + + + + + + + + + */ + // int n = 0; + // First table should have all single borders + for (XWPFTableRow row : table.getRows()) { + // System.out.println("Row " + ++n); + for (XWPFTableCell cell : row.getTableCells()) { + XmlCursor cursor = cell.getCTTc().newCursor(); + assertTrue("No tcPr element", cursor.toChild(DocxConstants.QNAME_TCPR_ELEM)); + assertTrue("No tcBorders element", cursor.toChild(DocxConstants.QNAME_TCBORDERS_ELEM)); + assertTrue("No top element", cursor.toChild(DocxConstants.QNAME_TOP_ELEM)); + assertEquals("single", cursor.getAttributeText(DocxConstants.QNAME_VAL_ATT)); + assertNull("Did not expect a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toNextSibling(); // Left + assertEquals("single", cursor.getAttributeText(DocxConstants.QNAME_VAL_ATT)); + assertNull("Did not expect a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toNextSibling(); // Bottom + assertEquals("single", cursor.getAttributeText(DocxConstants.QNAME_VAL_ATT)); + assertNull("Did not expect a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toNextSibling(); // Right + assertEquals("single", cursor.getAttributeText(DocxConstants.QNAME_VAL_ATT)); + assertNull("Did not expect a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + } + } + + assertTrue("Expected a second table", iterator.hasNext()); + table = iterator.next(); + + for (XWPFTableRow row : table.getRows()) { + // System.out.println("Row " + ++n); + for (XWPFTableCell cell : row.getTableCells()) { + XmlCursor cursor = cell.getCTTc().newCursor(); + assertTrue("No tcPr element", cursor.toChild(DocxConstants.QNAME_TCPR_ELEM)); + assertTrue("No tcBorders element", cursor.toChild(DocxConstants.QNAME_TCBORDERS_ELEM)); + assertTrue("No top element", cursor.toChild(DocxConstants.QNAME_TOP_ELEM)); + assertNotNull("Expected a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toNextSibling(); // Left + assertNotNull("Expected a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toNextSibling(); // Bottom + assertNotNull("Expected a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toNextSibling(); // Right + assertNotNull("Expected a color attribute", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + } + } + + assertTrue("Expected a third table", iterator.hasNext()); + table = iterator.next(); + XWPFTableRow row = table.getRow(0); // Header row. + XWPFTableCell cell = row.getCell(1); // Center cell + XmlCursor cursor = cell.getCTTc().newCursor(); + assertTrue(cursor.toChild(DocxConstants.QNAME_TCPR_ELEM)); + assertTrue(cursor.toChild(DocxConstants.QNAME_TCBORDERS_ELEM)); + assertTrue(cursor.toChild(DocxConstants.QNAME_LEFT_ELEM)); + assertEquals("00FF00", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toParent(); + assertTrue(cursor.toChild(DocxConstants.QNAME_RIGHT_ELEM)); + assertEquals("0000FF", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toParent(); + assertTrue(cursor.toChild(DocxConstants.QNAME_TOP_ELEM)); + assertEquals("F0F0F0", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + cursor.toParent(); + assertTrue(cursor.toChild(DocxConstants.QNAME_BOTTOM_ELEM)); + assertEquals("0F0F0F", cursor.getAttributeText(DocxConstants.QNAME_COLOR_ATT)); + + } catch (Exception e) { + e.printStackTrace(); + fail("Got unexpected " + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + } + } diff --git a/src/test/resources/simplewp/simplewpml-issue-30.swpx b/src/test/resources/simplewp/simplewpml-issue-30.swpx new file mode 100644 index 0000000..df8584e --- /dev/null +++ b/src/test/resources/simplewp/simplewpml-issue-30.swpx @@ -0,0 +1,138 @@ + + + + + + + +
+

Odd Header Paragraph 1

+

Odd Header Paragraph 2

+
+
+

Even Header Paragraph 1

+
+
+

+ Odd Footer: + + After page-number-ref +

+
+
+

Even Footer

+
+
+
+ +

+ Issue #30: Table Border Color Tests +

+

+ Table with no border colors. Should have all borders. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Column 1

Column 2

Column 3

R1C1

R1C2

R1C3

R2C1

R2C2

R2C3

R3C1

R3C2

R3C3

+

+ Table with border color. Should have all red borders. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Column 1

Column 2

Column 3

R1C1

R1C2

R1C3

R2C1

R2C2

R2C3

R3C1

R3C2

R3C3

+

+ Table with border color. Each row has different colors. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Column 1 no border color

Left: Green, top: F0F0F0, right: Blue, Bottom: 0F0F0F

Column 3 no border color

R1C1

R1C2

R1C3

R2C1 no border color

R2C2 Left: Green, top: F0F0F0, right: Blue, Bottom: 0F0F0F

R2C3 no border color

R3C1

R3C2

R3C3

+ +