embedds = new LinkedList<>();
// Get the embeddings for the workbook
PackagePart part = getPackagePart();
for (PackageRelationship rel : getPackagePart().getRelationshipsByType(OLE_OBJECT_REL_TYPE)) {
embedds.add(part.getRelatedPart(rel));
}
for (PackageRelationship rel : getPackagePart().getRelationshipsByType(PACK_OBJECT_REL_TYPE)) {
embedds.add(part.getRelatedPart(rel));
}
return embedds;
}
/**
* Finds that for example the 2nd entry in the body list is the 1st paragraph
*/
private int getBodyElementSpecificPos(int pos, List extends IBodyElement> list) {
// If there's nothing to find, skip it
if (list.size() == 0) {
return -1;
}
if (pos >= 0 && pos < bodyElements.size()) {
// Ensure the type is correct
IBodyElement needle = bodyElements.get(pos);
if (needle.getElementType() != list.get(0).getElementType()) {
// Wrong type
return -1;
}
// Work back until we find it
int startPos = Math.min(pos, list.size() - 1);
for (int i = startPos; i >= 0; i--) {
if (list.get(i) == needle) {
return i;
}
}
}
// Couldn't be found
return -1;
}
/**
* Look up the paragraph at the specified position in the body elements list
* and return this paragraphs position in the paragraphs list
*
* @param pos The position of the relevant paragraph in the body elements
* list
* @return the position of the paragraph in the paragraphs list, if there is
* a paragraph at the position in the bodyelements list. Else it
* will return -1
*/
public int getParagraphPos(int pos) {
return getBodyElementSpecificPos(pos, paragraphs);
}
/**
* get with the position of a table in the bodyelement array list
* the position of this table in the table array list
*
* @param pos position of the table in the bodyelement array list
* @return if there is a table at the position in the bodyelement array list,
* else it will return null.
*/
public int getTablePos(int pos) {
return getBodyElementSpecificPos(pos, tables);
}
/**
* Add a new paragraph at position of the cursor. The cursor must be on the
* {@link XmlCursor.TokenType#START} tag of an subelement
* of the documents body. When this method is done, the cursor passed as
* parameter points to the {@link XmlCursor.TokenType#END}
* of the newly inserted paragraph.
*
* @param cursor The cursor-position where the new paragraph should be added.
* @return the {@link XWPFParagraph} object representing the newly inserted
* 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
*/
XmlCursor newParaPos = p.newCursor();
try {
/*
* 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;
} finally {
newParaPos.dispose();
}
}
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;
XmlCursor tableCursor = t.newCursor();
try {
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;
} finally {
tableCursor.dispose();
}
}
return null;
}
/**
* verifies that cursor is on the right position
*
* @param cursor
*/
private boolean isCursorInBody(XmlCursor cursor) {
XmlCursor verify = cursor.newCursor();
verify.toParent();
boolean result = (verify.getObject() == this.ctDocument.getBody());
verify.dispose();
return result;
}
private int getPosOfBodyElement(IBodyElement needle) {
BodyElementType type = needle.getElementType();
IBodyElement current;
for (int i = 0; i < bodyElements.size(); i++) {
current = bodyElements.get(i);
if (current.getElementType() == type) {
if (current.equals(needle)) {
return i;
}
}
}
return -1;
}
/**
* Get the position of the paragraph, within the list
* of all the body elements.
*
* @param p The paragraph to find
* @return The location, or -1 if the paragraph couldn't be found
*/
public int getPosOfParagraph(XWPFParagraph p) {
return getPosOfBodyElement(p);
}
/**
* Get the position of the table, within the list of
* all the body elements.
*
* @param t The table to find
* @return The location, or -1 if the table couldn't be found
*/
public int getPosOfTable(XWPFTable t) {
return getPosOfBodyElement(t);
}
/**
* commit and saves the document
*/
@Override
protected void commit() throws IOException {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTDocument1.type.getName().getNamespaceURI(), "document"));
PackagePart part = getPackagePart();
OutputStream out = part.getOutputStream();
ctDocument.save(out, xmlOptions);
out.close();
}
/**
* Gets the index of the relation we're trying to create
*
* @param relation
* @return i
*/
private int getRelationIndex(XWPFRelation relation) {
int i = 1;
for (RelationPart rp : getRelationParts()) {
if (rp.getRelationship().getRelationshipType().equals(relation.getRelation())) {
i++;
}
}
return i;
}
/**
* Appends a new paragraph to this document
*
* @return a new paragraph
*/
public XWPFParagraph createParagraph() {
XWPFParagraph p = new XWPFParagraph(ctDocument.getBody().addNewP(), this);
bodyElements.add(p);
paragraphs.add(p);
return p;
}
/**
* Creates an empty numbering if one does not already exist and sets the numbering member
*
* @return numbering
*/
public XWPFNumbering createNumbering() {
if (numbering == null) {
NumberingDocument numberingDoc = NumberingDocument.Factory.newInstance();
XWPFRelation relation = XWPFRelation.NUMBERING;
int i = getRelationIndex(relation);
XWPFNumbering wrapper = (XWPFNumbering) createRelationship(relation, XWPFFactory.getInstance(), i);
wrapper.setNumbering(numberingDoc.addNewNumbering());
numbering = wrapper;
}
return numbering;
}
/**
* Creates an empty styles for the document if one does not already exist
*
* @return styles
*/
public XWPFStyles createStyles() {
if (styles == null) {
StylesDocument stylesDoc = StylesDocument.Factory.newInstance();
XWPFRelation relation = XWPFRelation.STYLES;
int i = getRelationIndex(relation);
XWPFStyles wrapper = (XWPFStyles) createRelationship(relation, XWPFFactory.getInstance(), i);
wrapper.setStyles(stylesDoc.addNewStyles());
styles = wrapper;
}
return styles;
}
/**
* Creates an empty footnotes element for the document if one does not already exist
*
* @return footnotes
*/
public XWPFFootnotes createFootnotes() {
if (footnotes == null) {
FootnotesDocument footnotesDoc = FootnotesDocument.Factory.newInstance();
XWPFRelation relation = XWPFRelation.FOOTNOTE;
int i = getRelationIndex(relation);
XWPFFootnotes wrapper = (XWPFFootnotes) createRelationship(relation, XWPFFactory.getInstance(), i);
wrapper.setFootnotes(footnotesDoc.addNewFootnotes());
wrapper.setIdManager(this.footnoteIdManager);
footnotes = wrapper;
}
return footnotes;
}
/**
* Add a CTFtnEdn footnote to the document.
*
* @param note CTFtnEnd to be added.
* @return New {@link XWPFFootnote}
*/
@Internal
public XWPFFootnote addFootnote(CTFtnEdn note) {
return footnotes.addFootnote(note);
}
/**
* Add a CTFtnEdn endnote to the document.
*
* @param note CTFtnEnd to be added.
* @return New {@link XWPFEndnote}
*/
@Internal
public XWPFEndnote addEndnote(CTFtnEdn note) {
XWPFEndnote endnote = new XWPFEndnote(this, note);
endnotes.addEndnote(note);
return endnote;
}
/**
* remove a BodyElement from bodyElements array list
*
* @param pos
* @return true if removing was successfully, else return false
*/
public boolean removeBodyElement(int pos) {
if (pos >= 0 && pos < bodyElements.size()) {
BodyElementType type = bodyElements.get(pos).getElementType();
if (type == BodyElementType.TABLE) {
int tablePos = getTablePos(pos);
tables.remove(tablePos);
ctDocument.getBody().removeTbl(tablePos);
}
if (type == BodyElementType.PARAGRAPH) {
int paraPos = getParagraphPos(pos);
paragraphs.remove(paraPos);
ctDocument.getBody().removeP(paraPos);
}
bodyElements.remove(pos);
return true;
}
return false;
}
/**
* copies content of a paragraph to a existing paragraph in the list paragraphs at position pos
*
* @param paragraph
* @param pos
*/
public void setParagraph(XWPFParagraph paragraph, int pos) {
paragraphs.set(pos, paragraph);
ctDocument.getBody().setPArray(pos, paragraph.getCTP());
/* TODO update body element, update xwpf element, verify that
* incoming paragraph belongs to this document or if not, XML was
* copied properly (namespace-abbreviations, etc.)
*/
}
/**
* @return the LastParagraph of the document
*/
public XWPFParagraph getLastParagraph() {
int lastPos = paragraphs.toArray().length - 1;
return paragraphs.get(lastPos);
}
/**
* Create an empty table with one row and one column as default.
*
* @return a new table
*/
public XWPFTable createTable() {
XWPFTable table = new XWPFTable(ctDocument.getBody().addNewTbl(), this);
bodyElements.add(table);
tables.add(table);
return table;
}
/**
* Create an empty table with a number of rows and cols specified
*
* @param rows
* @param cols
* @return table
*/
public XWPFTable createTable(int rows, int cols) {
XWPFTable table = new XWPFTable(ctDocument.getBody().addNewTbl(), this, rows, cols);
bodyElements.add(table);
tables.add(table);
return table;
}
/**
*
*/
public void createTOC() {
CTSdtBlock block = this.getDocument().getBody().addNewSdt();
TOC toc = new TOC(block);
for (XWPFParagraph par : paragraphs) {
String parStyle = par.getStyle();
if (parStyle != null && parStyle.startsWith("Heading")) {
try {
int level = Integer.parseInt(parStyle.substring("Heading".length()));
toc.addRow(level, par.getText(), 1, "112723803");
} catch (NumberFormatException e) {
LOG.atError().withThrowable(e).log("can't format number in TOC heading");
}
}
}
}
/**
* Replace content of table in array tables at position pos with a
*
* @param pos
* @param table
*/
public void setTable(int pos, XWPFTable table) {
tables.set(pos, table);
ctDocument.getBody().setTblArray(pos, table.getCTTbl());
}
/**
* Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="readOnly" w:enforcement="1"/>
*
*
* @return true if documentProtection is enforced with option any
*/
public boolean isEnforcedProtection() {
return settings.isEnforcedWith();
}
/**
* Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
* and that the kind of protection is readOnly (w:edit="readOnly")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="readOnly" w:enforcement="1"/>
*
*
* @return true if documentProtection is enforced with option readOnly
*/
public boolean isEnforcedReadonlyProtection() {
return settings.isEnforcedWith(STDocProtect.READ_ONLY);
}
/**
* Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
* and that the kind of protection is forms (w:edit="forms")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="forms" w:enforcement="1"/>
*
*
* @return true if documentProtection is enforced with option forms
*/
public boolean isEnforcedFillingFormsProtection() {
return settings.isEnforcedWith(STDocProtect.FORMS);
}
/**
* Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
* and that the kind of protection is comments (w:edit="comments")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="comments" w:enforcement="1"/>
*
*
* @return true if documentProtection is enforced with option comments
*/
public boolean isEnforcedCommentsProtection() {
return settings.isEnforcedWith(STDocProtect.COMMENTS);
}
/**
* Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
* and that the kind of protection is trackedChanges (w:edit="trackedChanges")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="trackedChanges" w:enforcement="1"/>
*
*
* @return true if documentProtection is enforced with option trackedChanges
*/
public boolean isEnforcedTrackedChangesProtection() {
return settings.isEnforcedWith(STDocProtect.TRACKED_CHANGES);
}
public boolean isEnforcedUpdateFields() {
return settings.isUpdateFields();
}
/**
* Enforces the readOnly protection.
* In the documentProtection tag inside settings.xml file,
* it sets the value of enforcement to "1" (w:enforcement="1")
* and the value of edit to readOnly (w:edit="readOnly")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="readOnly" w:enforcement="1"/>
*
*/
public void enforceReadonlyProtection() {
settings.setEnforcementEditValue(STDocProtect.READ_ONLY);
}
/**
* Enforces the readOnly protection with a password.
*
* sample snippet from settings.xml
*
* <w:documentProtection w:edit="readOnly" w:enforcement="1"
* w:cryptProviderType="rsaAES" w:cryptAlgorithmClass="hash"
* w:cryptAlgorithmType="typeAny" w:cryptAlgorithmSid="14"
* w:cryptSpinCount="100000" w:hash="..." w:salt="...."
* />
*
*
* @param password the plaintext password, if null no password will be applied
* @param hashAlgo the hash algorithm - only md2, m5, sha1, sha256, sha384 and sha512 are supported.
* if null, it will default default to sha1
*/
public void enforceReadonlyProtection(String password, HashAlgorithm hashAlgo) {
settings.setEnforcementEditValue(STDocProtect.READ_ONLY, password, hashAlgo);
}
/**
* Enforce the Filling Forms protection.
* In the documentProtection tag inside settings.xml file,
* it sets the value of enforcement to "1" (w:enforcement="1")
* and the value of edit to forms (w:edit="forms")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="forms" w:enforcement="1"/>
*
*/
public void enforceFillingFormsProtection() {
settings.setEnforcementEditValue(STDocProtect.FORMS);
}
/**
* Enforce the Filling Forms protection.
*
* sample snippet from settings.xml
*
* <w:documentProtection w:edit="forms" w:enforcement="1"
* w:cryptProviderType="rsaAES" w:cryptAlgorithmClass="hash"
* w:cryptAlgorithmType="typeAny" w:cryptAlgorithmSid="14"
* w:cryptSpinCount="100000" w:hash="..." w:salt="...."
* />
*
*
* @param password the plaintext password, if null no password will be applied
* @param hashAlgo the hash algorithm - only md2, m5, sha1, sha256, sha384 and sha512 are supported.
* if null, it will default default to sha1
*/
public void enforceFillingFormsProtection(String password, HashAlgorithm hashAlgo) {
settings.setEnforcementEditValue(STDocProtect.FORMS, password, hashAlgo);
}
/**
* Enforce the Comments protection.
* In the documentProtection tag inside settings.xml file,
* it sets the value of enforcement to "1" (w:enforcement="1")
* and the value of edit to comments (w:edit="comments")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="comments" w:enforcement="1"/>
*
*/
public void enforceCommentsProtection() {
settings.setEnforcementEditValue(STDocProtect.COMMENTS);
}
/**
* Enforce the Comments protection.
*
* sample snippet from settings.xml
*
* <w:documentProtection w:edit="comments" w:enforcement="1"
* w:cryptProviderType="rsaAES" w:cryptAlgorithmClass="hash"
* w:cryptAlgorithmType="typeAny" w:cryptAlgorithmSid="14"
* w:cryptSpinCount="100000" w:hash="..." w:salt="...."
* />
*
*
* @param password the plaintext password, if null no password will be applied
* @param hashAlgo the hash algorithm - only md2, m5, sha1, sha256, sha384 and sha512 are supported.
* if null, it will default default to sha1
*/
public void enforceCommentsProtection(String password, HashAlgorithm hashAlgo) {
settings.setEnforcementEditValue(STDocProtect.COMMENTS, password, hashAlgo);
}
/**
* Enforce the Tracked Changes protection.
* In the documentProtection tag inside settings.xml file,
* it sets the value of enforcement to "1" (w:enforcement="1")
* and the value of edit to trackedChanges (w:edit="trackedChanges")
*
* sample snippet from settings.xml
*
* <w:settings ... >
* <w:documentProtection w:edit="trackedChanges" w:enforcement="1"/>
*
*/
public void enforceTrackedChangesProtection() {
settings.setEnforcementEditValue(STDocProtect.TRACKED_CHANGES);
}
/**
* Enforce the Tracked Changes protection.
*
* sample snippet from settings.xml
*
* <w:documentProtection w:edit="trackedChanges" w:enforcement="1"
* w:cryptProviderType="rsaAES" w:cryptAlgorithmClass="hash"
* w:cryptAlgorithmType="typeAny" w:cryptAlgorithmSid="14"
* w:cryptSpinCount="100000" w:hash="..." w:salt="...."
* />
*
*
* @param password the plaintext password, if null no password will be applied
* @param hashAlgo the hash algorithm - only md2, m5, sha1, sha256, sha384 and sha512 are supported.
* if null, it will default default to sha1
*/
public void enforceTrackedChangesProtection(String password, HashAlgorithm hashAlgo) {
settings.setEnforcementEditValue(STDocProtect.TRACKED_CHANGES, password, hashAlgo);
}
/**
* Validates the existing password
*
* @param password
* @return true, only if password was set and equals, false otherwise
*/
public boolean validateProtectionPassword(String password) {
return settings.validateProtectionPassword(password);
}
/**
* Remove protection enforcement.
* In the documentProtection tag inside settings.xml file
* it sets the value of enforcement to "0" (w:enforcement="0")
*/
public void removeProtectionEnforcement() {
settings.removeEnforcement();
}
/**
* Enforces fields update on document open (in Word).
* In the settings.xml file
* sets the updateSettings value to true (w:updateSettings w:val="true")
*
* NOTICES:
*
* - Causing Word to ask on open: "This document contains fields that may refer to other files. Do you want to update the fields in this document?"
* (if "Update automatic links at open" is enabled)
* - Flag is removed after saving with changes in Word
*
*/
public void enforceUpdateFields() {
settings.setUpdateFields();
}
/**
* Check if revision tracking is turned on.
*
* @return true
if revision tracking is turned on
*/
public boolean isTrackRevisions() {
return settings.isTrackRevisions();
}
/**
* Enable or disable revision tracking.
*
* @param enable true
to turn on revision tracking, false
to turn off revision tracking
*/
public void setTrackRevisions(boolean enable) {
settings.setTrackRevisions(enable);
}
/**
* Returns the current zoom factor in percent values, i.e. 100 is normal zoom.
*
* @return A percent value denoting the current zoom setting of this document.
*/
public long getZoomPercent() {
return settings.getZoomPercent();
}
/**
* Set the zoom setting as percent value, i.e. 100 is normal zoom.
*
* @param zoomPercent A percent value denoting the zoom setting for this document.
*/
public void setZoomPercent(long zoomPercent) {
settings.setZoomPercent(zoomPercent);
}
/**
* Returns the even-and-odd-headings setting
*
* @return True or false indicating whether or not separate even and odd headings is turned on.
*/
public boolean getEvenAndOddHeadings() {
return settings.getEvenAndOddHeadings();
}
/**
* Sets the even-and-odd-headings setting
* @param enable Set to true to turn on separate even and odd headings.
*/
public void setEvenAndOddHeadings(boolean enable) {
settings.setEvenAndOddHeadings(enable);
}
/**
* Returns the mirror margins setting
*
* @return True or false indicating whether or not mirror margins is turned on.
*/
public boolean getMirrorMargins() {
return settings.getMirrorMargins();
}
/**
* Sets the mirror margins setting
* @param enable Set to true to turn on mirror margins.
*/
public void setMirrorMargins(boolean enable) {
settings.setMirrorMargins(enable);
}
/**
* inserts an existing XWPFTable to the arrays bodyElements and tables
*
* @param pos
* @param table
*/
@Override
public void insertTable(int pos, XWPFTable table) {
bodyElements.add(pos, table);
int i = 0;
for (CTTbl tbl : ctDocument.getBody().getTblArray()) {
if (tbl == table.getCTTbl()) {
break;
}
i++;
}
tables.add(i, table);
}
/**
* Returns all Pictures, which are referenced from the document itself.
*
* @return a {@link List} of {@link XWPFPictureData}. The returned {@link List} is unmodifiable. Use #a
*/
public List getAllPictures() {
return Collections.unmodifiableList(pictures);
}
/**
* @return all Pictures in this package
*/
public List getAllPackagePictures() {
List result = new ArrayList<>();
Collection> values = packagePictures.values();
for (List list : values) {
result.addAll(list);
}
return Collections.unmodifiableList(result);
}
void registerPackagePictureData(XWPFPictureData picData) {
List list = packagePictures.computeIfAbsent(picData.getChecksum(), k -> new ArrayList<>(1));
if (!list.contains(picData)) {
list.add(picData);
}
}
XWPFPictureData findPackagePictureData(byte[] pictureData, int format) {
long checksum = IOUtils.calculateChecksum(pictureData);
XWPFPictureData xwpfPicData = null;
/*
* Try to find PictureData with this checksum. Create new, if none
* exists.
*/
List xwpfPicDataList = packagePictures.get(checksum);
if (xwpfPicDataList != null) {
Iterator iter = xwpfPicDataList.iterator();
while (iter.hasNext() && xwpfPicData == null) {
XWPFPictureData curElem = iter.next();
if (Arrays.equals(pictureData, curElem.getData())) {
xwpfPicData = curElem;
}
}
}
return xwpfPicData;
}
public String addPictureData(byte[] pictureData, int format) throws InvalidFormatException {
XWPFPictureData xwpfPicData = findPackagePictureData(pictureData, format);
POIXMLRelation relDesc = XWPFPictureData.RELATIONS[format];
if (xwpfPicData == null) {
/* Part doesn't exist, create a new one */
int idx = getNextPicNameNumber(format);
xwpfPicData = (XWPFPictureData) createRelationship(relDesc, XWPFFactory.getInstance(), idx);
/* write bytes to new part */
PackagePart picDataPart = xwpfPicData.getPackagePart();
try (OutputStream out = picDataPart.getOutputStream()) {
out.write(pictureData);
} catch (IOException e) {
throw new POIXMLException(e);
}
registerPackagePictureData(xwpfPicData);
pictures.add(xwpfPicData);
return getRelationId(xwpfPicData);
} else if (!getRelations().contains(xwpfPicData)) {
/*
* Part already existed, but was not related so far. Create
* relationship to the already existing part and update
* POIXMLDocumentPart data.
*/
// TODO add support for TargetMode.EXTERNAL relations.
RelationPart rp = addRelation(null, XWPFRelation.IMAGES, xwpfPicData);
return rp.getRelationship().getId();
} else {
/* Part already existed, get relation id and return it */
return getRelationId(xwpfPicData);
}
}
public String addPictureData(InputStream is, int format) throws InvalidFormatException {
try {
byte[] data = IOUtils.toByteArray(is);
return addPictureData(data, format);
} catch (IOException e) {
throw new POIXMLException(e);
}
}
/**
* get the next free ImageNumber
*
* @param format
* @return the next free ImageNumber
* @throws InvalidFormatException If the format of the picture is not known.
*/
public int getNextPicNameNumber(int format) throws InvalidFormatException {
int img = getAllPackagePictures().size() + 1;
String proposal = XWPFPictureData.RELATIONS[format].getFileName(img);
PackagePartName createPartName = PackagingURIHelper.createPartName(proposal);
while (this.getPackage().getPart(createPartName) != null) {
img++;
proposal = XWPFPictureData.RELATIONS[format].getFileName(img);
createPartName = PackagingURIHelper.createPartName(proposal);
}
return img;
}
/**
* returns the PictureData by blipID
*
* @param blipID
* @return XWPFPictureData of a specificID
*/
public XWPFPictureData getPictureDataByID(String blipID) {
POIXMLDocumentPart relatedPart = getRelationById(blipID);
if (relatedPart instanceof XWPFPictureData) {
return (XWPFPictureData) relatedPart;
}
return null;
}
/**
* getNumbering
*
* @return numbering
*/
public XWPFNumbering getNumbering() {
return numbering;
}
/**
* get Styles
*
* @return styles for this document
*/
public XWPFStyles getStyles() {
return styles;
}
@Override
public XWPFParagraph getParagraph(CTP p) {
for (XWPFParagraph paragraph : paragraphs) {
if (paragraph.getCTP() == p) {
return paragraph;
}
}
return null;
}
/**
* get a table by its CTTbl-Object
*
* @param ctTbl
* @return a table by its CTTbl-Object or null
* @see IBody#getTable(CTTbl)
*/
@Override
public XWPFTable getTable(CTTbl ctTbl) {
for (int i = 0; i < tables.size(); i++) {
if (getTables().get(i).getCTTbl() == ctTbl) {
return getTables().get(i);
}
}
return null;
}
public Iterator getTablesIterator() {
return tables.iterator();
}
public Iterator getParagraphsIterator() {
return paragraphs.iterator();
}
/**
* Returns the paragraph that of position pos
*
* @see IBody#getParagraphArray(int)
*/
@Override
public XWPFParagraph getParagraphArray(int pos) {
if (pos >= 0 && pos < paragraphs.size()) {
return paragraphs.get(pos);
}
return null;
}
/**
* returns the Part, to which the body belongs, which you need for adding relationship to other parts
* Actually it is needed of the class XWPFTableCell. Because you have to know to which part the tableCell
* belongs.
*
* @see IBody#getPart()
*/
@Override
public POIXMLDocumentPart getPart() {
return this;
}
/**
* get the PartType of the body, for example
* DOCUMENT, HEADER, FOOTER, FOOTNOTE,
*
* @see IBody#getPartType()
*/
@Override
public BodyType getPartType() {
return BodyType.DOCUMENT;
}
/**
* get the TableCell which belongs to the TableCell
*
* @param cell
*/
@Override
public XWPFTableCell getTableCell(CTTc cell) {
XmlCursor cursor = cell.newCursor();
cursor.toParent();
XmlObject o = cursor.getObject();
if (!(o instanceof CTRow)) {
return null;
}
CTRow row = (CTRow) o;
cursor.toParent();
o = cursor.getObject();
cursor.dispose();
if (!(o instanceof CTTbl)) {
return null;
}
CTTbl tbl = (CTTbl) o;
XWPFTable table = getTable(tbl);
if (table == null) {
return null;
}
XWPFTableRow tableRow = table.getRow(row);
if (tableRow == null) {
return null;
}
return tableRow.getTableCell(cell);
}
@Override
public XWPFDocument getXWPFDocument() {
return this;
}
/**
* This method is used to create template for chart XML
* no need to read MS-Word file and modify charts
*
* @return This method return object of XWPFChart Object with default height and width
* @throws InvalidFormatException
* @throws IOException
* @since POI 4.0.0
*/
public XWPFChart createChart() throws InvalidFormatException, IOException {
return createChart(XDDFChart.DEFAULT_WIDTH, XDDFChart.DEFAULT_HEIGHT);
}
/**
* This method is used to create template for chart XML
* no need to read MS-Word file and modify charts
*
* @param width width of chart in document
* @param height height of chart in document
* @return This method return object of XWPFChart
* @throws InvalidFormatException
* @throws IOException
* @since POI 4.0.0
*/
public XWPFChart createChart(int width, int height) throws InvalidFormatException, IOException {
return createChart(createParagraph().createRun(), width, height);
}
/**
*
* @param run in which the chart will be attached.
* @param width in EMU.
* @param height in EMU.
* @return the new chart.
* @throws InvalidFormatException
* @throws IOException
* @since POI 4.1.2
*/
public XWPFChart createChart(XWPFRun run, int width, int height) throws InvalidFormatException, IOException {
//get chart number
int chartNumber = getNextPartNumber(XWPFRelation.CHART, charts.size() + 1);
//create relationship in document for new chart
RelationPart rp = createRelationship(
XWPFRelation.CHART, XWPFFactory.getInstance(), chartNumber, false);
// initialize xwpfchart object
XWPFChart xwpfChart = rp.getDocumentPart();
xwpfChart.setChartIndex(chartNumber);
xwpfChart.attach(rp.getRelationship().getId(), run);
xwpfChart.setChartBoundingBox(width, height);
//add chart object to chart list
charts.add(xwpfChart);
return xwpfChart;
}
/**
* Create a new footnote and add it to the document.
*
* @return New XWPFFootnote.
* @since 4.0.0
*/
public XWPFFootnote createFootnote() {
XWPFFootnotes footnotes = this.createFootnotes();
XWPFFootnote footnote = footnotes.createFootnote();
return footnote;
}
/**
* Remove the specified footnote if present.
*
* @param pos Array position of the footnote to be removed.
* @return True if the footnote was removed.
* @since 4.0.0
*/
public boolean removeFootnote(int pos) {
if (null != footnotes) {
return footnotes.removeFootnote(pos);
} else {
return false;
}
}
/**
* Create a new end note and add it to the document.
*
* @return New {@link XWPFEndnote}.
* @since 4.0.0
*/
public XWPFEndnote createEndnote() {
XWPFEndnotes endnotes = this.createEndnotes();
XWPFEndnote endnote = endnotes.createEndnote();
return endnote;
}
public XWPFEndnotes createEndnotes() {
if (endnotes == null) {
EndnotesDocument endnotesDoc = EndnotesDocument.Factory.newInstance();
XWPFRelation relation = XWPFRelation.ENDNOTE;
int i = getRelationIndex(relation);
XWPFEndnotes wrapper = (XWPFEndnotes) createRelationship(relation, XWPFFactory.getInstance(), i);
wrapper.setEndnotes(endnotesDoc.addNewEndnotes());
wrapper.setIdManager(footnoteIdManager);
endnotes = wrapper;
}
return endnotes;
}
/**
* Gets the list of end notes for the document.
*
* @return List, possibly empty, of {@link XWPFEndnote}s.
*/
public List getEndnotes() {
if (endnotes == null) {
return Collections.emptyList();
}
return endnotes.getEndnotesList();
}
/**
* Remove the specified end note if present.
*
* @param pos Array position of the end note to be removed.
* @return True if the end note was removed.
* @since 4.0.0
*/
public boolean removeEndnote(int pos) {
if (null != endnotes) {
return endnotes.removeEndnote(pos);
} else {
return false;
}
}
}