From d9a34a0836a7e41a975017fb021f5af90d954ae5 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Tue, 23 Jan 2018 21:26:38 +0000 Subject: Find main document part using _rels/.rels --- .../zwobble/mammoth/internal/archives/Archive.java | 1 + .../mammoth/internal/archives/InMemoryArchive.java | 5 +++ .../mammoth/internal/archives/ZippedArchive.java | 5 +++ .../mammoth/internal/docx/DocumentReader.java | 36 +++++++++++++++++++--- .../org/zwobble/mammoth/internal/util/Lists.java | 8 +++++ 5 files changed, 51 insertions(+), 4 deletions(-) (limited to 'src/main/java/org') diff --git a/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java b/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java index 4440234..2f5a4ae 100644 --- a/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java +++ b/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java @@ -7,4 +7,5 @@ import java.util.Optional; public interface Archive extends Closeable { Optional tryGetInputStream(String name) throws IOException; + boolean exists(String name); } diff --git a/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java b/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java index 9ad7134..0844e8d 100644 --- a/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java +++ b/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java @@ -45,6 +45,11 @@ public class InMemoryArchive implements MutableArchive { .map(ByteArrayInputStream::new); } + @Override + public boolean exists(String name) { + return entries.containsKey(name); + } + @Override public void writeEntry(String path, String content) { entries.put(path, content.getBytes(StandardCharsets.UTF_8)); diff --git a/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java b/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java index 853ced9..47be074 100644 --- a/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java +++ b/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java @@ -28,6 +28,11 @@ public class ZippedArchive implements Archive { } } + @Override + public boolean exists(String name) { + return zipFile.getEntry(name) != null; + } + @Override public void close() throws IOException { zipFile.close(); diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java b/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java index 4ef9c37..9d1b270 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java @@ -14,7 +14,8 @@ import java.nio.file.Path; import java.util.List; import java.util.Optional; -import static org.zwobble.mammoth.internal.util.Lists.list; +import static org.zwobble.mammoth.internal.util.Lists.*; +import static org.zwobble.mammoth.internal.util.Strings.trimLeft; public class DocumentReader { @FunctionalInterface @@ -23,12 +24,15 @@ public class DocumentReader { } public static InternalResult readDocument(Optional path, Archive zipFile) { + Relationships packageRelationships = readPackageRelationships(zipFile); + String documentFilename = findDocumentFilename(zipFile, packageRelationships); + Styles styles = readStyles(zipFile); Numbering numbering = readNumbering(zipFile); ContentTypes contentTypes = readContentTypes(zipFile); FileReader fileReader = new PathRelativeFileReader(path); BodyReaders bodyReaders = name -> { - Relationships relationships = readRelationships(zipFile, name); + Relationships relationships = readRelationshipsFor(zipFile, name); return new BodyXmlReader(styles, numbering, relationships, contentTypes, zipFile, fileReader); }; return InternalResult.flatMap( @@ -36,10 +40,30 @@ public class DocumentReader { readComments(zipFile, bodyReaders), (notes, comments) -> { DocumentXmlReader reader = new DocumentXmlReader(bodyReaders.forName("document"), notes, comments); - return reader.readElement(parseOfficeXml(zipFile, "word/document.xml")); + return reader.readElement(parseOfficeXml(zipFile, documentFilename)); }); } + private static Relationships readPackageRelationships(Archive archive) { + return readRelationships(archive, "_rels/.rels"); + } + + private static String findDocumentFilename(Archive archive, Relationships packageRelationships) { + String officeDocumentType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; + List targets = eagerConcat( + eagerMap( + packageRelationships.findTargetsByType(officeDocumentType), + target -> trimLeft(target, '/') + ), + list("word/document.xml") + ); + List validTargets = eagerFilter(targets, archive::exists); + return tryGetFirst(validTargets) + .orElseThrow(() -> new PassThroughException( + new IOException("Could not find word/document.xml in ZIP file. Are you sure this is a valid .docx file?") + )); + } + private static InternalResult> readComments(Archive file, BodyReaders bodyReaders) { return tryParseOfficeXml(file, "word/comments.xml") .map(new CommentXmlReader(bodyReaders.forName("comments"))::readElement) @@ -75,8 +99,12 @@ public class DocumentReader { .orElse(ContentTypes.DEFAULT); } + private static Relationships readRelationshipsFor(Archive zipFile, String name) { + return readRelationships(zipFile, "word/_rels/" + name + ".xml.rels"); + } + private static Relationships readRelationships(Archive zipFile, String name) { - return tryParseOfficeXml(zipFile, "word/_rels/" + name + ".xml.rels") + return tryParseOfficeXml(zipFile, name) .map(RelationshipsXml::readRelationshipsXmlElement) .orElse(Relationships.EMPTY); } diff --git a/src/main/java/org/zwobble/mammoth/internal/util/Lists.java b/src/main/java/org/zwobble/mammoth/internal/util/Lists.java index 3fbb5dd..b6d6570 100644 --- a/src/main/java/org/zwobble/mammoth/internal/util/Lists.java +++ b/src/main/java/org/zwobble/mammoth/internal/util/Lists.java @@ -80,6 +80,14 @@ public class Lists { return stream(iterable).collect(Collectors.toList()); } + public static Optional tryGetFirst(List list) { + if (list.isEmpty()) { + return Optional.empty(); + } else { + return Optional.of(list.get(0)); + } + } + public static Optional tryGetLast(List list) { if (list.isEmpty()) { return Optional.empty(); -- cgit v1.2.3