kc10boom
kc10boom

Reputation: 13

Generate Inter-Document Hyperlink with Apache POI in 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>

Upvotes: 1

Views: 2615

Answers (1)

Axel Richter
Axel Richter

Reputation: 61852

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();

 }
}

Upvotes: 4

Related Questions