Generate Inter-Document Hyperlink with Apache POI in Java - java

I'm generating an XWPFDocument with Apache POI (never used it before this) and I'd like to link one paragraph to another paragraph inside the same .docx document. Is this possible using POI's native functionality or do I need to deep-dive into XML Bean wrapper classes (i.e. CTP) to hand-jam this or am I out of luck? Every instance of a question regarding hyperlinks and POI that I have seen references creating either an external-type hyperlink or a link between Excel workbook sheets. I am as of now only able to generate a 'hyperlink' in the sense of ctrl-clicking the paragraph inside the finished document and it appears to simply do a text search starting from the top of the document. Here's the code I am currently using to achieve this. Thanks in advance!
public static void addInternalHyperlink(XWPFParagraph origin, String text, XWPFParagraph target) {
if (target != null) {
// Create the hyperlink itself
CTHyperlink link = origin.getCTP().addNewHyperlink();
link.setAnchor(target.getText());
// Create hyperlink text
CTText linkText = CTText.Factory.newInstance();
linkText.setStringValue(text);
CTR ctr = CTR.Factory.newInstance();
ctr.setTArray(new CTText[] {linkText});
// Format hyperlink text
CTFonts fonts = CTFonts.Factory.newInstance();
fonts.setAscii("Times New Roman");
CTRPr rpr = ctr.addNewRPr();
CTColor color = CTColor.Factory.newInstance();
color.setVal("0000FF");
rpr.setColor(color);
CTRPr rpr1 = ctr.addNewRPr();
rpr1.addNewU().setVal(STUnderline.SINGLE);
// Insert formatted text into link
link.setRArray(new CTR[] {ctr});
}
}
Please note that I'd like to use the 'origin' argument as the paragraph containing the actual link, the 'text' argument as the link text, and the 'target' argument as the actual link destination.
UPDATE: Here's an XML snippet containing a sample paragraph which I have linked to a section heading via the Word GUI.
<w:p w14:paraId="5B1C3A0C" w14:textId="659E388D" w:rsidR="00A4419C" w:rsidRDefault="00A4419C" w:rsidP="00A4419C"><w:hyperlink w:anchor="_Another_Heading" w:history="1"><w:r w:rsidRPr="00A4419C"><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr><w:t>Here is some stuff that could b</w:t></w:r><w:r w:rsidRPr="00A4419C"><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr><w:t>e</w:t></w:r><w:r w:rsidRPr="00A4419C"><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr><w:t xml:space="preserve"> the link</w:t></w:r></w:hyperlink></w:p><w:p w14:paraId="19996B78" w14:textId="5C39B081" w:rsidR="00A4419C" w:rsidRPr="00A4419C" w:rsidRDefault="00A4419C" w:rsidP="00A4419C"><w:pPr><w:pStyle w:val="Heading1"/></w:pPr><w:bookmarkStart w:id="0" w:name="_Another_Heading"/><w:bookmarkEnd w:id="0"/><w:r><w:t>Another Heading</w:t></w:r><w:bookmarkStart w:id="1" w:name="_GoBack"/><w:bookmarkEnd w:id="1"/></w:p>

