Skip to content

Commit

Permalink
Feature/issue 136 external hyperlinks (#138)
Browse files Browse the repository at this point in the history
* Fixes #136: Test for external link rId passes

---------

Co-authored-by: Eliot Kimber <ekimber@contrext.com>
  • Loading branch information
drmacro and contrext authored Jan 15, 2024
1 parent 12c47f5 commit 036c500
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 13 deletions.
19 changes: 11 additions & 8 deletions src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.apache.poi.xwpf.usermodel.XWPFNum;
import org.apache.poi.xwpf.usermodel.XWPFNumbering;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRelation;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFStyle;
import org.apache.poi.xwpf.usermodel.XWPFStyles;
Expand Down Expand Up @@ -1756,7 +1757,7 @@ private void makeFootnote(XWPFParagraph para, XmlObject xml) throws DocxGenerati
}
}

noteCursor.dispose();
noteCursor.close();

}

Expand Down Expand Up @@ -2243,27 +2244,29 @@ private void makeHyperlink(XWPFParagraph para, XmlCursor cursor) throws DocxGene
// Convention in simple WP XML is fragment identifiers are to bookmark IDs,
// while everything else is a URI to an external resource.

// If the hyperlink is to a bookmark (just a fragment ID) then we we create a hyperlink with
// an anchor, otherwise we create an external hyperlink to a URI.

CTHyperlink hyperlink = para.getCTP().addNewHyperlink();

// Set the appropriate target:

if (href.startsWith("#")) {
// Just a fragment ID, must be to a bookmark
// Just a fragment ID, must be to a bookmark, set @anchor attribute.
String bookmarkName = href.substring(1);
hyperlink.setAnchor(bookmarkName);
} else {
// Create a relationship that targets the href and use the
// relationship's ID on the hyperlink
// It's not yet clear from the POI API how to create a new relationship for
// use by an external hyperlink.
// throw new NotImplementedException("Links to external resources not yet implemented.");
// Create an external hyperlink. This creates the necessary relationship.
// Not using the createHyperlinkRun() of paragraph because it doesn't handle the
// runs within the <hyperlink> as we need. Set the @rId attribute.
String rId = para.getDocument().getPackagePart().addExternalRelationship(href, XWPFRelation.HYPERLINK.getRelation()).getId();
hyperlink.setId(rId);
}

cursor.push();
XWPFHyperlinkRun hyperlinkRun = makeHyperlinkRun(hyperlink, cursor, para);
cursor.pop();
para.addRun(hyperlinkRun);

}

/**
Expand Down
23 changes: 20 additions & 3 deletions src/test/java/org/wordinator/xml2docx/TestDocxGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,17 +251,34 @@ public void testHyperlinkHandling() throws Exception {
XWPFParagraph p = iterator.next();
assertNotNull("Expected a paragraph", p);
assertEquals("Issue 65: Data following hyperlink is dropped", p.getText());
// Normal footnote with generated ref
// Hyperlink to a bookmark (internal link):
p = iterator.next();
Iterator<IRunElement> runIterator = p.getIRuns().iterator();
assertTrue("Expected runs", runIterator.hasNext());
IRunElement run = runIterator.next();
assertEquals("First run in para before the hyperlink", ((XWPFRun)run).getText(0));
run = runIterator.next(); // Should be the hyperlink
assertTrue("Expected a XWPFHyperlinkRun", run instanceof XWPFHyperlinkRun);
assertTrue("Expected fun following the hyperlink", runIterator.hasNext());
run = runIterator.next(); // Should be the hyperlink
assertTrue("Expected run following the hyperlink", runIterator.hasNext());
run = runIterator.next();
assertEquals("Run after the hyperlink.", ((XWPFRun)run).getText(0));
// Now look for external link:
p = iterator.next();
assertNotNull("Expected another paragraph after internal hyperlink", p);
p = iterator.next();
assertNotNull("Expected another paragraph after internal hyperlink", p);
p = iterator.next(); // Should be paragraph with hyperlink
assertNotNull("Expected another paragraph after internal hyperlink", p);
runIterator = p.getIRuns().iterator();
assertTrue("Expected runs", runIterator.hasNext());
run = runIterator.next();
assertTrue("First run in para before the hyperlink", ((XWPFRun)run).getText(0).startsWith("External hyperlink:"));
run = runIterator.next(); // Should be the hyperlink
assertTrue("Expected a XWPFHyperlinkRun", run instanceof XWPFHyperlinkRun);
XWPFHyperlinkRun linkRun = (XWPFHyperlinkRun)run;
String rId = linkRun.getHyperlinkId();
assertNotNull("Expected and rId", rId);

}

@Test
Expand Down
Binary file modified src/test/resources/docx/Manual-and-Styled-Lists.docx
Binary file not shown.
4 changes: 3 additions & 1 deletion src/test/resources/simplewp/simplewpml-issue-65.swpx
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@
<run> 1996; 24: 1-11.</run>
<bookmarkEnd id="IDunique_6_Connect_42_li_olb_5tg_tt"/>
</p>
</body>
<p><run>External hyperlink: </run><hyperlink href="https://github.com/drmacro/wordinator"><run>Wordinator</run>
</hyperlink><run>After hyperlink para with external hyperlink</run></p>
</body>
</document>
2 changes: 1 addition & 1 deletion version.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=1.1.3
version=1.2.0

0 comments on commit 036c500

Please sign in to comment.