From ba1e8de44655b7add2a9efca993dcf7a75eb282b Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 13 Oct 2022 10:38:31 +0000 Subject: [PATCH] [bug-66312] partial fix for insertNewParagraph(XmlCursor cursor) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1904563 13f79535-47bb-0310-9956-ffa450edef68 --- legal/LICENSE | 8 +- .../poi/xslf/usermodel/XSLFSlideShow.java | 2 +- .../poi/xwpf/usermodel/XWPFDocument.java | 192 ++++++++---------- .../xssf/usermodel/TestXSSFColGrouping.java | 2 +- .../org/apache/poi/xwpf/TestXWPFBugs.java | 30 +++ .../poi/poifs/crypt/CryptoFunctions.java | 2 +- test-data/document/bug66312.docx | Bin 0 -> 4500 bytes 7 files changed, 125 insertions(+), 111 deletions(-) create mode 100644 test-data/document/bug66312.docx diff --git a/legal/LICENSE b/legal/LICENSE index be6c2c65c2..e73db2bb31 100644 --- a/legal/LICENSE +++ b/legal/LICENSE @@ -222,12 +222,12 @@ Office Open XML schemas (poi-ooxml-full-*.jar) Furthermore, both Microsoft and Adobe have granted patent licenses to this work [3,4,5]. - [1] http://www.ecma-international.org/publications/standards/Ecma-376.htm - [2] http://www.ecma-international.org/memento/Ecmabylaws.htm + [1] https://www.ecma-international.org/publications/standards/Ecma-376.htm + [2] https://www.ecma-international.org/memento/Ecmabylaws.htm [3] http://www.microsoft.com/openspecifications/en/us/programs/osp/default.aspx - [4] http://www.ecma-international.org/publications/files/ECMA-ST/Ecma%20PATENT/ + [4] https://www.ecma-international.org/publications/files/ECMA-ST/Ecma%20PATENT/ Patent%20statements%20ok/ECMA-376%20Edition%202%20Microsoft%20Patent%20Declaration.pdf - [5] http://www.ecma-international.org/publications/files/ECMA-ST/Ecma%20PATENT/ + [5] https://www.ecma-international.org/publications/files/ECMA-ST/Ecma%20PATENT/ Patent%20statements%20ok/ECMA-376%20Adobe%20Patent%20Declaration.pdf diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSlideShow.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSlideShow.java index f489b8c32e..2ba5b79ff4 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSlideShow.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSlideShow.java @@ -56,7 +56,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; * If you are using these low level classes, then you * will almost certainly need to refer to the OOXML * specifications from - * http://www.ecma-international.org/publications/standards/Ecma-376.htm + * https://www.ecma-international.org/publications/standards/Ecma-376.htm * * WARNING - APIs expected to change rapidly */ diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index 0f7f08f619..fdfb3429a8 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -96,7 +96,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument; * XML structure come through. You'll therefore almost * certainly need to refer to the OOXML specifications * from - * http://www.ecma-international.org/publications/standards/Ecma-376.htm + * https://www.ecma-international.org/publications/standards/Ecma-376.htm * at some point in your use. */ @SuppressWarnings("unused") @@ -678,118 +678,102 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * * @param cursor The cursor-position where the new paragraph should be added. * @return the {@link XWPFParagraph} object representing the newly inserted - * CTP object + * CTP object. */ @Override public XWPFParagraph insertNewParagraph(XmlCursor cursor) { - if (isCursorInBody(cursor)) { - String uri = CTP.type.getName().getNamespaceURI(); - /* - * TODO DO not use a coded constant, find the constant in the OOXML - * classes instead, as the child of type CT_Paragraph is defined in the - * OOXML schema as 'p' - */ - String localPart = "p"; - // creates a new Paragraph, cursor is positioned inside the new - // element - cursor.beginElement(localPart, uri); - // move the cursor to the START token to the paragraph just created - cursor.toParent(); - CTP p = (CTP) cursor.getObject(); - XWPFParagraph newP = new XWPFParagraph(p, this); - XmlObject o = null; - /* - * move the cursor to the previous element until a) the next - * paragraph is found or b) all elements have been passed - */ - while (!(o instanceof CTP) && (cursor.toPrevSibling())) { - o = cursor.getObject(); - } - /* - * if the object that has been found is a) not a paragraph or b) is - * the paragraph that has just been inserted, as the cursor in the - * while loop above was not moved as there were no other siblings, - * then the paragraph that was just inserted is the first paragraph - * in the body. Otherwise, take the previous paragraph and calculate - * the new index for the new paragraph. - */ - if ((!(o instanceof CTP)) || o == p) { - paragraphs.add(0, newP); - } else { - int pos = paragraphs.indexOf(getParagraph((CTP) o)) + 1; - paragraphs.add(pos, newP); - } - - /* - * create a new cursor, that points to the START token of the just - * inserted paragraph - */ - try (XmlCursor newParaPos = p.newCursor()) { - /* - * Calculate the paragraphs index in the list of all body - * elements - */ - int i = 0; - cursor.toCursor(newParaPos); - while (cursor.toPrevSibling()) { - o = cursor.getObject(); - if (o instanceof CTP || o instanceof CTTbl) { - i++; - } - } - bodyElements.add(i, newP); - cursor.toCursor(newParaPos); - cursor.toEndToken(); - return newP; - } + String uri = CTP.type.getName().getNamespaceURI(); + /* + * TODO DO not use a coded constant, find the constant in the OOXML + * classes instead, as the child of type CT_Paragraph is defined in the + * OOXML schema as 'p' + */ + String localPart = "p"; + // creates a new Paragraph, cursor is positioned inside the new + // element + cursor.beginElement(localPart, uri); + // move the cursor to the START token to the paragraph just created + cursor.toParent(); + CTP p = (CTP) cursor.getObject(); + XWPFParagraph newP = new XWPFParagraph(p, this); + XmlObject o = null; + /* + * move the cursor to the previous element until a) the next + * paragraph is found or b) all elements have been passed + */ + while (!(o instanceof CTP) && (cursor.toPrevSibling())) { + o = cursor.getObject(); + } + /* + * if the object that has been found is a) not a paragraph or b) is + * the paragraph that has just been inserted, as the cursor in the + * while loop above was not moved as there were no other siblings, + * then the paragraph that was just inserted is the first paragraph + * in the body. Otherwise, take the previous paragraph and calculate + * the new index for the new paragraph. + */ + if ((!(o instanceof CTP)) || o == p) { + paragraphs.add(0, newP); + } else { + int pos = paragraphs.indexOf(getParagraph((CTP) o)) + 1; + paragraphs.add(pos, newP); + } + + /* + * create a new cursor, that points to the START token of the just + * inserted paragraph + */ + try (XmlCursor newParaPos = p.newCursor()) { + /* + * Calculate the paragraphs index in the list of all body + * elements + */ + int i = 0; + cursor.toCursor(newParaPos); + while (cursor.toPrevSibling()) { + o = cursor.getObject(); + if (o instanceof CTP || o instanceof CTTbl) { + i++; + } + } + bodyElements.add(i, newP); + cursor.toCursor(newParaPos); + cursor.toEndToken(); + return newP; } - return null; } @Override public XWPFTable insertNewTbl(XmlCursor cursor) { - if (isCursorInBody(cursor)) { - String uri = CTTbl.type.getName().getNamespaceURI(); - String localPart = "tbl"; - cursor.beginElement(localPart, uri); - cursor.toParent(); - CTTbl t = (CTTbl) cursor.getObject(); - XWPFTable newT = new XWPFTable(t, this); - XmlObject o = null; - while (!(o instanceof CTTbl) && (cursor.toPrevSibling())) { - o = cursor.getObject(); - } - if (!(o instanceof CTTbl)) { - tables.add(0, newT); - } else { - int pos = tables.indexOf(getTable((CTTbl) o)) + 1; - tables.add(pos, newT); - } - int i = 0; - try (XmlCursor tableCursor = t.newCursor()) { - cursor.toCursor(tableCursor); - while (cursor.toPrevSibling()) { - o = cursor.getObject(); - if (o instanceof CTP || o instanceof CTTbl) { - i++; - } - } - bodyElements.add(i, newT); - cursor.toCursor(tableCursor); - cursor.toEndToken(); - return newT; - } + String uri = CTTbl.type.getName().getNamespaceURI(); + String localPart = "tbl"; + cursor.beginElement(localPart, uri); + cursor.toParent(); + CTTbl t = (CTTbl) cursor.getObject(); + XWPFTable newT = new XWPFTable(t, this); + XmlObject o = null; + while (!(o instanceof CTTbl) && (cursor.toPrevSibling())) { + o = cursor.getObject(); } - return null; - } - - /** - * verifies that cursor is on the right position - */ - private boolean isCursorInBody(XmlCursor cursor) { - try (XmlCursor verify = cursor.newCursor()) { - verify.toParent(); - return (verify.getObject() == this.ctDocument.getBody()); + if (!(o instanceof CTTbl)) { + tables.add(0, newT); + } else { + int pos = tables.indexOf(getTable((CTTbl) o)) + 1; + tables.add(pos, newT); + } + int i = 0; + try (XmlCursor tableCursor = t.newCursor()) { + cursor.toCursor(tableCursor); + while (cursor.toPrevSibling()) { + o = cursor.getObject(); + if (o instanceof CTP || o instanceof CTTbl) { + i++; + } + } + bodyElements.add(i, newT); + cursor.toCursor(tableCursor); + cursor.toEndToken(); + return newT; } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java index 18eb1cc0da..69f4e79e62 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java @@ -33,7 +33,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; /** * Test asserts the POI produces <cols> element that could be read and properly interpreted by the MS Excel. * For specification of the "cols" element see the chapter 3.3.1.16 of the "Office Open XML Part 4 - Markup Language Reference.pdf". - * The specification can be downloaded at http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%201st%20edition%20Part%204%20(PDF).zip. + * The specification can be downloaded at https://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%201st%20edition%20Part%204%20(PDF).zip. * *

