From 076de3f0567b44e349f7f7af99e5c4ad215646a2 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 20 Nov 2025 15:56:45 +0000 Subject: Handle hyperlinked wp:anchor and wp:inline elements --- .../internal/docx/StatefulBodyXmlReader.java | 74 ++++++++++++++++++---- 1 file changed, 61 insertions(+), 13 deletions(-) (limited to 'src/main/java/org') 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 1f5aff4..9983d09 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java @@ -694,34 +694,52 @@ class StatefulBodyXmlReader { private ReadResult readInline(XmlElement element) { XmlElementLike properties = element.findChildOrEmpty("wp:docPr"); + Optional altText = Optionals.first( properties.getAttributeOrNone("descr").filter(description -> !description.trim().isEmpty()), properties.getAttributeOrNone("title") ); + + XmlElementLike hlinkClickElement = properties.findChildOrEmpty("a:hlinkClick"); + Optional href = hlinkClickElement.getAttributeOrNone("r:id") + .map(relationships::findTargetByRelationshipId); + XmlElementList blips = element.findChildren("a:graphic") .findChildren("a:graphicData") .findChildren("pic:pic") .findChildren("pic:blipFill") .findChildren("a:blip"); - return readBlips(blips, altText); + return readBlips(blips, altText, href); } - private ReadResult readBlips(XmlElementList blips, Optional altText) { - return ReadResult.flatMap(blips, blip -> readBlip(blip, altText)); + private ReadResult readBlips( + XmlElementList blips, + Optional altText, + Optional href + ) { + return ReadResult.flatMap(blips, blip -> readBlip(blip, altText, href)); } - private ReadResult readBlip(XmlElement blip, Optional altText) { - Optional embedRelationshipId = blip.getAttributeOrNone("r:embed"); - Optional linkRelationshipId = blip.getAttributeOrNone("r:link"); - if (embedRelationshipId.isPresent()) { - String imagePath = relationshipIdToDocxPath(embedRelationshipId.get()); - return readImage(imagePath, altText, () -> Archives.getInputStream(file, imagePath)); - } else if (linkRelationshipId.isPresent()) { - String imagePath = relationships.findTargetByRelationshipId(linkRelationshipId.get()); - return readImage(imagePath, altText, () -> fileReader.getInputStream(imagePath)); - } else { + private ReadResult readBlip( + XmlElement blip, + Optional altText, + Optional href + ) { + Optional blipImage = findBlipImage(blip); + + if (!blipImage.isPresent()) { return ReadResult.emptyWithWarning("Could not find image file for a:blip element"); } + + ReadResult result = readImage(blipImage.get().path, altText, blipImage.get().open); + + if (!href.isPresent()) { + return result; + } + + return result.map(imageElements -> list( + new Hyperlink(href, Optional.empty(), Optional.empty(), imageElements) + )); } private ReadResult readImage(String imagePath, Optional altText, InputStreamSupplier open) { @@ -736,6 +754,36 @@ class StatefulBodyXmlReader { } } + private static class BlipImage { + private final String path; + private final InputStreamSupplier open; + + public BlipImage(String path, InputStreamSupplier open) { + this.path = path; + this.open = open; + } + } + + private Optional findBlipImage(XmlElement blip) { + Optional embedRelationshipId = blip.getAttributeOrNone("r:embed"); + Optional linkRelationshipId = blip.getAttributeOrNone("r:link"); + if (embedRelationshipId.isPresent()) { + String imagePath = relationshipIdToDocxPath(embedRelationshipId.get()); + return Optional.of(new BlipImage( + imagePath, + () -> Archives.getInputStream(file, imagePath) + )); + } else if (linkRelationshipId.isPresent()) { + String imagePath = relationships.findTargetByRelationshipId(linkRelationshipId.get()); + return Optional.of(new BlipImage( + imagePath, + () -> fileReader.getInputStream(imagePath) + )); + } else { + return Optional.empty(); + } + } + private ReadResult readSdt(XmlElement element) { ReadResult contentResult = readElements( element.findChildOrEmpty("w:sdtContent").getChildren() -- cgit v1.2.3