aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2016-02-21 09:44:16 +0000
committerMichael Williamson <mike@zwobble.org>2016-02-21 09:44:16 +0000
commitc1604e510cc831a00f6105df35c68a856f061b65 (patch)
tree699e55b0d654ec464fe25c0b21964de3d9bbc780 /src
parent9d5da9d5339055198aac8d0d41c3d33d97344ebd (diff)
Allow input streams to be read
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/mammoth/Mammoth.java42
-rw-r--r--src/main/java/org/zwobble/mammoth/docx/DocumentReader.java25
-rw-r--r--src/main/java/org/zwobble/mammoth/docx/InMemoryDocxFile.java46
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/MammothTests.java47
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java4
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java28
6 files changed, 135 insertions, 57 deletions
diff --git a/src/main/java/org/zwobble/mammoth/Mammoth.java b/src/main/java/org/zwobble/mammoth/Mammoth.java
index 93c639a..4963c3a 100644
--- a/src/main/java/org/zwobble/mammoth/Mammoth.java
+++ b/src/main/java/org/zwobble/mammoth/Mammoth.java
@@ -7,6 +7,7 @@ import org.zwobble.mammoth.documents.HasChildren;
import org.zwobble.mammoth.documents.Paragraph;
import org.zwobble.mammoth.documents.Text;
import org.zwobble.mammoth.docx.DocxFile;
+import org.zwobble.mammoth.docx.InMemoryDocxFile;
import org.zwobble.mammoth.docx.ZippedDocxFile;
import org.zwobble.mammoth.html.Html;
import org.zwobble.mammoth.results.Result;
@@ -18,6 +19,8 @@ import org.zwobble.mammoth.util.Casts;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.zip.ZipFile;
@@ -61,30 +64,47 @@ public class Mammoth {
.mapParagraph(ParagraphMatcher.styleName("Normal"), HtmlPath.element("p"))
.build();
- public static Result<String> convertToHtml(File file) {
+ public static Result<String> convertToHtml(InputStream stream) throws IOException {
+ return convertToHtml(stream, Options.DEFAULT);
+ }
+
+ public static Result<String> convertToHtml(InputStream stream, Options options) throws IOException {
+ return withDocxFile(stream, zipFile ->
+ convertToHtml(Optional.empty(), zipFile, options));
+ }
+
+ public static Result<String> convertToHtml(File file) throws IOException {
return convertToHtml(file, Options.DEFAULT);
}
- public static Result<String> convertToHtml(File file, Options options) {
+ public static Result<String> convertToHtml(File file, Options options) throws IOException {
return withDocxFile(file, zipFile ->
- readDocument(Optional.of(file.toPath()), zipFile)
- .flatMap(nodes -> DocumentConverter.convertToHtml(options.idPrefix, options.preserveEmptyParagraphs, DEFAULT_STYLE_MAP, nodes))
- .map(Html::stripEmpty)
- .map(Html::collapse)
- .map(Html::write));
+ convertToHtml(Optional.of(file.toPath()), zipFile, options));
}
- public static Result<String> extractRawText(File file) {
+ private static Result<String> convertToHtml(Optional<Path> path, DocxFile zipFile, Options options) {
+ return readDocument(path, zipFile)
+ .flatMap(nodes -> DocumentConverter.convertToHtml(options.idPrefix, options.preserveEmptyParagraphs, DEFAULT_STYLE_MAP, nodes))
+ .map(Html::stripEmpty)
+ .map(Html::collapse)
+ .map(Html::write);
+ }
+
+ public static Result<String> extractRawText(File file) throws IOException {
return withDocxFile(file, zipFile ->
readDocument(Optional.of(file.toPath()), zipFile)
.map(Mammoth::extractRawTextOfChildren));
}
- private static <T> T withDocxFile(File file, Function<DocxFile, T> function) {
+ private static <T> T withDocxFile(File file, Function<DocxFile, T> function) throws IOException {
try (DocxFile zipFile = new ZippedDocxFile(new ZipFile(file))) {
return function.apply(zipFile);
- } catch (IOException e) {
- throw new UnsupportedOperationException("Should return a result of failure");
+ }
+ }
+
+ private static <T> T withDocxFile(InputStream stream, Function<DocxFile, T> function) throws IOException {
+ try (DocxFile zipFile = InMemoryDocxFile.fromStream(stream)) {
+ return function.apply(zipFile);
}
}
diff --git a/src/main/java/org/zwobble/mammoth/docx/DocumentReader.java b/src/main/java/org/zwobble/mammoth/docx/DocumentReader.java
index 284dd89..1fc0fc8 100644
--- a/src/main/java/org/zwobble/mammoth/docx/DocumentReader.java
+++ b/src/main/java/org/zwobble/mammoth/docx/DocumentReader.java
@@ -7,6 +7,9 @@ import org.zwobble.mammoth.util.MammothLists;
import org.zwobble.mammoth.xml.XmlElement;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.Optional;
@@ -24,7 +27,14 @@ public class DocumentReader {
ContentTypes contentTypes = readContentTypes(zipFile);
FileReader fileReader = uri -> {
try {
- return path.get().toUri().resolve(uri).toURL().openStream();
+ Optional<URI> absoluteUri = asAbsoluteUri(uri);
+ if (absoluteUri.isPresent()) {
+ return open(absoluteUri.get());
+ } else if (path.isPresent()) {
+ return open(path.get().toUri().resolve(uri));
+ } else {
+ throw new IOException("path of document is unknown, but is required for relative URI");
+ }
} catch (IOException exception) {
throw new IOException("could not open external image '" + uri + "': " + exception.getMessage());
}
@@ -40,6 +50,19 @@ public class DocumentReader {
});
}
+ private static InputStream open(URI uri) throws IOException {
+ return uri.toURL().openStream();
+ }
+
+ private static Optional<URI> asAbsoluteUri(String uriString) {
+ try {
+ URI uri = new URI(uriString);
+ return uri.isAbsolute() ? Optional.of(uri) : Optional.empty();
+ } catch (URISyntaxException exception) {
+ return Optional.empty();
+ }
+ }
+
private static Result<Notes> readNotes(DocxFile file, BodyReaders bodyReaders) {
return Result.map(
tryParseOfficeXml(file, "word/footnotes.xml")
diff --git a/src/main/java/org/zwobble/mammoth/docx/InMemoryDocxFile.java b/src/main/java/org/zwobble/mammoth/docx/InMemoryDocxFile.java
new file mode 100644
index 0000000..3c6d4b4
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/docx/InMemoryDocxFile.java
@@ -0,0 +1,46 @@
+package org.zwobble.mammoth.docx;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteStreams;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Optional;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class InMemoryDocxFile implements DocxFile {
+ public static DocxFile fromStream(InputStream stream) throws IOException {
+ ZipInputStream zipStream = new ZipInputStream(stream);
+ ImmutableMap.Builder<String, byte[]> entries = ImmutableMap.builder();
+ ZipEntry entry;
+ while ((entry = zipStream.getNextEntry()) != null) {
+ entries.put(entry.getName(), ByteStreams.toByteArray(zipStream));
+ }
+ return new InMemoryDocxFile(entries.build());
+ }
+
+ public static DocxFile fromStrings(Map<String, String> entries) {
+ return new InMemoryDocxFile(Maps.transformValues(entries, value -> value.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ private final Map<String, byte[]> entries;
+
+ public InMemoryDocxFile(Map<String, byte[]> entries) {
+ this.entries = entries;
+ }
+
+ @Override
+ public Optional<InputStream> tryGetInputStream(String name) throws IOException {
+ return Optional.ofNullable(entries.get(name))
+ .map(ByteArrayInputStream::new);
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+}
diff --git a/src/test/java/org/zwobble/mammoth/tests/MammothTests.java b/src/test/java/org/zwobble/mammoth/tests/MammothTests.java
index 603d39a..52efde2 100644
--- a/src/test/java/org/zwobble/mammoth/tests/MammothTests.java
+++ b/src/test/java/org/zwobble/mammoth/tests/MammothTests.java
@@ -16,42 +16,42 @@ import static org.zwobble.mammoth.tests.DeepReflectionMatcher.deepEquals;
public class MammothTests {
@Test
- public void docxContainingOneParagraphIsConvertedToSingleParagraphElement() {
+ public void docxContainingOneParagraphIsConvertedToSingleParagraphElement() throws IOException {
assertThat(
convertToHtml("single-paragraph.docx"),
deepEquals(success("<p>Walking on imported air</p>")));
}
@Test
- public void canReadFilesWithUtf8Bom() {
+ public void canReadFilesWithUtf8Bom() throws IOException {
assertThat(
convertToHtml("utf8-bom.docx"),
deepEquals(success("<p>This XML has a byte order mark.</p>")));
}
@Test
- public void emptyParagraphsAreIgnoredByDefault() {
+ public void emptyParagraphsAreIgnoredByDefault() throws IOException {
assertThat(
convertToHtml("empty.docx"),
deepEquals(success("")));
}
@Test
- public void emptyParagraphsArePreservedIfIgnoreEmptyParagraphsIsFalse() {
+ public void emptyParagraphsArePreservedIfIgnoreEmptyParagraphsIsFalse() throws IOException {
assertThat(
convertToHtml("empty.docx", Mammoth.Options.DEFAULT.preserveEmptyParagraphs()),
deepEquals(success("<p></p>")));
}
@Test
- public void simpleListIsConvertedToListElements() {
+ public void simpleListIsConvertedToListElements() throws IOException {
assertThat(
convertToHtml("simple-list.docx"),
deepEquals(success("<ul><li>Apple</li><li>Banana</li></ul>")));
}
@Test
- public void wordTablesAreConvertedToHtmlTables() {
+ public void wordTablesAreConvertedToHtmlTables() throws IOException {
assertThat(
convertToHtml("tables.docx"),
deepEquals(success(
@@ -64,7 +64,7 @@ public class MammothTests {
}
@Test
- public void inlineImagesAreIncludedInOutput() {
+ public void inlineImagesAreIncludedInOutput() throws IOException {
assertThat(
convertToHtml("tiny-picture.docx"),
deepEquals(success("<p><img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAOvgAADr4B6kKxwAAAABNJREFUKFNj/M+ADzDhlWUYqdIAQSwBE8U+X40AAAAASUVORK5CYII=\" /></p>")));
@@ -86,6 +86,23 @@ public class MammothTests {
}
@Test
+ public void warnIfDocumentHasImagesStoredOutsideOfDocumentWhenPathOfDocumentIsUnknown() throws IOException {
+ Path tempDirectory = Files.createTempDirectory("mammoth-");
+ try {
+ Path documentPath = tempDirectory.resolve("external-picture.docx");
+ Files.copy(TestData.file("external-picture.docx").toPath(), documentPath);
+ assertThat(
+ Mammoth.convertToHtml(documentPath.toUri().toURL().openStream()),
+ allOf(
+ hasProperty("value", equalTo("")),
+ hasProperty("warnings", contains(
+ hasProperty("message", startsWith("could not open external image 'tiny-picture.png': path of document is unknown, but is required for relative URI"))))));
+ } finally {
+ tempDirectory.toFile().delete();
+ }
+ }
+
+ @Test
public void warnIfImagesStoredOutsideOfDocumentAreNotFound() throws IOException {
Path tempDirectory = Files.createTempDirectory("mammoth-");
try {
@@ -103,14 +120,14 @@ public class MammothTests {
}
@Test
- public void contentTypesAreRead() {
+ public void contentTypesAreRead() throws IOException {
assertThat(
convertToHtml("tiny-picture-custom-content-type.docx"),
deepEquals(success("<p><img src=\"data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAOvgAADr4B6kKxwAAAABNJREFUKFNj/M+ADzDhlWUYqdIAQSwBE8U+X40AAAAASUVORK5CYII=\" /></p>")));
}
@Test
- public void footnotesAreAppendedToText() {
+ public void footnotesAreAppendedToText() throws IOException {
assertThat(
convertToHtml("footnotes.docx", Mammoth.Options.DEFAULT.idPrefix("doc-42-")),
deepEquals(success(
@@ -122,7 +139,7 @@ public class MammothTests {
}
@Test
- public void endNotesAreAppendedToText() {
+ public void endNotesAreAppendedToText() throws IOException {
assertThat(
convertToHtml("endnotes.docx", Mammoth.Options.DEFAULT.idPrefix("doc-42-")),
deepEquals(success(
@@ -134,7 +151,7 @@ public class MammothTests {
}
@Test
- public void relationshipsAreReadForEachFileContainingBodyXml() {
+ public void relationshipsAreReadForEachFileContainingBodyXml() throws IOException {
assertThat(
convertToHtml("footnote-hyperlink.docx", Mammoth.Options.DEFAULT.idPrefix("doc-42-")),
deepEquals(success(
@@ -143,25 +160,25 @@ public class MammothTests {
}
@Test
- public void textBoxesAreRead() {
+ public void textBoxesAreRead() throws IOException {
assertThat(
convertToHtml("text-box.docx"),
deepEquals(success("<p>Datum plane</p>")));
}
@Test
- public void canExtractRawText() {
+ public void canExtractRawText() throws IOException {
assertThat(
Mammoth.extractRawText(TestData.file("simple-list.docx")),
deepEquals(success("Apple\n\nBanana\n\n")));
}
- private Result<String> convertToHtml(String name) {
+ private Result<String> convertToHtml(String name) throws IOException {
File file = TestData.file(name);
return Mammoth.convertToHtml(file);
}
- private Result<String> convertToHtml(String name, Mammoth.Options options) {
+ private Result<String> convertToHtml(String name, Mammoth.Options options) throws IOException {
File file = TestData.file(name);
return Mammoth.convertToHtml(file, options);
}
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 2c1d505..8b31452 100644
--- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java
+++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java
@@ -437,7 +437,7 @@ public class BodyXmlTests {
Relationships relationships = new Relationships(map(
"rId5", new Relationship("media/hat.png")));
String imageBytes = "Not an image at all!";
- DocxFile file = new InMemoryDocxFile(map("word/media/hat.png", imageBytes));
+ DocxFile file = InMemoryDocxFile.fromStrings(map("word/media/hat.png", imageBytes));
Image image = (Image) readSuccess(
a(bodyReader, with(RELATIONSHIPS, relationships), with(DOCX_FILE, file)),
@@ -465,7 +465,7 @@ public class BodyXmlTests {
XmlElement element = inlineImageXml(embeddedBlipXml("rId5"), "");
Relationships relationships = new Relationships(map(
"rId5", new Relationship("media/hat.emf")));
- DocxFile file = new InMemoryDocxFile(map("word/media/hat.emf", "Not an image at all!"));
+ DocxFile file = InMemoryDocxFile.fromStrings(map("word/media/hat.emf", "Not an image at all!"));
ContentTypes contentTypes = new ContentTypes(map("emf", "image/x-emf"), map());
Result<?> result = read(
diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java b/src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java
deleted file mode 100644
index 4aef42e..0000000
--- a/src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.zwobble.mammoth.tests.docx;
-
-import org.zwobble.mammoth.docx.DocxFile;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import java.util.Optional;
-
-public class InMemoryDocxFile implements DocxFile {
- private final Map<String, String> entries;
-
- public InMemoryDocxFile(Map<String, String> entries) {
- this.entries = entries;
- }
-
- @Override
- public Optional<InputStream> tryGetInputStream(String name) throws IOException {
- return Optional.ofNullable(entries.get(name))
- .map(value -> new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)));
- }
-
- @Override
- public void close() throws IOException {
- }
-}