Duplicate Table Paragraphs in Docx created with Apache POI - java

I'm using Apache POI in order to create a docx containing a table.
In order to format the table, I'm adding paragraphs to the cell, using this method:
private XWPFParagraph getTableParagraph(XWPFDocument document, XWPFParagraph paragraph, String text, boolean bold, boolean wrap, boolean allineaDx){
if (paragraph == null) paragraph = document.createParagraph();
XWPFRun p2run = paragraph.createRun();
p2run.setText(text);
p2run.setFontSize(5);
p2run.setBold(bold);
if (wrap) paragraph.setWordWrap(wrap);
if (allineaDx) paragraph.setAlignment(ParagraphAlignment.RIGHT);
return paragraph;
}
and I call the method with:
XWPFTableRow tableOneRowOne = tableOne.getRow(0);
tableOneRowOne.getCell(0).setParagraph(getTableParagraph(document, tableOneRowOne.getCell(0).getParagraphArray(0), "some text", true, true, false));
the table comes out as desired, but all the paragraphs created and inserted in the cells are also visible at the end of the table. Why? How can I prevent this?

problem solved
the duplication was caused by document.createParagraph().
i changed the method into this:
private XWPFParagraph getTableParagraph(XWPFTableCell cell, String text, boolean bold, boolean wrap, boolean allineaDx) throws Exception{
XWPFParagraph paragraph = cell.addParagraph();
cell.removeParagraph(0);
XWPFRun p2run = paragraph.createRun();
p2run.setText(text);
p2run.setFontSize(5);
p2run.setBold(bold);
if (wrap) paragraph.setWordWrap(wrap);
if (allineaDx) paragraph.setAlignment(ParagraphAlignment.RIGHT);
return paragraph;
}
and now everything works just fine. Please note the cell.removeParagraph(0)
Cells come with a null paragraph on their own, and adding a new paragraph ends up in having duplicated paragraph inside the cell. Removing the original paragraph works fine.

Related

How to allow more rows to be created in apache POI with read only protection?

I'm working creating a docx file which needs to be readonly protected except in some fields. For those fields I have solved it with this:
pInit.getCTP().addNewPermStart();
...
pEnd.getCTP().addNewPermEnd();
But now I want to allow adding new rows to a XWPFTable, but this line:
document.enforceReadonlyProtection(wordLockedPass, HashAlgorithm.sha1);
block this functionality and I don't know what to do.
Thanks in advance!
If the table is in an editable region (between PermStart and PermEnd) then it will be editable including adding rows.
The PermStart and PermEnd can be inserted in document body using
// CTPermStart marking the start of unprotected range
CTPermStart ctPermStart = document.getDocument().getBody().addNewPermStart();
ctPermStart.setEdGrp(STEdGrp.EVERYONE);
ctPermStart.setId("123456"); //note the Id
and
// CTPerm marking the end of unprotected range
document.getDocument().getBody().addNewPermEnd().setId("123456"); //note the same Id
All body elements between this PermStart and PermEnd are editable in a protected document. That is true also for tables between this PermStart and PermEnd.
If only parts of tables shall be editable, then PermStart and PermEnd also can be inserted within the table. For example if only the last row of a table shall be editable and new rows shall be insertable, then:
XWPFTable table = ...;
...
// CTPermStart marking the start of unprotected range
ctPermStart = table.getCTTbl().addNewPermStart();
ctPermStart.setEdGrp(STEdGrp.EVERYONE);
ctPermStart.setId("789012"); //note the Id
XWPFTableRow row = table.createRow();
// CTPerm marking the end of unprotected range
table.getCTTbl().addNewPermEnd().setId("789012"); //note the same Id
...
Complete example:
import java.io.*;
import org.apache.poi.wp.usermodel.*;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPermStart;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STEdGrp;
public class CreateWordPartialProtected {
public static void main(String[] args) throws Exception {
XWPFDocument document= new XWPFDocument();
// create header
XWPFHeader header = document.createHeader(HeaderFooterType.DEFAULT);
XWPFParagraph paragraph = header.createParagraph();
paragraph.setAlignment(ParagraphAlignment.LEFT);
XWPFRun run = paragraph.createRun();
run.setText("The page header:");
// create footer
XWPFFooter footer = document.createFooter(HeaderFooterType.DEFAULT);
paragraph = footer.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
run = paragraph.createRun();
run.setText("Page ");
paragraph.getCTP().addNewFldSimple().setInstr("PAGE \\* MERGEFORMAT");
run = paragraph.createRun();
run.setText(" of ");
paragraph.getCTP().addNewFldSimple().setInstr("NUMPAGES \\* MERGEFORMAT");
// the body content
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("This body part is protected.");
paragraph = document.createParagraph();
// CTPermStart marking the start of unprotected range
CTPermStart ctPermStart = document.getDocument().getBody().addNewPermStart();
ctPermStart.setEdGrp(STEdGrp.EVERYONE);
ctPermStart.setId("123456"); //note the Id
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("This body part is not protected.");
// CTPerm marking the end of unprotected range
document.getDocument().getBody().addNewPermEnd().setId("123456"); //note the same Id
paragraph = document.createParagraph();
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("This body part is protected again.");
paragraph = document.createParagraph();
XWPFTable table = document.createTable(1, 3);
table.setWidth("100%");
table.getRow(0).getCell(0).setText("Column 1");
table.getRow(0).getCell(1).setText("Column 2");
table.getRow(0).getCell(2).setText("Column 3");
// CTPermStart marking the start of unprotected range
ctPermStart = table.getCTTbl().addNewPermStart();
ctPermStart.setEdGrp(STEdGrp.EVERYONE);
ctPermStart.setId("789012"); //note the Id
XWPFTableRow row = table.createRow();
// CTPerm marking the end of unprotected range
table.getCTTbl().addNewPermEnd().setId("789012"); //note the same Id
paragraph = document.createParagraph();
paragraph = document.createParagraph();
run=paragraph.createRun();
run.setText("This body part is protected again.");
paragraph = document.createParagraph();
document.enforceReadonlyProtection("passwd", org.apache.poi.poifs.crypt.HashAlgorithm.sha1); //enforce readonly protection
FileOutputStream out = new FileOutputStream("CreateWordPartialProtected.docx");
document.write(out);
out.close();
document.close();
}
}
This code is tested and works using apache poi 5.2.2. It produces a Word document having two editable regions. The second is in a table after the title row. So that title row is protected. The last row is in that editable region. So this row is editable and new rows can be inserted above and below that row. All this tested in Microsoft Word 2017 and Microsoft Word 365. Other text-processing applications, which are able to handle *.docx files, might not respect editable regions.

apache poi disable default footer for the first page

I am trying to create a word document in which I will have no footer only in the first page and a footer for the rest of the pages. I wrote the following code (I also tried to change -reverse- the order of creation of footer and footerFirst objects) but that did not help. I still have the default footer on all pages.
How should I disable the footer from the first page? Thanks in advance.
private XWPFDocument initDocument(String FILE) throws Exception{
XWPFDocument document = new XWPFDocument();
XWPFHeaderFooterPolicy headerFooterPolicy = document.getHeaderFooterPolicy();
if (headerFooterPolicy == null) headerFooterPolicy = document.createHeaderFooterPolicy();
// create header start
XWPFFooter footer = headerFooterPolicy.createFooter(XWPFHeaderFooterPolicy.DEFAULT);
//XWPFParagraph paragraph = footer.createParagraph();
XWPFParagraph paragraph = footer.getParagraphArray(0);
if (paragraph == null)
paragraph = footer.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = paragraph.createRun();
run.setFontSize(11);
run.setFontFamily("Times New Roman");
run.setText("Some company info in the footer");
XWPFFooter footerFirst = headerFooterPolicy.createFooter(XWPFHeaderFooterPolicy.FIRST);
paragraph = footerFirst.getParagraphArray(0);
if (paragraph == null)
paragraph = footerFirst.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
run = paragraph.createRun();
run.setText(" ");
return document;
}
That there is a different header set for first page only, means not that this header will also be shown. In Word GUI there is a checkbox [x] Different First Page in Header & Footer Tools to achieve that.
And according Office Open XML Part 4 - Markup Language Reference there must a boolean XML element titlePg be set to determine that there is a title page present.
In old apache poi versions using XWPFHeaderFooterPolicy this XML element titlePg can only be set using underlying low level objects using document.getDocument().getBody().getSectPr().addNewTitlePg();.
But using current apache poi versions (since 3.16) there is no need using XWPFHeaderFooterPolicy directly. Now there is XWPFDocument.createHeader and XWPFDocument.createFooter using a HeaderFooterType. This sets titlePg flag in XML when HeaderFooterType.FIRST is used.
Complete example which sets and uses HeaderFooterType.FIRST and HeaderFooterType.DEFAULT:
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.wp.usermodel.HeaderFooterType;
public class CreateWordFooters {
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
// the body content
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("The Body... first page");
paragraph = document.createParagraph();
run=paragraph.createRun();
run.addBreak(BreakType.PAGE);
run.setText("The Body... second page");
// create first page footer
XWPFFooter footer = document.createFooter(HeaderFooterType.FIRST);
paragraph = footer.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
run = paragraph.createRun();
run.setText("First page footer...");
// create default footer
footer = document.createFooter(HeaderFooterType.DEFAULT);
paragraph = footer.createParagraph();
paragraph.setAlignment(ParagraphAlignment.LEFT);
run = paragraph.createRun();
run.setText("Default footer...");
FileOutputStream out = new FileOutputStream("CreateWordFooters.docx");
document.write(out);
out.close();
document.close();
}
}

Center and bold the contents of table cells in Apache POI Word document

How can I center and bold the content of the table cells using Apache POI in a Word document? This is the code I use to build the table:
XWPFDocument document = new XWPFDocument();
XWPFTable table = document.createTable();
XWPFTableRow tableRowOne = table.getRow(0);
tableRowOne.getCell(0).setText("CUSTOMER_NAME");
tableRowOne.addNewTableCell().setText("Kumar");
tableRowOne.addNewTableCell().setText("CUSTOMER_ID");
tableRowOne.addNewTableCell().setText("123");
XWPFTableRow tableRowTwo = table.createRow();
tableRowTwo.getCell(0).setText("AGE_GENDER");
tableRowTwo.getCell(1).setText("25/M");
tableRowTwo.getCell(2).setText("VISIT_DATE");
tableRowTwo.getCell(3).setText("11/02/2021");
XWPFTableRow tableRowThree = table.createRow();
tableRowThree.getCell(0).setText("REFERRED_BY");
tableRowThree.getCell(1).setText("Self");
In Word text formatting is stored in text runs XWPFRun. Paragraph alignment is stored in paragraphs XWPFParagraph. This also is true for tables. So you need get XWPFParagraphs from XWPFTableCell and then XWPFRuns from the paragraphs. Then you can set paragraph alignment and text formatting.
See XWPFTableCell for methods to get XWPFParagraphs.
Complete Example:
import java.io.File;
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
public class CreateWordTable {
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("The table:");
XWPFTable table = document.createTable();
table.setWidth("100%");
XWPFTableRow tableRow = table.getRow(0);
tableRow.getCell(0).setText("CUSTOMER_NAME");
tableRow.getCell(0).getParagraphs().get(0).getRuns().get(0).setBold(true);
tableRow.addNewTableCell().setText("Kumar");
tableRow.getCell(1).getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
tableRow.addNewTableCell().setText("CUSTOMER_ID");
tableRow.getCell(2).getParagraphs().get(0).getRuns().get(0).setBold(true);
tableRow.addNewTableCell().setText("123");
tableRow.getCell(3).getParagraphs().get(0).setAlignment(ParagraphAlignment.RIGHT);
tableRow = table.createRow();
tableRow.getCell(0).setText("AGE_GENDER");
tableRow.getCell(0).getParagraphs().get(0).getRuns().get(0).setBold(true);
tableRow.getCell(1).setText("25/M");
tableRow.getCell(1).getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
tableRow.getCell(2).setText("VISIT_DATE");
tableRow.getCell(2).getParagraphs().get(0).getRuns().get(0).setBold(true);
tableRow.getCell(3).setText("11/02/2021");
tableRow.getCell(3).getParagraphs().get(0).setAlignment(ParagraphAlignment.RIGHT);
tableRow = table.createRow();
tableRow.getCell(0).setText("REFERRED_BY");
tableRow.getCell(0).getParagraphs().get(0).getRuns().get(0).setBold(true);
tableRow.getCell(1).setText("Self");
tableRow.getCell(1).getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
tableRow.getCell(2).setText("");
tableRow.getCell(3).setText("");
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("CreateWordTable.docx");
document.write(out);
out.close();
document.close();
}
}
Note: This works using current apache poi 5.0.0. Former versions had bugs in XWPFTableCell.setText so paragraphs and runs were not present after XWPFTableCell.setText was called.