The solution falls into two parts.
First we need a XWPFHyperlinkRun whose target is an anchor in the document.
Second we need that target anchor, which can be a bookmark in the document for example. So we need creating such bookmark in the document.
Unfortunately both is not supported using only high level classes of apache poi until now. So we need the low level classes form ooxml-schemas too.
The following code works using apache poi 4.0.0 together with ooxml-schemas-1.4.
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHyperlink;
import java.math.BigInteger;
public class CreateWordHyperlinkBookmark {
static XWPFHyperlinkRun createHyperlinkRunToAnchor(XWPFParagraph paragraph, String anchor) throws Exception {
CTHyperlink cthyperLink=paragraph.getCTP().addNewHyperlink();
cthyperLink.setAnchor(anchor);
cthyperLink.addNewR();
return new XWPFHyperlinkRun(
cthyperLink,
cthyperLink.getRArray(0),
paragraph
);
}
static XWPFParagraph createBookmarkedParagraph(XWPFDocument document, String anchor, int bookmarkId) {
XWPFParagraph paragraph = document.createParagraph();
CTBookmark bookmark = paragraph.getCTP().addNewBookmarkStart();
bookmark.setName(anchor);
bookmark.setId(BigInteger.valueOf(bookmarkId));
XWPFRun run = paragraph.createRun();
paragraph.getCTP().addNewBookmarkEnd().setId(BigInteger.valueOf(bookmarkId));
return paragraph;
}
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
String anchor = "hyperlink_target";
int bookmarkId = 0;
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("This is a text paragraph having ");
//create hyperlink run
XWPFHyperlinkRun hyperlinkrun = createHyperlinkRunToAnchor(paragraph, anchor);
hyperlinkrun.setText("a link to an bookmark anchor");
hyperlinkrun.setColor("0000FF");
hyperlinkrun.setUnderline(UnderlinePatterns.SINGLE);
run = paragraph.createRun();
run.setText(" in it.");
//some empty paragraphs
for (int i = 0; i < 10; i++) {
paragraph = document.createParagraph();
}
//create bookmarked paragraph as the hyperlink target
paragraph = createBookmarkedParagraph(document, anchor, bookmarkId++);
run = paragraph.getRuns().get(0);
run.setText("This is the target.");
FileOutputStream out = new FileOutputStream("CreateWordHyperlinkBookmark.docx");
document.write(out);
out.close();
document.close();
}
}

Related

How to center a paragraph vertically in Apache POI Java?

I am currently build a word formatter tool and have some problems with Apache POI.
I am using org.apache.poi version 5.2.0
Have a look at this picture:
I need it centered both horizontally and vertically.
Currently it only centers horizontally but not vertically.
And I dont know why. Here is the code I am using:
for (XWPFParagraph p : docx.getParagraphs()) {
System.out.println(p.getText());
boolean setBold = false;
boolean setItalic = false;
int fontSizeToSet = 16;
if (counter == 0) {
p.setAlignment(ParagraphAlignment.CENTER);
p.setVerticalAlignment(TextAlignment.CENTER);
}
if (counter == 1) {
p.setPageBreak(true);
}
List<XWPFRun> runs = p.getRuns();
if (runs != null) {
for (XWPFRun r : runs) {
if (counter == 0) {
r.setFontSize(14);
r.setBold(true);
r.setFontFamily("Bookman Old Style");
}
}
}
counter++;
}
What am I doing wrong?
XWPFParagraph.setVerticalAlignment is not made for vertical aligning a paragraph on the page. It sets the vertical alignment within the text line. This is similar to what vertical-align does in CSS. It only takes effect if the text line is higher than the single elements in that line. For example if there is text having various font sizes in one text line.
TextAlignment has following enum constants:
AUTO
Specifies that all text in the parent object shall be aligned automatically when displayed.
BASELINE
Specifies that all text in the parent object shall be aligned to the baseline of each character when displayed.
BOTTOM
Specifies that all text in the parent object shall be aligned to the bottom of each character when displayed.
CENTER
Specifies that all text in the parent object shall be aligned to the center of each character when displayed.
TOP
Specifies that all text in the parent object shall be aligned to the top of each character when displayed.
The following complete code sample shows the effect of the different text alignment settings.
To vertically center a paragraph (or more) on the page, those paragraphs must be on a single page. And there must be section properties set for this page. In section properties one then can set VAlign for the section above.
Unfortunately does apche poi not provide setting section properties up to now. So the low level org.openxmlformats.schemas.wordprocessingml.x2006.main.* classes must be used to achieve the same.
The folowing complete example also shows this. It puts a paragraph with section break next page for section above. So the first paragraph is on it's own page. Then it sets page vertical align to center for page above (section above).
import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.wp.usermodel.*;
public class CreateWordParagraphAndPageAlignment {
static void createSomeRichTextContent(XWPFParagraph paragraph) {
XWPFRun run = paragraph.createRun();
run.setText("Aligned ");
run.setFontSize(11);
run = paragraph.createRun();
run.setText("paragraph ");
run.setFontSize(22);
run = paragraph.createRun();
run.setText("having ");
run.setFontSize(33);
run = paragraph.createRun();
run.setText("various ");
run.setFontSize(22);
run = paragraph.createRun();
run.setText("font sizes");
run.setFontSize(11);
}
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("Default paragraph in first page, which has page vertical alignment set.");
run.setFontSize(44);
paragraph = document.createParagraph();
//paragraph with section break next page for section above
paragraph = document.createParagraph();
org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPr = paragraph.getCTP().addNewPPr().addNewSectPr();
ctSectPr.addNewType().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STSectionMark.NEXT_PAGE);
//set page vertical align center for page above (section above)
ctSectPr.addNewVAlign().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc.CENTER);
//page size setting (A4) for the section above
org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz ctPageSz = ctSectPr.addNewPgSz();
ctPageSz.setW(java.math.BigInteger.valueOf(Math.round(8.27d*72d*20d))); //A4 = 8.27" * 72 * 20 = Twips
ctPageSz.setH(java.math.BigInteger.valueOf(Math.round(11.69d*72d*20d))); //A4 = 11.69" * 72 * 20 = Twips
paragraph = document.createParagraph();
run = paragraph.createRun();
run.setText("Default paragraph");
paragraph = document.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setVerticalAlignment(TextAlignment.AUTO);
createSomeRichTextContent(paragraph);
paragraph = document.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setVerticalAlignment(TextAlignment.BASELINE);
createSomeRichTextContent(paragraph);
paragraph = document.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setVerticalAlignment(TextAlignment.BOTTOM);
createSomeRichTextContent(paragraph);
paragraph = document.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setVerticalAlignment(TextAlignment.CENTER);
createSomeRichTextContent(paragraph);
paragraph = document.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setVerticalAlignment(TextAlignment.TOP);
createSomeRichTextContent(paragraph);
paragraph = document.createParagraph();
// page size setting (A4) for the last section above must be at last in body
ctSectPr = document.getDocument().getBody().addNewSectPr();
ctPageSz = ctSectPr.addNewPgSz();
ctPageSz.setW(java.math.BigInteger.valueOf(Math.round(8.27d*72d*20d))); //A4 = 8.27" * 72 * 20 = Twips
ctPageSz.setH(java.math.BigInteger.valueOf(Math.round(11.69d*72d*20d))); //A4 = 11.69" * 72 * 20 = Twips
FileOutputStream out = new FileOutputStream("./CreateWordParagraphAndPageAlignment.docx");
document.write(out);
out.close();
document.close();
}
}

