From fa31a65d143141906b05c98392b954284698cb57 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sat, 18 Jul 2009 09:09:59 +0000 Subject: [PATCH] Support for extraction of footnotes from docx files, see Bugzilla 45556 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@795328 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 4 +- .../poi/xssf/usermodel/XSSFRelation.java | 18 + .../xwpf/model/XWPFHyperlinkDecorator.java | 2 +- .../poi/xwpf/usermodel/XWPFDocument.java | 43 ++- .../poi/xwpf/usermodel/XWPFFootnote.java | 43 +++ .../poi/xwpf/usermodel/XWPFHeaderFooter.java | 3 +- .../poi/xwpf/usermodel/XWPFParagraph.java | 340 +++++++++-------- .../poi/xwpf/usermodel/XWPFRelation.java | 24 ++ .../apache/poi/xwpf/usermodel/XWPFTable.java | 8 +- .../xwpf/extractor/TestXWPFWordExtractor.java | 355 ++++++++---------- .../poi/xwpf/usermodel/TestXWPFTable.java | 12 +- .../org/apache/poi/hwpf/data/footnotes.docx | Bin 0 -> 12823 bytes .../apache/poi/hwpf/data/form_footnotes.docx | Bin 0 -> 27824 bytes .../apache/poi/hwpf/data/table_footnotes.docx | Bin 0 -> 13177 bytes 14 files changed, 471 insertions(+), 381 deletions(-) create mode 100755 src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hwpf/data/footnotes.docx create mode 100755 src/scratchpad/testcases/org/apache/poi/hwpf/data/form_footnotes.docx create mode 100755 src/scratchpad/testcases/org/apache/poi/hwpf/data/table_footnotes.docx diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 415f5390e..7e764b2c6 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,6 +33,8 @@ + 45556 - Fixed ExtractorFactory to support .xltx and .dotx files + 45556 - Support for extraction of footnotes from docx files 47520 - Initial support for custom XML mappings in XSSF 47460 - Fixed NPE when retrieving core properties from a newly created workbook 47498 - Fixed HyperlinkRecord to properly handle URL monikers @@ -41,7 +43,7 @@ 47448 - Allow HSSFEventFactory to handle non-zero padding at the end of the workbook stream 47456 - Support for getting OLE object data in PowerPointExtractor 47411 - Explicitly set the 1900 date system when creating XSSF workbooks - 47400 - Support fo text extraction of footnotes, endnotes and comments in HWPF + 47400 - Support for text extraction of footnotes, endnotes and comments in HWPF 47415 - Fixed PageSettingsBlock to allow multiple PLS records 47412 - Fixed concurrency issue with EscherProperties.initProps() 47143 - Fixed OOM in HSSFWorkbook#getAllPictures when reading .xls files containing metafiles diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java index c3b97c6c7..702f549ba 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java @@ -66,6 +66,24 @@ public final class XSSFRelation extends POIXMLRelation { "/xl/workbook.xml", null ); + public static final XSSFRelation TEMPLATE_WORKBOOK = new XSSFRelation( + "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "/xl/workbook.xml", + null + ); + public static final XSSFRelation MACRO_TEMPLATE_WORKBOOK = new XSSFRelation( + "application/vnd.ms-excel.template.macroEnabled.main+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "/xl/workbook.xml", + null + ); + public static final XSSFRelation MACRO_ADDIN_WORKBOOK = new XSSFRelation( + "application/vnd.ms-excel.addin.macroEnabled.main+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "/xl/workbook.xml", + null + ); public static final XSSFRelation WORKSHEET = new XSSFRelation( "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", diff --git a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHyperlinkDecorator.java b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHyperlinkDecorator.java index 4a246063c..4be1e302b 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHyperlinkDecorator.java +++ b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHyperlinkDecorator.java @@ -19,7 +19,7 @@ package org.apache.poi.xwpf.model; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHyperlink; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText; -import org.apache.poi.xwpf.usermodel.XWPFParagraph;; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; /** * Decorator class for XWPFParagraph allowing to add hyperlinks diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index 63ba925f8..2e86e79c7 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -30,15 +30,7 @@ import org.apache.xmlbeans.XmlOptions; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.*; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CommentsDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.DocumentDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; import javax.xml.namespace.QName; @@ -60,6 +52,7 @@ public class XWPFDocument extends POIXMLDocument { protected List hyperlinks; protected List paragraphs; protected List tables; + protected Map footnotes; /** Handles the joy of different headers/footers for different pages */ private XWPFHeaderFooterPolicy headerFooterPolicy; @@ -87,6 +80,7 @@ public class XWPFDocument extends POIXMLDocument { comments = new ArrayList(); paragraphs = new ArrayList(); tables= new ArrayList(); + footnotes = new HashMap(); try { DocumentDocument doc = DocumentDocument.Factory.parse(getPackagePart().getInputStream()); @@ -94,6 +88,8 @@ public class XWPFDocument extends POIXMLDocument { CTBody body = ctDocument.getBody(); + initFootnotes(); + // filling paragraph list for (CTP p : body.getPArray()) { paragraphs.add(new XWPFParagraph(p, this)); @@ -101,7 +97,7 @@ public class XWPFDocument extends POIXMLDocument { // Get any tables for(CTTbl table : body.getTblArray()) { - tables.add(new XWPFTable(table)); + tables.add(new XWPFTable(this, table)); } // Sort out headers and footers @@ -118,7 +114,6 @@ public class XWPFDocument extends POIXMLDocument { } initHyperlinks(); - } catch (XmlException e) { throw new POIXMLException(e); } @@ -139,6 +134,19 @@ public class XWPFDocument extends POIXMLDocument { } } + private void initFootnotes() throws XmlException, IOException { + for(POIXMLDocumentPart p : getRelations()){ + String relation = p.getPackageRelationship().getRelationshipType(); + if(relation.equals(XWPFRelation.FOOTNOTE.getRelation())){ + FootnotesDocument footnotesDocument = FootnotesDocument.Factory.parse(p.getPackagePart().getInputStream()); + + for(CTFtnEdn ctFtnEdn : footnotesDocument.getFootnotes().getFootnoteArray()) { + footnotes.put(ctFtnEdn.getId().intValue(), new XWPFFootnote(this, ctFtnEdn)); + } + } + } + } + /** * Create a new SpreadsheetML package and setup the default minimal content */ @@ -205,6 +213,15 @@ public class XWPFDocument extends POIXMLDocument { return null; } + + public XWPFFootnote getFootnoteByID(int id) { + return footnotes.get(id); + } + + public Collection getFootnotes() { + return footnotes == null ? new ArrayList() : footnotes.values(); + } + public XWPFHyperlink[] getHyperlinks() { return hyperlinks.toArray( new XWPFHyperlink[hyperlinks.size()] @@ -323,7 +340,7 @@ public class XWPFDocument extends POIXMLDocument { * @return a new table */ public XWPFTable createTable(){ - return new XWPFTable(ctDocument.getBody().addNewTbl()); + return new XWPFTable(this, ctDocument.getBody().addNewTbl()); } /** @@ -333,7 +350,7 @@ public class XWPFDocument extends POIXMLDocument { * @return table */ public XWPFTable createTable(int rows, int cols) { - return new XWPFTable(ctDocument.getBody().addNewTbl(), rows, cols); + return new XWPFTable(this, ctDocument.getBody().addNewTbl(), rows, cols); } } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java new file mode 100755 index 000000000..8ee834d48 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java @@ -0,0 +1,43 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.xwpf.usermodel; + +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +public class XWPFFootnote implements Iterable { + private List paragraphs = new ArrayList(); + + public XWPFFootnote(XWPFDocument document, CTFtnEdn body) { + for (CTP p : body.getPArray()) { + paragraphs.add(new XWPFParagraph(p, document)); + } + } + + public List getParagraphs() { + return paragraphs; + } + + public Iterator iterator(){ + return paragraphs.iterator(); + } + +} diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java index 22ca339ab..3c84bf228 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java @@ -65,7 +65,8 @@ public abstract class XWPFHeaderFooter { new XWPFTable[headerFooter.getTblArray().length]; for(int i=0; i rs = new ArrayList(); - CTR[] tmp; + if (!isEmpty()) { + // All the runs to loop over + // TODO - replace this with some sort of XPath expression + // to directly find all the CTRs, in the right order + ArrayList rs = new ArrayList(); + CTR[] tmp; - // Get the main text runs - tmp = paragraph.getRArray(); - for (int i = 0; i < tmp.length; i++) { - rs.add(tmp[i]); - } + // Get the main text runs + tmp = paragraph.getRArray(); + for (int i = 0; i < tmp.length; i++) { + rs.add(tmp[i]); + } - // Not sure quite what these are, but they hold - // more text runs - CTSdtRun[] sdts = paragraph.getSdtArray(); - for (int i = 0; i < sdts.length; i++) { - CTSdtContentRun run = sdts[i].getSdtContent(); - tmp = run.getRArray(); - for (int j = 0; j < tmp.length; j++) { - rs.add(tmp[j]); - } - } + // Not sure quite what these are, but they hold + // more text runs + CTSdtRun[] sdts = paragraph.getSdtArray(); + for (int i = 0; i < sdts.length; i++) { + CTSdtContentRun run = sdts[i].getSdtContent(); + tmp = run.getRArray(); + for (int j = 0; j < tmp.length; j++) { + rs.add(tmp[j]); + } + } - // Get text of the paragraph - for (int j = 0; j < rs.size(); j++) { - // Grab the text and tabs of the paragraph - // Do so in a way that preserves the ordering - XmlCursor c = rs.get(j).newCursor(); - c.selectPath("./*"); - while (c.toNextSelection()) { - XmlObject o = c.getObject(); - if (o instanceof CTText) { - text.append(((CTText) o).getStringValue()); - } - if (o instanceof CTPTab) { - text.append("\t"); - } - } + // Get text of the paragraph + for (int j = 0; j < rs.size(); j++) { + // Grab the text and tabs of the paragraph + // Do so in a way that preserves the ordering + XmlCursor c = rs.get(j).newCursor(); + c.selectPath("./*"); + while (c.toNextSelection()) { + XmlObject o = c.getObject(); + if (o instanceof CTText) { + text.append(((CTText) o).getStringValue()); + } + if (o instanceof CTPTab) { + text.append("\t"); + } + //got a reference to a footnote + if (o instanceof CTFtnEdnRef) { + CTFtnEdnRef ftn = (CTFtnEdnRef) o; + footnoteText.append("[").append(ftn.getId()).append(": "); + XWPFFootnote footnote = document.getFootnoteByID(ftn.getId().intValue()); - // Loop over pictures inside our - // paragraph, looking for text in them - CTPicture[] picts = rs.get(j).getPictArray(); - for (int k = 0; k < picts.length; k++) { - XmlObject[] t = picts[k] - .selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//w:t"); - for (int m = 0; m < t.length; m++) { - NodeList kids = t[m].getDomNode().getChildNodes(); - for (int n = 0; n < kids.getLength(); n++) { - if (kids.item(n) instanceof Text) { - pictureText.append("\n"); - pictureText.append(kids.item(n).getNodeValue()); - } - } - } - } - } - } + boolean first = true; + for (XWPFParagraph p : footnote.getParagraphs()) { + if (!first) { + footnoteText.append("\n"); + first = false; + } + footnoteText.append(p.getText()); + } + + footnoteText.append("]"); + } + } + + // Loop over pictures inside our + // paragraph, looking for text in them + CTPicture[] picts = rs.get(j).getPictArray(); + for (int k = 0; k < picts.length; k++) { + XmlObject[] t = picts[k] + .selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//w:t"); + for (int m = 0; m < t.length; m++) { + NodeList kids = t[m].getDomNode().getChildNodes(); + for (int n = 0; n < kids.getLength(); n++) { + if (kids.item(n) instanceof Text) { + pictureText.append("\n"); + pictureText.append(kids.item(n).getNodeValue()); + } + } + } + } + } + } } public CTP getCTP() { - return paragraph; + return paragraph; } public boolean isEmpty() { - return !paragraph.getDomNode().hasChildNodes(); + return !paragraph.getDomNode().hasChildNodes(); } public XWPFDocument getDocument() { - return document; + return document; } /** @@ -146,7 +145,9 @@ public class XWPFParagraph { * in it. */ public String getText() { - return getParagraphText() + getPictureText(); + StringBuffer out = new StringBuffer(); + out.append(text).append(footnoteText).append(pictureText); + return out.toString(); } /** @@ -154,14 +155,23 @@ public class XWPFParagraph { * paragraph */ public String getParagraphText() { - return text.toString(); + return text.toString(); } /** * Returns any text from any suitable pictures in the paragraph */ public String getPictureText() { - return pictureText.toString(); + return pictureText.toString(); + } + + /** + * Returns the footnote text of the paragraph + * + * @return the footnote text or empty string if the paragraph does not have footnotes + */ + public String getFootnoteText() { + return footnoteText.toString(); } /** @@ -170,7 +180,7 @@ public class XWPFParagraph { * @return a new text run */ public XWPFRun createRun() { - return new XWPFRun(paragraph.addNewR(), this); + return new XWPFRun(paragraph.addNewR(), this); } /** @@ -350,12 +360,12 @@ public class XWPFParagraph { * @see Borders a list of all types of borders */ public void setBorderBottom(Borders border) { - CTPBdr ct = getCTPBrd(true); - CTBorder pr = ct.isSetBottom() ? ct.getBottom() : ct.addNewBottom(); - if (border.getValue() == Borders.NONE.getValue()) - ct.unsetBottom(); - else - pr.setVal(STBorder.Enum.forInt(border.getValue())); + CTPBdr ct = getCTPBrd(true); + CTBorder pr = ct.isSetBottom() ? ct.getBottom() : ct.addNewBottom(); + if (border.getValue() == Borders.NONE.getValue()) + ct.unsetBottom(); + else + pr.setVal(STBorder.Enum.forInt(border.getValue())); } /** @@ -367,13 +377,13 @@ public class XWPFParagraph { * @see Borders a list of all types of borders */ public Borders getBorderBottom() { - CTPBdr border = getCTPBrd(false); - CTBorder ct = null; - if (border != null) { - ct = border.getBottom(); - } - STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; - return Borders.valueOf(ptrn.intValue()); + CTPBdr border = getCTPBrd(false); + CTBorder ct = null; + if (border != null) { + ct = border.getBottom(); + } + STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; + return Borders.valueOf(ptrn.intValue()); } /** @@ -399,12 +409,12 @@ public class XWPFParagraph { * @see Borders for a list of all possible borders */ public void setBorderLeft(Borders border) { - CTPBdr ct = getCTPBrd(true); - CTBorder pr = ct.isSetLeft() ? ct.getLeft() : ct.addNewLeft(); - if (border.getValue() == Borders.NONE.getValue()) - ct.unsetLeft(); - else - pr.setVal(STBorder.Enum.forInt(border.getValue())); + CTPBdr ct = getCTPBrd(true); + CTBorder pr = ct.isSetLeft() ? ct.getLeft() : ct.addNewLeft(); + if (border.getValue() == Borders.NONE.getValue()) + ct.unsetLeft(); + else + pr.setVal(STBorder.Enum.forInt(border.getValue())); } /** @@ -416,13 +426,13 @@ public class XWPFParagraph { * @see Borders for a list of all possible borders */ public Borders getBorderLeft() { - CTPBdr border = getCTPBrd(false); - CTBorder ct = null; - if (border != null) { - ct = border.getLeft(); - } - STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; - return Borders.valueOf(ptrn.intValue()); + CTPBdr border = getCTPBrd(false); + CTBorder ct = null; + if (border != null) { + ct = border.getLeft(); + } + STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; + return Borders.valueOf(ptrn.intValue()); } /** @@ -448,12 +458,12 @@ public class XWPFParagraph { * @see Borders for a list of all possible borders */ public void setBorderRight(Borders border) { - CTPBdr ct = getCTPBrd(true); - CTBorder pr = ct.isSetRight() ? ct.getRight() : ct.addNewRight(); - if (border.getValue() == Borders.NONE.getValue()) - ct.unsetRight(); - else - pr.setVal(STBorder.Enum.forInt(border.getValue())); + CTPBdr ct = getCTPBrd(true); + CTBorder pr = ct.isSetRight() ? ct.getRight() : ct.addNewRight(); + if (border.getValue() == Borders.NONE.getValue()) + ct.unsetRight(); + else + pr.setVal(STBorder.Enum.forInt(border.getValue())); } /** @@ -465,13 +475,13 @@ public class XWPFParagraph { * @see Borders for a list of all possible borders */ public Borders getBorderRight() { - CTPBdr border = getCTPBrd(false); - CTBorder ct = null; - if (border != null) { - ct = border.getRight(); - } - STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; - return Borders.valueOf(ptrn.intValue()); + CTPBdr border = getCTPBrd(false); + CTBorder ct = null; + if (border != null) { + ct = border.getRight(); + } + STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; + return Borders.valueOf(ptrn.intValue()); } /** @@ -501,12 +511,12 @@ public class XWPFParagraph { * @see Borders for a list of all possible borders */ public void setBorderBetween(Borders border) { - CTPBdr ct = getCTPBrd(true); - CTBorder pr = ct.isSetBetween() ? ct.getBetween() : ct.addNewBetween(); - if (border.getValue() == Borders.NONE.getValue()) - ct.unsetBetween(); - else - pr.setVal(STBorder.Enum.forInt(border.getValue())); + CTPBdr ct = getCTPBrd(true); + CTBorder pr = ct.isSetBetween() ? ct.getBetween() : ct.addNewBetween(); + if (border.getValue() == Borders.NONE.getValue()) + ct.unsetBetween(); + else + pr.setVal(STBorder.Enum.forInt(border.getValue())); } /** @@ -518,13 +528,13 @@ public class XWPFParagraph { * @see Borders for a list of all possible borders */ public Borders getBorderBetween() { - CTPBdr border = getCTPBrd(false); - CTBorder ct = null; - if (border != null) { - ct = border.getBetween(); - } - STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; - return Borders.valueOf(ptrn.intValue()); + CTPBdr border = getCTPBrd(false); + CTBorder ct = null; + if (border != null) { + ct = border.getBetween(); + } + STBorder.Enum ptrn = ct != null ? ct.getVal() : STBorder.NONE; + return Borders.valueOf(ptrn.intValue()); } /** @@ -544,13 +554,13 @@ public class XWPFParagraph { * boolean value */ public void setPageBreak(boolean pageBreak) { - CTPPr ppr = getCTPPr(); - CTOnOff ct_pageBreak = ppr.isSetPageBreakBefore() ? ppr - .getPageBreakBefore() : ppr.addNewPageBreakBefore(); - if (pageBreak) - ct_pageBreak.setVal(STOnOff.TRUE); - else - ct_pageBreak.setVal(STOnOff.FALSE); + CTPPr ppr = getCTPPr(); + CTOnOff ct_pageBreak = ppr.isSetPageBreakBefore() ? ppr + .getPageBreakBefore() : ppr.addNewPageBreakBefore(); + if (pageBreak) + ct_pageBreak.setVal(STOnOff.TRUE); + else + ct_pageBreak.setVal(STOnOff.FALSE); } /** @@ -569,14 +579,14 @@ public class XWPFParagraph { * @return boolean - if page break is set */ public boolean isPageBreak() { - CTPPr ppr = getCTPPr(); - CTOnOff ct_pageBreak = ppr.isSetPageBreakBefore() ? ppr - .getPageBreakBefore() : null; - if (ct_pageBreak != null - && ct_pageBreak.getVal().intValue() == STOnOff.INT_TRUE) - return true; - else - return false; + CTPPr ppr = getCTPPr(); + CTOnOff ct_pageBreak = ppr.isSetPageBreakBefore() ? ppr + .getPageBreakBefore() : null; + if (ct_pageBreak != null + && ct_pageBreak.getVal().intValue() == STOnOff.INT_TRUE) + return true; + else + return false; } /** @@ -640,7 +650,7 @@ public class XWPFParagraph { * paragraph in the document in absolute units. * * @return bigInteger - value representing the spacing after the paragraph - * @see #setSpacingAfterLines(int) + * @see #setSpacingAfterLines(int) */ public int getSpacingAfterLines() { CTSpacing spacing = getCTSpacing(false); @@ -902,12 +912,12 @@ public class XWPFParagraph { * @param wrap - boolean */ public void setWordWrap(boolean wrap) { - CTOnOff wordWrap = getCTPPr().isSetWordWrap() ? getCTPPr() - .getWordWrap() : getCTPPr().addNewWordWrap(); - if (wrap) - wordWrap.setVal(STOnOff.TRUE); - else - wordWrap.unsetVal(); + CTOnOff wordWrap = getCTPPr().isSetWordWrap() ? getCTPPr() + .getWordWrap() : getCTPPr().addNewWordWrap(); + if (wrap) + wordWrap.setVal(STOnOff.TRUE); + else + wordWrap.unsetVal(); } /** @@ -919,14 +929,14 @@ public class XWPFParagraph { * @return boolean */ public boolean isWordWrap() { - CTOnOff wordWrap = getCTPPr().isSetWordWrap() ? getCTPPr() - .getWordWrap() : null; - if (wordWrap != null) { - return (wordWrap.getVal() == STOnOff.ON - || wordWrap.getVal() == STOnOff.TRUE || wordWrap.getVal() == STOnOff.X_1) ? true - : false; - } else - return false; + CTOnOff wordWrap = getCTPPr().isSetWordWrap() ? getCTPPr() + .getWordWrap() : null; + if (wordWrap != null) { + return (wordWrap.getVal() == STOnOff.ON + || wordWrap.getVal() == STOnOff.TRUE || wordWrap.getVal() == STOnOff.X_1) ? true + : false; + } else + return false; } /** diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java index 5532ee299..c1b91bb07 100755 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java @@ -40,6 +40,24 @@ public final class XWPFRelation extends POIXMLRelation { "/word/document.xml", null ); + public static final XWPFRelation TEMPLATE = new XWPFRelation( + "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "/word/document.xml", + null + ); + public static final XWPFRelation MACRO_DOCUMENT = new XWPFRelation( + "application/vnd.ms-word.document.macroEnabled.main+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "/word/document.xml", + null + ); + public static final XWPFRelation MACRO_TEMPLATE_DOCUMENT = new XWPFRelation( + "application/vnd.ms-word.template.macroEnabledTemplate.main+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "/word/document.xml", + null + ); public static final XWPFRelation FONT_TABLE = new XWPFRelation( "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable", @@ -88,6 +106,12 @@ public final class XWPFRelation extends POIXMLRelation { null, null ); + public static final XWPFRelation FOOTNOTE = new XWPFRelation( + null, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", + null, + null + ); private XWPFRelation(String type, String rel, String defaultName, Class cls) { diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java index 59a849766..17d0465f5 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java @@ -42,8 +42,8 @@ public class XWPFTable { private CTTbl ctTbl; - public XWPFTable(CTTbl table, int row, int col) { - this(table); + public XWPFTable(XWPFDocument doc, CTTbl table, int row, int col) { + this(doc, table); for (int i = 0; i < row; i++) { XWPFTableRow tabRow = (getRow(i) == null) ? createRow() : getRow(i); for (int k = 0; k < col; k++) { @@ -54,7 +54,7 @@ public class XWPFTable { } - public XWPFTable(CTTbl table) { + public XWPFTable(XWPFDocument doc, CTTbl table) { this.ctTbl = table; // is an empty table: I add one row and one column as default @@ -65,7 +65,7 @@ public class XWPFTable { StringBuffer rowText = new StringBuffer(); for (CTTc cell : row.getTcArray()) { for (CTP ctp : cell.getPArray()) { - XWPFParagraph p = new XWPFParagraph(ctp, null); + XWPFParagraph p = new XWPFParagraph(ctp, doc); if (rowText.length() > 0) { rowText.append('\t'); } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java index 2704e0371..1527e562b 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java @@ -17,6 +17,7 @@ package org.apache.poi.xwpf.extractor; import java.io.File; +import java.io.IOException; import org.apache.poi.POIXMLDocument; import org.apache.poi.xwpf.usermodel.XWPFDocument; @@ -27,202 +28,176 @@ import junit.framework.TestCase; * Tests for HXFWordExtractor */ public class TestXWPFWordExtractor extends TestCase { - /** - * A very simple file - */ - private XWPFDocument xmlA; - private File fileA; - /** - * A fairly complex file - */ - private XWPFDocument xmlB; - private File fileB; - /** - * With a simplish header+footer - */ - private XWPFDocument xmlC; - private File fileC; - /** - * With different header+footer on first/rest - */ - private XWPFDocument xmlD; - private File fileD; - - /** - * File with hyperlinks - */ - private XWPFDocument xmlE; - private File fileE; - protected void setUp() throws Exception { - super.setUp(); - - fileA = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "sample.docx" - ); - fileB = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "IllustrativeCases.docx" - ); - fileC = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "ThreeColHeadFoot.docx" - ); - fileD = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "DiffFirstPageHeadFoot.docx" - ); - fileE = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "TestDocument.docx" - ); - assertTrue(fileA.exists()); - assertTrue(fileB.exists()); - assertTrue(fileC.exists()); - assertTrue(fileD.exists()); - assertTrue(fileE.exists()); - - xmlA = new XWPFDocument(POIXMLDocument.openPackage(fileA.toString())); - xmlB = new XWPFDocument(POIXMLDocument.openPackage(fileB.toString())); - xmlC = new XWPFDocument(POIXMLDocument.openPackage(fileC.toString())); - xmlD = new XWPFDocument(POIXMLDocument.openPackage(fileD.toString())); - xmlE = new XWPFDocument(POIXMLDocument.openPackage(fileE.toString())); - } + /** + * Get text out of the simple file + */ + public void testGetSimpleText() throws Exception { + XWPFDocument doc = open("sample.docx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); - /** - * Get text out of the simple file - */ - public void testGetSimpleText() throws Exception { - new XWPFWordExtractor(xmlA); - new XWPFWordExtractor(POIXMLDocument.openPackage(fileA.toString())); - - XWPFWordExtractor extractor = - new XWPFWordExtractor(xmlA); - extractor.getText(); - - String text = extractor.getText(); - assertTrue(text.length() > 0); - - // Check contents - assertTrue(text.startsWith( - "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nunc at risus vel erat tempus posuere. Aenean non ante. Suspendisse vehicula dolor sit amet odio." - )); - assertTrue(text.endsWith( - "Phasellus ultricies mi nec leo. Sed tempus. In sit amet lorem at velit faucibus vestibulum.\n" - )); - - // Check number of paragraphs - int ps = 0; - char[] t = text.toCharArray(); - for (int i = 0; i < t.length; i++) { - if(t[i] == '\n') { ps++; } - } - assertEquals(3, ps); - } - - /** - * Tests getting the text out of a complex file - */ - public void testGetComplexText() throws Exception { - XWPFWordExtractor extractor = - new XWPFWordExtractor(xmlB); - extractor.getText(); - - String text = extractor.getText(); - assertTrue(text.length() > 0); - - char euro = '\u20ac'; + String text = extractor.getText(); + assertTrue(text.length() > 0); + + // Check contents + assertTrue(text.startsWith( + "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nunc at risus vel erat tempus posuere. Aenean non ante. Suspendisse vehicula dolor sit amet odio." + )); + assertTrue(text.endsWith( + "Phasellus ultricies mi nec leo. Sed tempus. In sit amet lorem at velit faucibus vestibulum.\n" + )); + + // Check number of paragraphs + int ps = 0; + char[] t = text.toCharArray(); + for (int i = 0; i < t.length; i++) { + if (t[i] == '\n') { + ps++; + } + } + assertEquals(3, ps); + } + + /** + * Tests getting the text out of a complex file + */ + public void testGetComplexText() throws Exception { + XWPFDocument doc = open("IllustrativeCases.docx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + + String text = extractor.getText(); + assertTrue(text.length() > 0); + + char euro = '\u20ac'; // System.err.println("'"+text.substring(text.length() - 40) + "'"); - - // Check contents - assertTrue(text.startsWith( - " \n(V) ILLUSTRATIVE CASES\n\n" - )); - assertTrue(text.contains( - "As well as gaining "+euro+"90 from child benefit increases, he will also receive the early childhood supplement of "+euro+"250 per quarter for Vincent for the full four quarters of the year.\n\n\n\n \n\n\n" - )); - assertTrue(text.endsWith( - "11.4%\t\t90\t\t\t\t\t250\t\t1,310\t\n\n" - )); - - // Check number of paragraphs - int ps = 0; - char[] t = text.toCharArray(); - for (int i = 0; i < t.length; i++) { - if(t[i] == '\n') { ps++; } - } - assertEquals(103, ps); - } - - public void testGetWithHyperlinks() throws Exception { - XWPFWordExtractor extractor = - new XWPFWordExtractor(xmlE); - extractor.getText(); - extractor.setFetchHyperlinks(true); - extractor.getText(); - // Now check contents - // TODO - fix once correctly handling contents - extractor.setFetchHyperlinks(false); - assertEquals( + // Check contents + assertTrue(text.startsWith( + " \n(V) ILLUSTRATIVE CASES\n\n" + )); + assertTrue(text.contains( + "As well as gaining " + euro + "90 from child benefit increases, he will also receive the early childhood supplement of " + euro + "250 per quarter for Vincent for the full four quarters of the year.\n\n\n\n \n\n\n" + )); + assertTrue(text.endsWith( + "11.4%\t\t90\t\t\t\t\t250\t\t1,310\t\n\n" + )); + + // Check number of paragraphs + int ps = 0; + char[] t = text.toCharArray(); + for (int i = 0; i < t.length; i++) { + if (t[i] == '\n') { + ps++; + } + } + assertEquals(103, ps); + } + + public void testGetWithHyperlinks() throws Exception { + XWPFDocument doc = open("TestDocument.docx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + + // Now check contents + // TODO - fix once correctly handling contents + extractor.setFetchHyperlinks(false); + assertEquals( // "This is a test document\nThis bit is in bold and italic\n" + // "Back to normal\nWe have a hyperlink here, and another.\n", - "This is a test document\nThis bit is in bold and italic\n" + - "Back to normal\nWe have a here, and .hyperlinkanother\n", - extractor.getText() - ); - - extractor.setFetchHyperlinks(true); - assertEquals( + "This is a test document\nThis bit is in bold and italic\n" + + "Back to normal\nWe have a here, and .hyperlinkanother\n", + extractor.getText() + ); + + extractor.setFetchHyperlinks(true); + assertEquals( // "This is a test document\nThis bit is in bold and italic\n" + // "Back to normal\nWe have a hyperlink here, and another.\n", - "This is a test document\nThis bit is in bold and italic\n" + - "Back to normal\nWe have a here, and .hyperlink another\n", - extractor.getText() - ); - } - - public void testHeadersFooters() throws Exception { - XWPFWordExtractor extractor = - new XWPFWordExtractor(xmlC); - extractor.getText(); - - assertEquals( - "First header column!\tMid header\tRight header!\n" + - "This is a sample word document. It has two pages. It has a three column heading, and a three column footer\n" + - "\n" + - "HEADING TEXT\n" + - "\n" + - "More on page one\n" + - "\n\n" + - "End of page 1\n\n" + - "This is page two. It also has a three column heading, and a three column footer.\n" + - "Footer Left\tFooter Middle\tFooter Right\n", - extractor.getText() - ); - - - // Now another file, expect multiple headers - // and multiple footers - extractor = - new XWPFWordExtractor(xmlD); - extractor.getText(); - - assertEquals( - "I am the header on the first page, and I" + '\u2019' + "m nice and simple\n" + - "First header column!\tMid header\tRight header!\n" + - "This is a sample word document. It has two pages. It has a simple header and footer, which is different to all the other pages.\n" + - "\n" + - "HEADING TEXT\n" + - "\n" + - "More on page one\n" + - "\n\n" + - "End of page 1\n\n" + - "This is page two. It also has a three column heading, and a three column footer.\n" + - "The footer of the first page\n" + - "Footer Left\tFooter Middle\tFooter Right\n", - extractor.getText() - ); - } + "This is a test document\nThis bit is in bold and italic\n" + + "Back to normal\nWe have a here, and .hyperlink another\n", + extractor.getText() + ); + } + + public void testHeadersFooters() throws Exception { + XWPFDocument doc = open("ThreeColHeadFoot.docx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + + assertEquals( + "First header column!\tMid header\tRight header!\n" + + "This is a sample word document. It has two pages. It has a three column heading, and a three column footer\n" + + "\n" + + "HEADING TEXT\n" + + "\n" + + "More on page one\n" + + "\n\n" + + "End of page 1\n\n" + + "This is page two. It also has a three column heading, and a three column footer.\n" + + "Footer Left\tFooter Middle\tFooter Right\n", + extractor.getText() + ); + + // Now another file, expect multiple headers + // and multiple footers + doc = open("DiffFirstPageHeadFoot.docx"); + extractor = new XWPFWordExtractor(doc); + extractor = + new XWPFWordExtractor(doc); + extractor.getText(); + + assertEquals( + "I am the header on the first page, and I" + '\u2019' + "m nice and simple\n" + + "First header column!\tMid header\tRight header!\n" + + "This is a sample word document. It has two pages. It has a simple header and footer, which is different to all the other pages.\n" + + "\n" + + "HEADING TEXT\n" + + "\n" + + "More on page one\n" + + "\n\n" + + "End of page 1\n\n" + + "This is page two. It also has a three column heading, and a three column footer.\n" + + "The footer of the first page\n" + + "Footer Left\tFooter Middle\tFooter Right\n", + extractor.getText() + ); + } + + public void testFootnotes() throws Exception { + XWPFDocument doc = open("footnotes.docx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + + assertTrue(extractor.getText().contains("snoska")); + } + + + public void testTableFootnotes() throws Exception { + XWPFDocument doc = open("table_footnotes.docx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + + assertTrue(extractor.getText().contains("snoska")); + } + + public void testFormFootnotes() throws Exception { + XWPFDocument doc = open("form_footnotes.docx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + + String text = extractor.getText(); + assertTrue("Unable to find expected word in text\n" + text, text.contains("testdoc")); + assertTrue("Unable to find expected word in text\n" + text, text.contains("test phrase")); + } + + //TODO use the same logic as in HSSFTestDataSamples + private XWPFDocument open(String sampleFileName) throws IOException { + File file = new File( + System.getProperty("HWPF.testdata.path"), sampleFileName); + + try { + if(!sampleFileName.equals(file.getCanonicalFile().getName())){ + throw new RuntimeException("File name is case-sensitive: requested '" + sampleFileName + + "' but actual file is '" + file.getCanonicalFile().getName() + "'"); + } + } catch (IOException e){ + throw new RuntimeException(e); + } + return new XWPFDocument(POIXMLDocument.openPackage(file.getPath())); + } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTable.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTable.java index 0819d4a38..4d877a9bc 100755 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTable.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTable.java @@ -43,14 +43,14 @@ public class TestXWPFTable extends TestCase { public void testConstructor() { CTTbl ctTable=CTTbl.Factory.newInstance(); - XWPFTable xtab=new XWPFTable(ctTable); + XWPFTable xtab=new XWPFTable(null, ctTable); assertNotNull(xtab); assertEquals(1,ctTable.sizeOfTrArray()); assertEquals(1,ctTable.getTrArray(0).sizeOfTcArray()); assertNotNull(ctTable.getTrArray(0).getTcArray(0).getPArray(0)); ctTable=CTTbl.Factory.newInstance(); - xtab=new XWPFTable(ctTable, 3,2); + xtab=new XWPFTable(null, ctTable, 3,2); assertNotNull(xtab); assertEquals(3,ctTable.sizeOfTrArray()); assertEquals(2,ctTable.getTrArray(0).sizeOfTcArray()); @@ -67,7 +67,7 @@ public class TestXWPFTable extends TestCase { CTText text=run.addNewT(); text.setStringValue("finally I can write!"); - XWPFTable xtab=new XWPFTable(table); + XWPFTable xtab=new XWPFTable(null, table); assertEquals("finally I can write!\n",xtab.getText()); } @@ -84,7 +84,7 @@ public class TestXWPFTable extends TestCase { r3.addNewTc().addNewP(); r3.addNewTc().addNewP(); - XWPFTable xtab=new XWPFTable(table); + XWPFTable xtab=new XWPFTable(null, table); assertEquals(3,xtab.getNumberOfRows()); assertNotNull(xtab.getRow(2)); @@ -95,7 +95,7 @@ public class TestXWPFTable extends TestCase { assertEquals(2,table.getTrArray(0).sizeOfTcArray()); //check creation of first row - xtab=new XWPFTable(CTTbl.Factory.newInstance()); + xtab=new XWPFTable(null, CTTbl.Factory.newInstance()); assertEquals(1,xtab.getCTTbl().getTrArray(0).sizeOfTcArray()); } @@ -104,7 +104,7 @@ public class TestXWPFTable extends TestCase { CTTbl table = CTTbl.Factory.newInstance(); table.addNewTblPr().addNewTblW().setW(new BigInteger("1000")); - XWPFTable xtab=new XWPFTable(table); + XWPFTable xtab=new XWPFTable(null, table); assertEquals(1000,xtab.getWidth()); diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/data/footnotes.docx b/src/scratchpad/testcases/org/apache/poi/hwpf/data/footnotes.docx new file mode 100755 index 0000000000000000000000000000000000000000..db4386c09c85279519fc0d1c086d9189cd2b2b19 GIT binary patch literal 12823 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 literal 0 HcmV?d00001 diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/data/form_footnotes.docx b/src/scratchpad/testcases/org/apache/poi/hwpf/data/form_footnotes.docx new file mode 100755 index 0000000000000000000000000000000000000000..70abb60c9a8f1676e5a2669e32fa1811385b248d GIT binary patch literal 27824 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 aVN5{=0_bo*QfDLpD)0_N;m36#;C}#$!W=6A literal 0 HcmV?d00001