From 554a0fe289bb74e5048b209546d22c423d33c528 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 1 Aug 2021 17:32:46 +0100 Subject: Handle internal hyperlinks created with complex fields --- .../internal/docx/StatefulBodyXmlReader.java | 43 +++++++++++++--------- .../zwobble/mammoth/tests/docx/BodyXmlTests.java | 29 ++++++++++++++- 2 files changed, 53 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java b/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java index 366cf16..b6bd51e 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java @@ -40,16 +40,16 @@ class StatefulBodyXmlReader { private interface ComplexField { ComplexField UNKNOWN = new ComplexField() {}; - static ComplexField hyperlink(String href) { - return new HyperlinkComplexField(href); + static ComplexField hyperlink(Function, Hyperlink> childrenToHyperlink) { + return new HyperlinkComplexField(childrenToHyperlink); } } private static class HyperlinkComplexField implements ComplexField { - private final String href; + private final Function, Hyperlink> childrenToHyperlink; - private HyperlinkComplexField(String href) { - this.href = href; + private HyperlinkComplexField(Function, Hyperlink> childrenToHyperlink) { + this.childrenToHyperlink = childrenToHyperlink; } } @@ -173,9 +173,9 @@ class StatefulBodyXmlReader { readRunStyle(properties), readElements(element.getChildren()), (style, children) -> { - Optional hyperlinkHref = currentHyperlinkHref(); - if (hyperlinkHref.isPresent()) { - children = list(Hyperlink.href(hyperlinkHref.get(), Optional.empty(), children)); + Optional hyperlinkComplexField = currentHyperlinkComplexField(); + if (hyperlinkComplexField.isPresent()) { + children = list(hyperlinkComplexField.get().childrenToHyperlink.apply(children)); } return new Run( @@ -193,9 +193,8 @@ class StatefulBodyXmlReader { ); } - private Optional currentHyperlinkHref() { - return tryGetLast(lazyFilter(this.complexFieldStack, HyperlinkComplexField.class)) - .map(field -> field.href); + private Optional currentHyperlinkComplexField() { + return tryGetLast(lazyFilter(this.complexFieldStack, HyperlinkComplexField.class)); } private boolean isBold(XmlElementLike properties) { @@ -287,14 +286,22 @@ class StatefulBodyXmlReader { return ReadResult.EMPTY_SUCCESS; } - private Optional parseHyperlinkFieldCode(String instrText) { - Pattern pattern = Pattern.compile("\\s*HYPERLINK \"(.*)\""); - Matcher matcher = pattern.matcher(instrText); - if (matcher.lookingAt()) { - return Optional.of(matcher.group(1)); - } else { - return Optional.empty(); + private Optional, Hyperlink>> parseHyperlinkFieldCode(String instrText) { + Pattern externalLinkPattern = Pattern.compile("\\s*HYPERLINK \"(.*)\""); + Matcher externalLinkMatcher = externalLinkPattern.matcher(instrText); + if (externalLinkMatcher.lookingAt()) { + String href = externalLinkMatcher.group(1); + return Optional.of(children -> Hyperlink.href(href, Optional.empty(), children)); + } + + Pattern internalLinkPattern = Pattern.compile("\\s*HYPERLINK\\s+\\\\l\\s+\"(.*)\""); + Matcher internalLinkMatcher = internalLinkPattern.matcher(instrText); + if (internalLinkMatcher.lookingAt()) { + String anchor = internalLinkMatcher.group(1); + return Optional.of(children -> Hyperlink.anchor(anchor, Optional.empty(), children)); } + + return Optional.empty(); } private InternalResult> readParagraphStyle(XmlElementLike properties) { diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java index 75af768..6c04f80 100644 --- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java +++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java @@ -291,7 +291,7 @@ public class BodyXmlTests { } @Test - public void runsInAComplexFieldForHyperlinksAreReadAsHyperlinks() { + public void runsInAComplexFieldForHyperlinksWithoutSwitchAreReadAsExternalHyperlinks() { XmlElement hyperlinkRunXml = runXml("this is a hyperlink"); XmlElement element = paragraphXml(list( BEGIN_COMPLEX_FIELD, @@ -315,6 +315,33 @@ public class BodyXmlTests { ))); } + @Test + public void runsInAComplexFieldForHyperlinksWithLSwitchAreReadAsInternalHyperlinks() { + XmlElement hyperlinkRunXml = runXml("this is a hyperlink"); + XmlElement element = paragraphXml(list( + BEGIN_COMPLEX_FIELD, + element("w:instrText", list( + XmlNodes.text(" HYPERLINK \\l \"InternalLink\"") + )), + SEPARATE_COMPLEX_FIELD, + hyperlinkRunXml, + END_COMPLEX_FIELD + )); + DocumentElement paragraph = readSuccess(bodyReader(), element); + + assertThat(paragraph, isParagraph(hasChildren( + isEmptyRun(), + isEmptyHyperlinkedRun(), + isHyperlinkedRun( + hasAnchor("InternalLink"), + hasChildren( + isTextElement("this is a hyperlink") + ) + ), + isEmptyRun() + ))); + } + @Test public void runsAfterAComplexFieldForHyperlinksAreNotReadAsHyperlinks() { XmlElement afterEndXml = runXml("this will not be a hyperlink"); -- cgit v1.2.3