How to use the POI to cancel the automatic adjustment of the spacing between Chinese and western languages in word paragraphs

I use XWPFDocument and XWPFParagraph class to create a Word, But there is always a gap between Chinese and Western, I cancel the automatic adjustment of the spacing between Chinese and western languages in word paragraphs through WORD can solve the problem, but how can I do that useing code;
I think can use CTStyle, but I don't know how to do that;
XWPFDocument doc = new XWPFDocument();
XWPFParagraph page = doc.createParagraph();
XWPFRun runs = page.createRun();
runs.setBold(false);
runs.setFontFamily("宋体");
runs.setFontSize(9);
runs.setText("12H型");
//export
OutputStream output = response.getOutputStream();
BufferedOutputStream bufferedOutPut = new BufferedOutputStream(output);
bufferedOutPut.flush();
doc.write(bufferedOutPut);
bufferedOutPut.close();
I suspect you mean the settings described in Configure text spacing between East Asian and Latin text.
Those settings are stored in document.xml using the elements autoSpaceDEand autoSpaceDN in paragraph properties.
Using apache poi this could be done like so:
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
public class CreateWordEnglishAndChinese {
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
if (paragraph.getCTP().getPPr() == null) paragraph.getCTP().addNewPPr();
if (paragraph.getCTP().getPPr().getAutoSpaceDE() == null) paragraph.getCTP().getPPr().addNewAutoSpaceDE();
paragraph.getCTP().getPPr().getAutoSpaceDE().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff.OFF);
if (paragraph.getCTP().getPPr().getAutoSpaceDN() == null) paragraph.getCTP().getPPr().addNewAutoSpaceDN();
paragraph.getCTP().getPPr().getAutoSpaceDN().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff.OFF);
XWPFRun run = paragraph.createRun();
run.setText("12H型:Type 12H");
FileOutputStream out = new FileOutputStream("CreateWordEnglishAndChinese.docx");
document.write(out);
out.close();
document.close();
}
}

