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 e167617..751d026 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 @@ -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 0000000..928e161 Binary files /dev/null and b/sample-data/footnotes/footnotes-custom-callouts.docx differ diff --git a/src/main/doctypes/simplewpml/simplewpml.rng b/src/main/doctypes/simplewpml/simplewpml.rng index cd3e432..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} - + @@ -855,7 +853,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 +879,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

+
+
+
@@ -1325,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 a09f34d..2f71efd 100644 --- a/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java +++ b/src/main/java/org/wordinator/xml2docx/generator/DocxConstants.java @@ -16,12 +16,18 @@ @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_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"); @@ -31,13 +37,16 @@ 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"); 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"); + 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 +68,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 +79,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 +102,31 @@ 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"; + 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 728a0e9..9e1601b 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; @@ -112,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, @@ -133,6 +140,8 @@ public TableBorderStyles(TableBorderStyles parentBorderStyles) { rightBorder = parentBorderStyles.getRightBorder(); rowSepBorder = parentBorderStyles.getRowSepBorder(); colSepBorder = parentBorderStyles.getColSepBorder(); + + // Get default border colors from parent? } /** @@ -150,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); @@ -162,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) { @@ -180,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() { @@ -303,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 */ @@ -1137,6 +1218,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 +1228,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 +1248,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(); } @@ -2125,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()); + } } } } @@ -2165,11 +2305,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 +2423,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..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; @@ -257,4 +265,172 @@ 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()); + } + + } + + @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-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".

+

+ +
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

+ +
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