aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/mammoth/Mammoth.java16
-rw-r--r--src/main/java/org/zwobble/mammoth/documents/DocumentElementVisitor.java2
-rw-r--r--src/main/java/org/zwobble/mammoth/documents/Image.java39
-rw-r--r--src/main/java/org/zwobble/mammoth/docx/BodyXmlReader.java28
-rw-r--r--src/main/java/org/zwobble/mammoth/docx/ContentTypes.java4
-rw-r--r--src/main/java/org/zwobble/mammoth/docx/DocxFile.java9
-rw-r--r--src/main/java/org/zwobble/mammoth/docx/ZippedDocxFile.java25
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java23
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java23
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java28
10 files changed, 185 insertions, 12 deletions
diff --git a/src/main/java/org/zwobble/mammoth/Mammoth.java b/src/main/java/org/zwobble/mammoth/Mammoth.java
index 9564a6f..0651944 100644
--- a/src/main/java/org/zwobble/mammoth/Mammoth.java
+++ b/src/main/java/org/zwobble/mammoth/Mammoth.java
@@ -8,24 +8,25 @@ import org.zwobble.mammoth.xml.XmlElement;
import java.io.File;
import java.io.IOException;
-import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static com.google.common.collect.Iterables.transform;
public class Mammoth {
public static Result<String> convertToHtml(File file) {
- try (ZipFile zipFile = new ZipFile(file)) {
- ZipEntry entry = zipFile.getEntry("word/document.xml");
- XmlElement documentXml = OfficeXml.parseXml(zipFile.getInputStream(entry));
+ try (DocxFile zipFile = new ZippedDocxFile(new ZipFile(file))) {
+ XmlElement documentXml = OfficeXml.parseXml(zipFile.getInputStream("word/document.xml"));
Styles styles = Styles.EMPTY;
Numbering numbering = Numbering.EMPTY;
Relationships relationships = Relationships.EMPTY;
+ ContentTypes contentTypes = ContentTypes.DEFAULT;
DocumentXmlReader reader = new DocumentXmlReader(new BodyXmlReader(
styles,
numbering,
- relationships));
+ relationships,
+ contentTypes,
+ zipFile));
return reader.readElement(documentXml)
.map(Mammoth::convertToHtml);
} catch (IOException e) {
@@ -104,6 +105,11 @@ public class Mammoth {
public String visit(NoteReference noteReference) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public String visit(Image image) {
+ throw new UnsupportedOperationException();
+ }
});
}
}
diff --git a/src/main/java/org/zwobble/mammoth/documents/DocumentElementVisitor.java b/src/main/java/org/zwobble/mammoth/documents/DocumentElementVisitor.java
index f3a4ca4..6874e87 100644
--- a/src/main/java/org/zwobble/mammoth/documents/DocumentElementVisitor.java
+++ b/src/main/java/org/zwobble/mammoth/documents/DocumentElementVisitor.java
@@ -15,4 +15,6 @@ public interface DocumentElementVisitor<T> {
T visit(Hyperlink hyperlink);
T visit(Bookmark bookmark);
T visit(NoteReference noteReference);
+
+ T visit(Image image);
}
diff --git a/src/main/java/org/zwobble/mammoth/documents/Image.java b/src/main/java/org/zwobble/mammoth/documents/Image.java
new file mode 100644
index 0000000..7435cbf
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/documents/Image.java
@@ -0,0 +1,39 @@
+package org.zwobble.mammoth.documents;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Optional;
+
+public class Image implements DocumentElement {
+ @FunctionalInterface
+ public interface InputStreamSupplier {
+ InputStream open() throws IOException;
+ }
+
+ private final Optional<String> altText;
+ private final Optional<String> contentType;
+ private final InputStreamSupplier open;
+
+ public Image(Optional<String> altText, Optional<String> contentType, InputStreamSupplier open) {
+ this.altText = altText;
+ this.contentType = contentType;
+ this.open = open;
+ }
+
+ public Optional<String> getAltText() {
+ return altText;
+ }
+
+ public Optional<String> getContentType() {
+ return contentType;
+ }
+
+ public InputStream open() throws IOException {
+ return open.open();
+ }
+
+ @Override
+ public <T> T accept(DocumentElementVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/docx/BodyXmlReader.java b/src/main/java/org/zwobble/mammoth/docx/BodyXmlReader.java
index 19f649a..4a0a169 100644
--- a/src/main/java/org/zwobble/mammoth/docx/BodyXmlReader.java
+++ b/src/main/java/org/zwobble/mammoth/docx/BodyXmlReader.java
@@ -17,16 +17,27 @@ 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;
-
- public BodyXmlReader(Styles styles, Numbering numbering, 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) {
@@ -62,6 +73,9 @@ public class BodyXmlReader {
case "w:pict":
return readPict(element);
+ case "v:imagedata":
+ return readImagedata(element);
+
case "w:ins":
case "w:smartTag":
case "w:drawing":
@@ -228,6 +242,16 @@ public class BodyXmlReader {
return readElements(element.children()).toExtra();
}
+ private ReadResult readImagedata(XmlElement element) {
+ Optional<String> title = element.getAttributeOrNone("o:title");
+ String relationshipId = element.getAttribute("r:id");
+ Relationship relationship = relationships.findRelationshipById(relationshipId);
+ String imagePath = "word/" + trimLeft(relationship.getTarget(), '/');
+ Optional<String> contentType = contentTypes.findContentType(imagePath);
+ Image image = new Image(title, contentType, () -> file.getInputStream(imagePath));
+ return success(image);
+ }
+
private Optional<String> readVal(XmlElementLike element, String name) {
return element.findChildOrEmpty(name).getAttributeOrNone("w:val");
}
diff --git a/src/main/java/org/zwobble/mammoth/docx/ContentTypes.java b/src/main/java/org/zwobble/mammoth/docx/ContentTypes.java
index f36ca49..81c3c1a 100644
--- a/src/main/java/org/zwobble/mammoth/docx/ContentTypes.java
+++ b/src/main/java/org/zwobble/mammoth/docx/ContentTypes.java
@@ -6,7 +6,11 @@ import com.google.common.io.Files;
import java.util.Map;
import java.util.Optional;
+import static org.zwobble.mammoth.util.MammothMaps.map;
+
public class ContentTypes {
+ public static final ContentTypes DEFAULT = new ContentTypes(map(), map());
+
private static final Map<String, String> imageExtensions = ImmutableMap.<String, String>builder()
.put("png", "png")
.put("gif", "gif")
diff --git a/src/main/java/org/zwobble/mammoth/docx/DocxFile.java b/src/main/java/org/zwobble/mammoth/docx/DocxFile.java
new file mode 100644
index 0000000..b05ee1f
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/docx/DocxFile.java
@@ -0,0 +1,9 @@
+package org.zwobble.mammoth.docx;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface DocxFile extends Closeable {
+ InputStream getInputStream(String name) throws IOException;
+}
diff --git a/src/main/java/org/zwobble/mammoth/docx/ZippedDocxFile.java b/src/main/java/org/zwobble/mammoth/docx/ZippedDocxFile.java
new file mode 100644
index 0000000..6b8528a
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/docx/ZippedDocxFile.java
@@ -0,0 +1,25 @@
+package org.zwobble.mammoth.docx;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class ZippedDocxFile implements DocxFile {
+ private final ZipFile zipFile;
+
+ public ZippedDocxFile(ZipFile zipFile) {
+ this.zipFile = zipFile;
+ }
+
+ @Override
+ public InputStream getInputStream(String name) throws IOException {
+ ZipEntry entry = zipFile.getEntry("word/document.xml");
+ return zipFile.getInputStream(entry);
+ }
+
+ @Override
+ public void close() throws IOException {
+ zipFile.close();
+ }
+}
diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java
index 5758dce..41cefd5 100644
--- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java
+++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java
@@ -2,10 +2,10 @@ package org.zwobble.mammoth.tests.docx;
import com.natpryce.makeiteasy.Instantiator;
import com.natpryce.makeiteasy.Property;
-import org.zwobble.mammoth.docx.BodyXmlReader;
-import org.zwobble.mammoth.docx.Numbering;
-import org.zwobble.mammoth.docx.Relationships;
-import org.zwobble.mammoth.docx.Styles;
+import org.zwobble.mammoth.docx.*;
+
+import java.io.IOException;
+import java.io.InputStream;
import static com.natpryce.makeiteasy.Property.newProperty;
@@ -13,10 +13,23 @@ public class BodyXmlReaderMakers {
public static final Property<BodyXmlReader, Styles> STYLES = newProperty();
public static final Property<BodyXmlReader, Numbering> NUMBERING = newProperty();
public static final Property<BodyXmlReader, Relationships> RELATIONSHIPS = newProperty();
+ public static final Property<BodyXmlReader, ContentTypes> CONTENT_TYPES = newProperty();
+ public static final Property<BodyXmlReader, DocxFile> DOCX_FILE = newProperty();
public static final Instantiator<BodyXmlReader> bodyReader =
propertyLookup -> new BodyXmlReader(
propertyLookup.valueOf(STYLES, Styles.EMPTY),
propertyLookup.valueOf(NUMBERING, Numbering.EMPTY),
- propertyLookup.valueOf(RELATIONSHIPS, Relationships.EMPTY));
+ propertyLookup.valueOf(RELATIONSHIPS, Relationships.EMPTY),
+ propertyLookup.valueOf(CONTENT_TYPES, ContentTypes.DEFAULT),
+ propertyLookup.valueOf(DOCX_FILE, new DocxFile() {
+ @Override
+ public InputStream getInputStream(String name) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ }));
}
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 c8f8887..cc0815e 100644
--- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java
+++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java
@@ -1,5 +1,6 @@
package org.zwobble.mammoth.tests.docx;
+import com.google.common.io.CharStreams;
import com.natpryce.makeiteasy.Maker;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
@@ -13,12 +14,15 @@ import org.zwobble.mammoth.xml.XmlElement;
import org.zwobble.mammoth.xml.XmlNode;
import org.zwobble.mammoth.xml.XmlNodes;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.util.List;
import java.util.Optional;
import static com.natpryce.makeiteasy.MakeItEasy.*;
import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasProperty;
import static org.zwobble.mammoth.documents.NoteReference.endnoteReference;
@@ -381,6 +385,25 @@ public class BodyXmlTests {
}
@Test
+ public void canReadImagedataElementsWithIdAttribute() throws IOException {
+ XmlElement element = element("v:imagedata", map("r:id", "rId5", "o:title", "It's a hat"));
+ 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));
+
+ Image image = (Image) readSuccess(
+ a(bodyReader, with(RELATIONSHIPS, relationships), with(DOCX_FILE, file)),
+ element);
+ assertThat(image, allOf(
+ hasProperty("altText", deepEquals(Optional.of("It's a hat"))),
+ hasProperty("contentType", deepEquals(Optional.of("image/png")))));
+ assertThat(
+ CharStreams.toString(new InputStreamReader(image.open())),
+ equalTo("Not an image at all!"));
+ }
+
+ @Test
public void appropriateElementsHaveTheirChildrenReadNormally() {
assertChildrenAreReadNormally("w:ins");
assertChildrenAreReadNormally("w:smartTag");
diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java b/src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java
new file mode 100644
index 0000000..2ed5e27
--- /dev/null
+++ b/src/test/java/org/zwobble/mammoth/tests/docx/InMemoryDocxFile.java
@@ -0,0 +1,28 @@
+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 InputStream getInputStream(String name) throws IOException {
+ String value = Optional.ofNullable(entries.get(name)).get();
+ return new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+}