* The test saves xlsx file on a disk if the system property is set: diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/TestXWPFBugs.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/TestXWPFBugs.java index 4a82546a45..2de05f25d8 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/TestXWPFBugs.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/TestXWPFBugs.java @@ -43,6 +43,9 @@ import org.apache.poi.poifs.filesystem.Ole10Native; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFTableCell; +import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.junit.jupiter.api.Test; import org.openxmlformats.schemas.wordprocessingml.x2006.main.DocumentDocument; @@ -182,4 +185,31 @@ class TestXWPFBugs { } } } + + @Test + void insertParagraphDirectlyIntoBody() throws IOException { + try (XWPFDocument document = new XWPFDocument(samples.openResourceAsStream("bug66312.docx"))) { + XWPFParagraph paragraph = document.getParagraphArray(0); + insertParagraph(paragraph, document); + assertEquals("Hello", document.getParagraphArray(0).getText()); + assertEquals("World", document.getParagraphArray(1).getText()); + } + } + + @Test + void insertParagraphIntoTable() throws IOException { + try (XWPFDocument document = new XWPFDocument(samples.openResourceAsStream("bug66312.docx"))) { + XWPFTableCell cell = document.getTableArray(0).getRow(0).getCell(0); + XWPFParagraph paragraph = cell.getParagraphArray(0); + insertParagraph(paragraph, document); + //TODO the issue reporter thinks that there should be 2 paragraphs (with 'Hello' and 'World' repectively). + assertEquals("World", cell.getParagraphArray(0).getText()); + } + } + + public static void insertParagraph(XWPFParagraph xwpfParagraph, XWPFDocument document) { + XmlCursor xmlCursor = xwpfParagraph.getCTP().newCursor(); + XWPFParagraph xwpfParagraph2 = document.insertNewParagraph(xmlCursor); + xwpfParagraph2.createRun().setText("Hello"); + } } diff --git a/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java b/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java index d48c58f1f1..4a8883597b 100644 --- a/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java +++ b/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java @@ -560,7 +560,7 @@ public final class CryptoFunctions { * @param password the password * @return the ansi bytes * - * @see Part 4 - Markup Language Reference - Ecma International - section 3.2.29 (workbookProtection) + * @see Part 4 - Markup Language Reference - Ecma International - section 3.2.29 (workbookProtection) */ private static byte[] toAnsiPassword(String password) { // TODO: charset conversion (see ecma spec) diff --git a/test-data/document/bug66312.docx b/test-data/document/bug66312.docx new file mode 100644 index 0000000000000000000000000000000000000000..3880a1364aec5b2a519051ba58737590063cd145 GIT binary patch literal 4500 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