From b31c5a4b4ccb835a583efa33cee178e122d564de Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 19 Feb 2017 18:16:52 +0000 Subject: Handle hyperlinks created with complex fields --- .../mammoth/internal/docx/BodyXmlReader.java | 452 +---------------- .../internal/docx/StatefulBodyXmlReader.java | 539 +++++++++++++++++++++ .../zwobble/mammoth/internal/util/Iterables.java | 14 + .../zwobble/mammoth/tests/docx/BodyXmlTests.java | 188 ++++++- .../mammoth/tests/docx/DocumentMatchers.java | 79 +++ 5 files changed, 823 insertions(+), 449 deletions(-) create mode 100644 src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java create mode 100644 src/test/java/org/zwobble/mammoth/tests/docx/DocumentMatchers.java diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/BodyXmlReader.java b/src/main/java/org/zwobble/mammoth/internal/docx/BodyXmlReader.java index 4cbdda7..ace6628 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/BodyXmlReader.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/BodyXmlReader.java @@ -1,34 +1,10 @@ package org.zwobble.mammoth.internal.docx; import org.zwobble.mammoth.internal.archives.Archive; -import org.zwobble.mammoth.internal.archives.Archives; -import org.zwobble.mammoth.internal.documents.*; -import org.zwobble.mammoth.internal.results.InternalResult; -import org.zwobble.mammoth.internal.util.Casts; -import org.zwobble.mammoth.internal.util.InputStreamSupplier; -import org.zwobble.mammoth.internal.util.Lists; -import org.zwobble.mammoth.internal.util.Optionals; import org.zwobble.mammoth.internal.xml.XmlElement; -import org.zwobble.mammoth.internal.xml.XmlElementLike; -import org.zwobble.mammoth.internal.xml.XmlElementList; import org.zwobble.mammoth.internal.xml.XmlNode; -import java.util.*; -import java.util.function.Function; - -import static org.zwobble.mammoth.internal.docx.ReadResult.EMPTY_SUCCESS; -import static org.zwobble.mammoth.internal.docx.ReadResult.success; -import static org.zwobble.mammoth.internal.docx.Uris.uriToZipEntryName; -import static org.zwobble.mammoth.internal.util.Iterables.lazyFilter; -import static org.zwobble.mammoth.internal.util.Lists.list; -import static org.zwobble.mammoth.internal.util.Maps.entry; -import static org.zwobble.mammoth.internal.util.Maps.lookup; -import static org.zwobble.mammoth.internal.util.Sets.set; - public class BodyXmlReader { - private static final Set IMAGE_TYPES_SUPPORTED_BY_BROWSERS = set( - "image/png", "image/gif", "image/jpeg", "image/svg+xml", "image/tiff"); - private final Styles styles; private final Numbering numbering; private final Relationships relationships; @@ -42,7 +18,8 @@ public class BodyXmlReader { Relationships relationships, ContentTypes contentTypes, Archive file, - FileReader fileReader) + FileReader fileReader + ) { this.styles = styles; this.numbering = numbering; @@ -52,414 +29,25 @@ public class BodyXmlReader { this.fileReader = fileReader; } - public ReadResult readElement(XmlElement element) { - switch (element.getName()) { - case "w:t": - return success(new Text(element.innerText())); - case "w:r": - return readRun(element); - case "w:p": - return readParagraph(element); - - case "w:tab": - return success(Tab.TAB); - case "w:br": - return readBreak(element); - - case "w:tbl": - return readTable(element); - case "w:tr": - return readElements(element.getChildren()).map(TableRow::new); - case "w:tc": - return readTableCell(element); - - case "w:hyperlink": - return readHyperlink(element); - case "w:bookmarkStart": - return readBookmark(element); - case "w:footnoteReference": - return readNoteReference(NoteType.FOOTNOTE, element); - case "w:endnoteReference": - return readNoteReference(NoteType.ENDNOTE, element); - case "w:commentReference": - return readCommentReference(element); - - case "w:pict": - return readPict(element); - - case "v:imagedata": - return readImagedata(element); - - case "wp:inline": - case "wp:anchor": - return readInline(element); - - case "w:sdt": - return readSdt(element); - - case "w:ins": - case "w:smartTag": - case "w:drawing": - case "v:roundrect": - case "v:shape": - case "v:textbox": - case "w:txbxContent": - return readElements(element.getChildren()); - - case "office-word:wrap": - case "v:shadow": - case "v:shapetype": - case "w:bookmarkEnd": - case "w:sectPr": - case "w:proofErr": - case "w:lastRenderedPageBreak": - case "w:commentRangeStart": - case "w:commentRangeEnd": - case "w:del": - case "w:footnoteRef": - case "w:endnoteRef": - case "w:annotationRef": - case "w:pPr": - case "w:rPr": - case "w:tblPr": - case "w:tblGrid": - case "w:tcPr": - return EMPTY_SUCCESS; - - default: - String warning = "An unrecognised element was ignored: " + element.getName(); - return ReadResult.emptyWithWarning(warning); - } - } - - private ReadResult readRun(XmlElement element) { - XmlElementLike properties = element.findChildOrEmpty("w:rPr"); - return ReadResult.map( - readRunStyle(properties), - readElements(element.getChildren()), - (style, children) -> new Run( - isBold(properties), - isItalic(properties), - isUnderline(properties), - isStrikethrough(properties), - readVerticalAlignment(properties), - style, - children)); - } - - private boolean isBold(XmlElementLike properties) { - return readBooleanElement(properties, "w:b"); - } - - private boolean isItalic(XmlElementLike properties) { - return readBooleanElement(properties, "w:i"); - } - - private boolean isUnderline(XmlElementLike properties) { - return readBooleanElement(properties, "w:u"); - } - - private boolean isStrikethrough(XmlElementLike properties) { - return readBooleanElement(properties, "w:strike"); - } - - private boolean readBooleanElement(XmlElementLike properties, String tagName) { - return properties.findChild(tagName) - .map(child -> child.getAttributeOrNone("w:val") - .map(value -> !value.equals("false") && !value.equals("0")) - .orElse(true)) - .orElse(false); - } - - private VerticalAlignment readVerticalAlignment(XmlElementLike properties) { - String verticalAlignment = readVal(properties, "w:vertAlign").orElse(""); - switch (verticalAlignment) { - case "superscript": - return VerticalAlignment.SUPERSCRIPT; - case "subscript": - return VerticalAlignment.SUBSCRIPT; - default: - // TODO: warn if set? - return VerticalAlignment.BASELINE; - } - } - - private InternalResult> readRunStyle(XmlElementLike properties) { - return readStyle(properties, "w:rStyle", "Run", styles::findCharacterStyleById); - } - - public ReadResult readElements(Iterable nodes) { - return ReadResult.flatMap(lazyFilter(nodes, XmlElement.class), this::readElement); + ReadResult readElements(Iterable nodes) { + return new StatefulBodyXmlReader( + styles, + numbering, + relationships, + contentTypes, + file, + fileReader + ).readElements(nodes); } - private ReadResult readParagraph(XmlElement element) { - XmlElementLike properties = element.findChildOrEmpty("w:pPr"); - Optional numbering = readNumbering(properties); - return ReadResult.map( - readParagraphStyle(properties), - readElements(element.getChildren()), - (style, children) -> new Paragraph(style, numbering, children)).appendExtra(); - } - - private InternalResult> readParagraphStyle(XmlElementLike properties) { - return readStyle(properties, "w:pStyle", "Paragraph", styles::findParagraphStyleById); - } - - private InternalResult> readStyle( - XmlElementLike properties, - String styleTagName, - String styleType, - Function> findStyleById) - { - return readVal(properties, styleTagName) - .map(styleId -> findStyleById(styleType, styleId, findStyleById)) - .orElse(InternalResult.empty()); - } - - private InternalResult> findStyleById( - String styleType, - String styleId, - Function> findStyleById) - { - Optional