mirror of https://github.com/apache/poi.git
[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
This commit is contained in:
parent
ce7c1c92da
commit
ba1e8de446
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p><em>
|
||||
* The test saves xlsx file on a disk if the system property is set:
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -560,7 +560,7 @@ public final class CryptoFunctions {
|
|||
* @param password the password
|
||||
* @return the ansi bytes
|
||||
*
|
||||
* @see <a href="http://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf">Part 4 - Markup Language Reference - Ecma International - section 3.2.29 (workbookProtection)</a>
|
||||
* @see <a href="https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf">Part 4 - Markup Language Reference - Ecma International - section 3.2.29 (workbookProtection)</a>
|
||||
*/
|
||||
private static byte[] toAnsiPassword(String password) {
|
||||
// TODO: charset conversion (see ecma spec)
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue