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.
Related
I am unable to add text containing blank lines as separate paragraphs to a word document.
If I try to add the following text that contains 3 different paragraphs.
Some text here.
Another text here.
Another one here.
what I get is 1. Some text here. 2. Another text here. 3. Another one here. as if they were the same paragraph.
Is it possible to add a text containing blank lines as separate paragraphs to a Word document using Apache POI?
public static void addingMyParagraphs(XWPFDocument doc, String text) throws InvalidFormatException, IOException {
XWPFParagraph p = doc.createParagraph();
XWPFRun run = p.createRun();
run.setText(text);
run.setFontFamily("Times new Roman");
}
--In the method below MyText variable is a textArea variable that's part of a javaFx application.
public void CreatingDocument() throws IOException, InvalidFormatException {
String theText = myText.getText();
addingMyParagraphs(doc, theText);
FileOutputStream output = new FileOutputStream("MyDocument.docx");
doc.write(output);
output.close();
}
}
You need to split your text into "paragraphs" and add each paragraph separately to your WORD document. This has nothing to do with JavaFX.
Here is an example that uses text blocks to simulate the text that is entered into the [JavaFX] TextArea. Explanations after the code.
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
public class PoiWord0 {
public static void main(String[] args) {
String text = """
1. Some text here.
2. Another text here.
3. Another one here.
""";
String[] paras = text.split("(?m)^[ \\t]*\\r?\\n");
try (XWPFDocument doc = new XWPFDocument();
FileOutputStream output = new FileOutputStream("MyDocument.docx")) {
for (String para : paras) {
XWPFParagraph p = doc.createParagraph();
XWPFRun run = p.createRun();
run.setText(para.stripTrailing());
}
doc.write(output);
}
catch (IOException xIo) {
xIo.printStackTrace();
}
}
}
I assume that a paragraph delimiter is a blank line, so I split the text on the blank lines. This still leaves the trailing newline character in each element of the array. I use stripTrailing() to remove that newline.
Now I have an array of paragraphs, so I simply add a new paragraph to the [WORD] document for each array element.
Note that the above code was written using JDK 15.
The regex for splitting the text came from the SO question entitled Remove empty line from a multi-line string with Java
try-with-resources was added in Java 7.
stripTrailing() was added in JDK 11
I am trying to work with apache poi for docx format file and I am stuck at using formulas in table. For instance see the image :
I did try setting text to "=SUM(ABOVE)" but it doesnt work this way.
I think I might need to set custom xml data here but I am not sure how to proceed. I tried following piece of code :
XWPFTable table = document.createTable();
//create first row
XWPFTableRow tableRowOne = table.getRow(0);
table.getRow(0).createCell();
table.getRow(0).getCell(0).setText("10");
table.getRow(0).createCell();
table.getRow(0).getCell(1).setText("=SUM(ABOVE)");
What I am doing in case of such requirements is as follows:
First, creating the simplest possible Word document having the required things in it using the Word GUI. Then have a look into what Word has created to get a idea what needs to be created using apache poi.
In concrete here:
Do creating the simplest possible table in Word which has a field {=SUM(ABOVE)} in it. Save that as *.docx. Now unzip that *.docx (Office Open XML files like *.docx are simply ZIP archive). Have a look at /word/document.xml in that archive. There you will find something like:
<w:tc>
<w:p>
<w:fldSimple w:instr="=SUM(ABOVE)"/>
...
</w:p>
</w:tc>
This is XML for a table cell having a paragraph having a fldSimple element in it where instr attribute contains the formula.
Now we know, we need the table cell XWPFTableCell and the XWPFParagraph in it. Then we need set a fldSimple element in this paragaraph where instr attribute contains the formula.
This would be as simple as
paragraphInCell.getCTP().addNewFldSimple().setInstr("=SUM(ABOVE)");
But of course something must tell Word the need to calculate the formula when the document opens. The simplest solution for this is setting the field "dirty". That leads to the need for updating the field while opening the document in Word. It also leads to a confirming message dialog about the need for updating.
Complete example using apache poi 4.1.0:
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
public class CreateWordTableSumAbove {
public static void main(String[] args) throws Exception {
XWPFDocument document= new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The table:");
//create the table
XWPFTable table = document.createTable(4,3);
table.setWidth("100%");
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
if (col < 2) table.getRow(row).getCell(col).setText("row " + row + ", col " + col);
else table.getRow(row).getCell(col).setText("" + ((row + 1) * 1234));
}
}
//set Sum row
table.getRow(3).getCell(0).setText("Sum:");
//get paragraph from cell where the sum field shall be contained
XWPFParagraph paragraphInCell = null;
if (table.getRow(3).getCell(2).getParagraphs().size() == 0) paragraphInCell = table.getRow(3).getCell(2).addParagraph();
else paragraphInCell = table.getRow(3).getCell(2).getParagraphs().get(0);
//set sum field in
CTSimpleField sumAbove = paragraphInCell.getCTP().addNewFldSimple();
sumAbove.setInstr("=SUM(ABOVE)");
//set sum field dirty, so it must be calculated while opening the document
sumAbove.setDirty(STOnOff.TRUE);
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("create_table.docx");
document.write(out);
out.close();
document.close();
}
}
That all only works properly when the document is opened using Microsoft Word. LibreOffice Writer is not able storing such formula fields into Office Open XML (*.docx) format nor is it able reading such Office Open XML formula fields properly.
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.
I have a file, "template.docx" that I would like to have placeholders (ie. [serial number]) that can be replaced with a string or maybe a table. I am using Apache POI and no i cannot use docx4j.
Is there a way to have the program iterate over all occurrences of "[serial number]" and replace them with a string? Many of these tags will be inside a large table so is there some equivalent command with the Apache POI to just pressing ctrl+f in word and using replace all?
Any suggestions would be appreciated, thanks
XWPFDocument (docx) has different kind of sub-elements like XWPFParagraphs, XWPFTables, XWPFNumbering etc.
Once you create XWPFDocument object via:
document = new XWPFDocument(inputStream);
You can iterate through all of Paragraphs:
document.getParagraphsIterator();
When you iterator through Paragraphs, For each Paragraph you will get multiple XWPFRuns which are multiple text blocks with same styling, some times same styling text blocks will be split into multiple XWPFRuns in which case you should look into this question to avoid splitting of your Runs, doing so will help identify your placeHolders without merging multiple Runs within same Paragraph. At this point you should expect that your placeHolder will not be split in multiple runs if that's the case then you can go ahead and Iterate over 'XWPFRun's for each paragraph and look for text matching your placeHolder, something like this will help:
XWPFParagraph para = (XWPFParagraph) xwpfParagraphElement;
for (XWPFRun run : para.getRuns()) {
if (run.getText(0) != null) {
String text = run.getText(0);
Matcher expressionMatcher = expression.matcher(text);
if (expressionMatcher.find() && expressionMatcher.groupCount() > 0) {
System.out.println("Expression Found...");
}
}
}
Where expressionMatcher is Matcher based on a RegularExpression for particular PlaceHolder. Try having regex that matches something optional before your PlaceHolder and after as well e.g \([]*)(PlaceHolderGroup)([]*)^, trust me it works best.
Once you find the right XWPFRun extract text of your interest in it and create a replacement text which should be easy enough, then you should replace new text with previous text in this particular run by:
run.setText(text, 0);
If you were to replace this whole XWPFRun with a completely a new XWPFRun or perhaps insert a new Paragraph/Table after the Paragraph owning this run, you would probably run into a few problems, like A. ConcurrentModificationException which means you cannot modify this List(of XWPFRuns) you are iterating and B. finding the position of new Element to insert. To resolve these issues you should have a List<XWPFParagraph> of XWPFParagarphs that can hold paras after which new Element is to be inserted. Once you have your List of replacement you can iterator over it and for each replacement Paragraph you simply get a cursor and insert new element at that cursor:
for (XWPFParagraph para: paras) {
XmlCursor cursor = (XmlCursor) para.getCTP().newCursor();
XWPFTable newTable = para.getBody().insertNewTbl(cursor);
//Generate your XWPF table based on what's inside para with your own logic
}
To create an XWPFTable, read this.
Hope this helps someone.
// Text nodes begin with w:t in the word document
final String XPATH_TO_SELECT_TEXT_NODES = "//w:t";
try {
// Open the input file
String fileName="test.docx";
String[] splited=fileName.split(".");
File dir=new File("D:\\temp\\test.docx");
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new FileInputStream(dir));
// Build a list of "text" elements
List<?> texts = wordMLPackage.getMainDocumentPart().getJAXBNodesViaXPath(XPATH_TO_SELECT_TEXT_NODES, true);
HashMap<String, String> mappings = new HashMap<String, String>();
mappings.put("1", "one");
mappings.put("2", "two");
// Loop through all "text" elements
Text text = null;
for (Object obj : texts) {
text = (Text) ((JAXBElement<?>) obj).getValue();
String textToReplace = text.getValue();
if (mappings.keySet().contains(textToReplace)) {
text.setValue(mappings.get(textToReplace));
}
}
wordMLPackage.save(new java.io.File("D:/temp/forPrint.docx"));//your path
} catch (Exception e) {
}
}
}
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.