how create TextBox in a cell of table in document .docx using apache poi

I used Javafx and
I created a table using apache poi:
XWPFDocument document = new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFTable table = document.createTable(4, 3);
and created the paragraph like the paragraph below:
XWPFParagraph p1 = table.getRow(0).getCell(2).getParagraphs().get(0);
XWPFRun r1 = p1.createRun();
r1.setText(category_number.getText() + category.toString());
Now, I want create a TextBox in a one cell of a row but don't know how address Cell and Row to a textBox and set text and alignment textBox.
Please Help me ):
A text box in a *.docx is a shape in the document content. Creating shapes is not yet implemented in XWPF. But it can be done using the underlying ooxml-schemas classes.
Example:
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTxbxContent;
import com.microsoft.schemas.vml.CTGroup;
import com.microsoft.schemas.vml.CTShape;
import org.w3c.dom.Node;
public class CreateWordTextBoxInTable {
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(4, 3);
// table header row
for (int c = 0; c < 3; c++ ) {
paragraph = table.getRow(0).getCell(c).getParagraphArray(0);
if (paragraph == null) paragraph = table.getRow(0).getCell(c).addParagraph();
run = paragraph.createRun();
run.setText("Column " + (c+1));
}
// get run in cell for text box
XWPFTableCell cell = table.getRow(1).getCell(1);
paragraph = cell.getParagraphArray(0);
if (paragraph == null) paragraph = cell.addParagraph();
run = paragraph.createRun();
// create inline text box in run
// first crfeate group shape
CTGroup ctGroup = CTGroup.Factory.newInstance();
// now add shape to group shape
CTShape ctShape = ctGroup.addNewShape();
ctShape.setStyle("width:100pt;height:36pt");
// add text box content to shape
CTTxbxContent ctTxbxContent = ctShape.addNewTextbox().addNewTxbxContent();
XWPFParagraph textboxparagraph = new XWPFParagraph(ctTxbxContent.addNewP(), (IBody)cell);
textboxparagraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun textboxrun = textboxparagraph.createRun();
textboxrun.setText("The TextBox content...");
textboxrun.setFontSize(10);
// add group shape as picture to the run
Node ctGroupNode = ctGroup.getDomNode();
CTPicture ctPicture = CTPicture.Factory.parse(ctGroupNode);
CTR cTR = run.getCTR();
cTR.addNewPict();
cTR.setPictArray(0, ctPicture);
FileOutputStream out = new FileOutputStream("test.docx");
document.write(out);
out.close();
}
}
This code was tested using apache poi 4.0.1 and needs the ooxml-schemas-1.4.jar in class path.

insert header(left, center & right) in ms word using apache poi 3.11

