Apr 11 2012

docx – internal hyperlinks

There have been a couple of posts on the forum lately regarding adding hyperlinks to other parts of a docx.

This blog post walks you through the generic process for investigating an issue like this.

First, create a sample docx in Word which exhibits the issue of interest.

Here I’m interested in hyperlinks to a heading, and to a bookmark. So see this docx. Second, look inside it (its a zip file). For the link to the heading, document.xml contains a w:p containing:

 
      <w:hyperlink w:anchor="_My_heading" w:history="1">
        <w:r>
          <w:rPr>
            <w:rStyle w:val="Hyperlink"/>
          </w:rPr>
          <w:t>My heading</w:t>
        </w:r>
      </w:hyperlink>

The heading itself is automatically given a bookmark:

    <w:p>
      <w:pPr>
        <w:pStyle w:val="Heading1"/>
      </w:pPr>
      <w:bookmarkStart w:id="0" w:name="_My_heading"/>
      <w:bookmarkEnd w:id="0"/>
      <w:r>
        <w:t>My heading</w:t>
      </w:r>
    </w:p> 

For the link to my bookmark, Word 2010 used the legacy field formulation:

    <w:p>
      <w:r>
        <w:fldChar w:fldCharType="begin"/>
      </w:r>
      <w:r>
        <w:instrText xml:space="preserve"> HYPERLINK  \l "bm1" </w:instrText>
      </w:r>
      <w:r>
        <w:fldChar w:fldCharType="separate"/>
      </w:r>
      <w:r w:rsidRPr="00D16ABA">
        <w:rPr>
          <w:rStyle w:val="Hyperlink"/>
        </w:rPr>
        <w:t>bm1</w:t>
      </w:r>
      <w:r>
        <w:fldChar w:fldCharType="end"/>
      </w:r>
    </w:p> 

Third, what rels are involved? To answer this, I run the docx through docx4j’s PartsList sample. It shows me that these hyperlinks don’t create any rels. Alternatively, to see this, you could have looked at the rels part when you unzipped the docx.

So we can see that adding an internal hyperlink to a heading requires that it be bookmarked first. Once you have a bookmark, you use a w:hyperlink to refer to the bookmark by name (not id). Doesn’t look like there is any reason to use fields for this.

Here’s a suitable method:

	/**
	 * Create a Hyperlink object, which is suitable for adding to a w:p
	 * @param bookmarkName
	 * @param linkText
	 * @return
	 */
	public static Hyperlink hyperlinkToBookmark(String bookmarkName, String linkText) {
		
		try {
			
			String hpl = "<w:hyperlink w:anchor=\"" + bookmarkName + "\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" " +
            "w:history=\"1\" >" +
            "<w:r>" +
            "<w:rPr>" +
            "<w:rStyle w:val=\"Hyperlink\" />" +  // TODO: enable this style in the document!
            "</w:rPr>" +
            "<w:t>" + linkText + "</w:t>" +
            "</w:r>" +
            "</w:hyperlink>";

			return (Hyperlink)XmlUtils.unmarshalString(hpl);
			
		} catch (Exception e) {
			// Shouldn't happen
			e.printStackTrace();
			return null;
		}
				
	}  

We can test it by altering the BookmarkAdd sample to add a link:

Hyperlink h = MainDocumentPart.hyperlinkToBookmark(bookmarkName, "link to bookmark");
wordMLPackage.getMainDocumentPart().addParagraphOfText("some text").getContent().add(h);

then checking the result opens in Word ok.

That’s all. Added to docx4j in revision 1777.

2 Responses so far

  1. 1

    Tom said,

    August 16, 2012 @ 12:48 am

    Hi there!

    I’m trying this Hyperlink object but can’t make it work.

    […]
    WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
    MainDocumentPart mdp = wordMLPackage.getMainDocumentPart();
    mdp.addStyledParagraphOfText(“Title”, “Title”);
    for(int i=0;i<40;i++) {
    mdp.addParagraphOfText("This is a quite long test sentence.");
    }
    mdp.addStyledParagraphOfText("Heading2", "Target");
    for(int i=0;i<40;i++) {
    mdp.addParagraphOfText("This is a quite long test sentence.");
    }
    Hyperlink h = mdp.hyperlinkToBookmark("_Target", "link to bookmark");
    mdp.addParagraphOfText("some text").getContent().add(h);
    […]

    After execution, at the end of the docx file there is the "some text" and after that "link to bookmark" where the last mentioned is a hyperlink but it wont goes to Target paragraph. (Instead it goes to the top of page.)

    What am i missing ?

  2. 2

    Tom said,

    August 16, 2012 @ 12:55 am

    Well, sorry for postin in a “rush”.

    Here is the solution for all who’s the same problem.

    Change this:
    Hyperlink h = mdp.hyperlinkToBookmark(“_Target”, “link to bookmark”);
    to this:
    Hyperlink h = mdp.hyperlinkToBookmark(“Target”, “link to bookmark”);

Comment RSS