package org.zwobble.mammoth.docx; import org.zwobble.mammoth.documents.*; import org.zwobble.mammoth.results.Result; import org.zwobble.mammoth.results.Warning; import org.zwobble.mammoth.util.InputStreamSupplier; import org.zwobble.mammoth.util.MammothOptionals; import org.zwobble.mammoth.xml.XmlElement; import org.zwobble.mammoth.xml.XmlElementLike; import org.zwobble.mammoth.xml.XmlElementList; import org.zwobble.mammoth.xml.XmlNode; import java.util.Optional; import java.util.function.Function; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; import static org.zwobble.mammoth.docx.ReadResult.EMPTY_SUCCESS; import static org.zwobble.mammoth.docx.ReadResult.success; import static org.zwobble.mammoth.results.Warning.warning; import static org.zwobble.mammoth.util.MammothLists.list; import static org.zwobble.mammoth.util.MammothStrings.trimLeft; public class BodyXmlReader { private final Styles styles; private final Numbering numbering; private final Relationships relationships; private final ContentTypes contentTypes; private final DocxFile file; public BodyXmlReader( Styles styles, Numbering numbering, Relationships relationships, ContentTypes contentTypes, DocxFile file) { this.styles = styles; this.numbering = numbering; this.relationships = relationships; this.contentTypes = contentTypes; this.file = file; } 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 readElements(element.children()).map(Table::new); case "w:tr": return readElements(element.children()).map(TableRow::new); case "w:tc": return readElements(element.children()).map(TableCell::new); 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:pict": return readPict(element); case "v:imagedata": return readImagedata(element); case "wp:inline": return readInline(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.children()); 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:commentReference": case "w:del": case "w:footnoteRef": case "w:endnoteRef": case "w:pPr": case "w:rPr": case "w:tblPr": case "w:tblGrid": case "w:tcPr": return EMPTY_SUCCESS; default: Warning warning = 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.children()), (style, children) -> new Run( isBold(properties), isItalic(properties), isUnderline(properties), isStrikethrough(properties), style, children)); } private boolean isBold(XmlElementLike properties) { return properties.hasChild("w:b"); } private boolean isItalic(XmlElementLike properties) { return properties.hasChild("w:i"); } private boolean isUnderline(XmlElementLike properties) { return properties.hasChild("w:u"); } private boolean isStrikethrough(XmlElementLike properties) { return properties.hasChild("w:strike"); } private Result> readRunStyle(XmlElementLike properties) { return readStyle(properties, "w:rStyle", "Run", styles::findCharacterStyleById); } public ReadResult readElements(Iterable nodes) { return ReadResult.concat( transform( filter(nodes, XmlElement.class), this::readElement)); } private ReadResult readParagraph(XmlElement element) { XmlElementLike properties = element.findChildOrEmpty("w:pPr"); Optional numbering = readNumbering(properties); return ReadResult.map( readParagraphStyle(properties), readElements(element.children()), (style, children) -> new Paragraph(style, numbering, children)).appendExtra(); } private Result> readParagraphStyle(XmlElementLike properties) { return readStyle(properties, "w:pStyle", "Paragraph", styles::findParagraphStyleById); } private Result> readStyle( XmlElementLike properties, String styleTagName, String styleType, Function> findStyleById) { return readVal(properties, styleTagName) .map(styleId -> findStyleById(styleType, styleId, findStyleById)) .orElse(Result.empty()); } private Result> findStyleById( String styleType, String styleId, Function> findStyleById) { Optional