How to add a hyperlink to a XWPFRun

I want to format the text of a XWPF Run as a hyperlink. I am able to add it to the paragraph with the code given below but the adds it in a separate line.
public static void appendExternalHyperlink(String url, String text, XWPFParagraph paragraph){
//Add the link as External relationship
String id=paragraph.getDocument().getPackagePart().addExternalRelationship(url, XWPFRelation.HYPERLINK.getRelation()).getId();
//Append the link and bind it to the relationship
CTHyperlink cLink=paragraph.getCTP().addNewHyperlink();
cLink.setId(id);
//Create the linked text
CTText ctText=CTText.Factory.newInstance();
ctText.setStringValue(text);
CTR ctr=CTR.Factory.newInstance();
ctr.setTArray(new CTText[]{ctText});
CTRPr rpr = ctr.addNewRPr();
CTColor colour = CTColor.Factory.newInstance();
colour.setVal("0000FF"); rpr.setColor(colour);
CTRPr rpr1 = ctr.addNewRPr(); rpr1.addNewU().setVal(STUnderline.SINGLE);
//Insert the linked text into the link
cLink.setRArray(new CTR[]{ctr});
}
And I invoke it like:
XWPFParagraph eduPara = doc.createParagraph();
eduPara.setAlignment(ParagraphAlignment.LEFT);
eduPara.setVerticalAlignment(TextAlignment.TOP);
XWPFRun eduRun7 = eduPara.createRun();
appendExternalHyperlink(center.getEduImpFile(), center.getEduImpFile(), eduPara);
eduRun7.addBreak();
Here center is an object that holds the values I need to print.The get functions give output in String format.
The output I get is as follows:
Program Output
I want the hyperlink to be in the same line as the previous run generating the text "File uploaded:"
This was a mistake on my part as it was going to the next-line because there was not enough space to place the line.

Updating the text of a XWPFParagraph using Apache POI

I have been able to loop through all paragraphs in a document and get at the text and everything and I have read and understood how you can create a document from scratch. But how can I update and replace the text in a paragraph? I can do createRun in a paragraph but that will just create a new piece of text in it.
...
FileInputStream fis = new FileInputStream("Muu.docx");
XWPFDocument myDoc = new XWPFDocument(fis);
XWPFParagraph[] myParas = myDoc.getParagraphs();
...
My theory is that I need to get at the existing "run" in the paragraph I want to change, or delete the paragraph and add it again) but I cannot find methods to do that.
You can't change the text on a XWPFParagraph directly. A XWPFParagraph is made up of one or more XWPFRun instances. These provide the way to set the text.
To change the text, your code would want to be something like:
public void changeText(XWPFParagraph p, String newText) {
List<XWPFRun> runs = p.getRuns();
for(int i = runs.size() - 1; i > 0; i--) {
p.removeRun(i);
}
XWPFRun run = runs.get(0);
run.setText(newText, 0);
}
That will ensure you only have one text run (the first one), and will replace all the text to be what you provided.

Categories

Resources