I need to insert header like the screenshot below in word document using apache poi
I have a code to insert the header. But it aligned to left like the screenshot below:
I used below code to insert header:
// write header content
XWPFDocument docx = new XWPFDocument();
CTSectPr sectPr = docx.getDocument().getBody().addNewSectPr();
XWPFHeaderFooterPolicy policy = new XWPFHeaderFooterPolicy(docx,sectPr);
CTP ctpHeader = CTP.Factory.newInstance();
CTR ctrHeader = ctpHeader.addNewR();
CTText ctHeader = ctrHeader.addNewT();
String headerText = "This is header";
ctHeader.setStringValue(headerText);
XWPFParagraph headerParagraph = new XWPFParagraph(ctpHeader, docx);
XWPFParagraph[] parsHeader = new XWPFParagraph[1];
parsHeader[0] = headerParagraph;
policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT, parsHeader);
FileOutputStream out = new FileOutputStream("D:/giri.docx");
docx.write(out);
out.close();
System.out.println("Done");
XWPFHeader header=policy.getDefaultHeader();
CTP ctpHeader = CTP.Factory.newInstance();
CTR ctrHeader = ctpHeader.addNewR();
CTText ctheader = ctrHeader.addNewT();
String HeaderText = "TSS Word Documentsss";
//ctheader.set(HeaderText);
XWPFParagraph headerParagraph = new XWPFParagraph(ctpHeader, document);
XWPFRun headerRun= headerParagraph.createRun();
headerRun.setBold(true);
headerRun.setFontSize(39);
headerRun.setColor("808000");
headerRun.setImprinted(true);
headerRun.setShadow(true);
headerRun.setCapitalized(true);
headerRun.setUnderline(UnderlinePatterns.DOT_DOT_DASH);
headerRun.setText(HeaderText);
headerParagraph.setAlignment(ParagraphAlignment.CENTER);
headerParagraph.setBorderBottom(Borders.BASIC_BLACK_DASHES);
XWPFParagraph[] parsHeader = new XWPFParagraph[1];
parsHeader[0] = headerParagraph;
policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT, parsHeader);
Unlike Excel, Word doesn't have left, centre and right headers - Word has a header. If you want three separate entries on a single line, you can format a paragraph with the appropriate alignment (e.g. centred/justified) and tab-stops (possibly 3 centred, or one each of left, centre and right), then insert tab characters into the text you're inserting. Alternatively, for multi-line inputs especially, you could insert a 3-column table with the appropriate cell formatting and send the outputs to the relevant cells.
In VBA, you might add a table to the page header using code like:
With ActiveDocument
.Tables.Add Range:=.Sections.First.Headers(wdHeaderFooterPrimary).Range, NumRows:=1, NumColumns:=3, AutoFitBehavior:=wdAutoFitFixed
End With
I'll leave it to you to do the C# conversion.

Apache POI Word using custom styles for titles

I am trying to create heading titles in a word (.docx) document, using apache-poi.
I have a template which contains only custom styles AND example of heading titles using the custom styles.
XWPFDocument document=new XWPFDocument(new FileInputStream("template.docx"));
My custom style is called "CUSTOM_YNP" (I created it directly in Word), but when I use the line below, it returns false
document.getStyles().styleExist("CUSTOM_YNP")
And, of course, when I try to use this style, it doesn't work, actually it print my string in "Normal" style
XWPFParagraph paragraph=document.createParagraph();
paragraph.setStyle("CUSTOM_YNP");
XWPFRun run=paragraph.createRun();
run.setText("TEST");
Just for the record, my "save document" line :
document.write(new FileOutputStream("myDoc.docx"));
I have read this question, but can't actually find a solution to my problem... How can I use predefined formats in DOCX with POI?
EDIT : It works if I create my own style using Apache-POI.... Still I woudl really like to use existing styles from the word document.
A *.docx is a ZIP archive. You can unzip it and look into the /word/styles.xml. There you will see that the w:styleId="CUSTOMYNP" without the underscore. The name is "CUSTOM_YNP" <w:name w:val="CUSTOM_YNP"/>. So:
XWPFDocument document = new XWPFDocument(new FileInputStream("template.docx"));
System.out.println(document.getStyles().styleExist("CUSTOMYNP"));
System.out.println(document.getStyles().getStyle("CUSTOMYNP").getName());
XWPFParagraph paragraph=document.createParagraph();
paragraph.setStyle("CUSTOMYNP");
XWPFRun run=paragraph.createRun();
run.setText("TEST");
document.write(new FileOutputStream("myDoc.docx"));
document.close();
Make sure you first create the Style and add it to your document:
XWPFDocument document = new XWPFDocument();
XWPFStyles styles = document.createStyles();
String heading1 = "My Heading 1";
addCustomHeadingStyle(document, styles, heading1, 1, 36, "4288BC");
XWPFParagraph paragraph = document.createParagraph();
paragraph.setStyle(heading1);
With the addCustomHeadingStyle being:
private static void addCustomHeadingStyle(XWPFDocument docxDocument, XWPFStyles styles, String strStyleId, int headingLevel, int pointSize, String hexColor) {
CTStyle ctStyle = CTStyle.Factory.newInstance();
...
//create your style
...
XWPFStyle style = new XWPFStyle(ctStyle);
style.setType(STStyleType.PARAGRAPH);
styles.addStyle(style);
}

Categories