package org.zwobble.mammoth.tests; import org.junit.Test; import org.zwobble.mammoth.DocumentConverter; import org.zwobble.mammoth.Result; import org.zwobble.mammoth.images.ImageConverter; import org.zwobble.mammoth.internal.archives.InMemoryArchive; import org.zwobble.mammoth.internal.docx.EmbeddedStyleMap; import org.zwobble.mammoth.internal.styles.parsing.ParseException; import org.zwobble.mammoth.internal.util.Base64Encoding; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.function.Function; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; import static org.zwobble.mammoth.internal.util.Lists.list; import static org.zwobble.mammoth.internal.util.Maps.map; import static org.zwobble.mammoth.tests.ResultMatchers.isResult; import static org.zwobble.mammoth.tests.ResultMatchers.isSuccess; import static org.zwobble.mammoth.tests.util.MammothAsserts.assertThrows; public class MammothTests { @Test public void docxContainingOneParagraphIsConvertedToSingleParagraphElement() throws IOException { assertThat( convertToHtml("single-paragraph.docx"), isSuccess("

Walking on imported air

")); } @Test public void canReadFilesWithUtf8Bom() throws IOException { assertThat( convertToHtml("utf8-bom.docx"), isSuccess("

This XML has a byte order mark.

")); } @Test public void emptyParagraphsAreIgnoredByDefault() throws IOException { assertThat( convertToHtml("empty.docx"), isSuccess("")); } @Test public void emptyParagraphsArePreservedIfIgnoreEmptyParagraphsIsFalse() throws IOException { assertThat( convertToHtml("empty.docx", mammoth -> mammoth.preserveEmptyParagraphs()), isSuccess("

")); } @Test public void simpleListIsConvertedToListElements() throws IOException { assertThat( convertToHtml("simple-list.docx"), isSuccess("")); } @Test public void wordTablesAreConvertedToHtmlTables() throws IOException { assertThat( convertToHtml("tables.docx"), isSuccess( "

Above

" + "" + "" + "" + "

Top left

Top right

Bottom left

Bottom right

" + "

Below

")); } @Test public void inlineImagesReferencedByPathRelativeToPartAreIncludedInOutput() throws IOException { assertThat( convertToHtml("tiny-picture.docx"), isSuccess("

")); } @Test public void inlineImagesReferencedByPathRelativeToBaseAreIncludedInOutput() throws IOException { assertThat( convertToHtml("tiny-picture-target-base-relative.docx"), isSuccess("

")); } @Test public void imagesStoredOutsideOfDocumentAreIncludedInOutput() throws IOException { Path tempDirectory = Files.createTempDirectory("mammoth-"); try { Path documentPath = tempDirectory.resolve("external-picture.docx"); Files.copy(TestData.file("external-picture.docx").toPath(), documentPath); Files.copy(TestData.file("tiny-picture.png").toPath(), tempDirectory.resolve("tiny-picture.png")); assertThat( new DocumentConverter().convertToHtml(documentPath.toFile()), isSuccess("

")); } finally { tempDirectory.toFile().delete(); } } @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( new DocumentConverter().convertToHtml(documentPath.toUri().toURL().openStream()), allOf( hasProperty("value", equalTo("")), hasProperty("warnings", contains( 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 { Path documentPath = tempDirectory.resolve("external-picture.docx"); Files.copy(TestData.file("external-picture.docx").toPath(), documentPath); assertThat( new DocumentConverter().convertToHtml(documentPath.toFile()), allOf( hasProperty("value", equalTo("")), hasProperty("warnings", contains( startsWith("could not open external image 'tiny-picture.png'"))))); } finally { tempDirectory.toFile().delete(); } } @Test public void imageConversionCanBeCustomised() throws IOException { ImageConverter.ImgElement imageConverter = image -> { String base64 = Base64Encoding.streamToBase64(image::getInputStream); String src = base64.substring(0, 2) + "," + image.getContentType(); return map("src", src); }; assertThat( convertToHtml("tiny-picture.docx", converter -> converter.imageConverter(imageConverter)), isSuccess("

") ); } @Test public void contentTypesAreRead() throws IOException { assertThat( convertToHtml("tiny-picture-custom-content-type.docx"), isSuccess("

")); } @Test public void footnotesAreAppendedToText() throws IOException { assertThat( convertToHtml("footnotes.docx", mammoth -> mammoth.idPrefix("doc-42-")), isSuccess( "

Ouch" + "[1]." + "[2]

" + "
  1. A tachyon walks into a bar. ↑

  2. " + "
  3. Fin. ↑

")); } @Test public void endNotesAreAppendedToText() throws IOException { assertThat( convertToHtml("endnotes.docx", mammoth -> mammoth.idPrefix("doc-42-")), isSuccess( "

Ouch" + "[1]." + "[2]

" + "
  1. A tachyon walks into a bar. ↑

  2. " + "
  3. Fin. ↑

")); } @Test public void whenStyleMappingIsDefinedForCommentReferencesThenCommentsAreIncluded() throws IOException { assertThat( convertToHtml("comments.docx", mammoth -> mammoth.idPrefix("doc-42-").addStyleMap("comment-reference => sup")), isSuccess( "

Ouch" + "[MW1]." + "[MW2]

" + "
Comment [MW1]

A tachyon walks into a bar. ↑

" + "
Comment [MW2]

Fin. ↑

" )); } @Test public void relationshipsAreReadForEachFileContainingBodyXml() throws IOException { assertThat( convertToHtml("footnote-hyperlink.docx", mammoth -> mammoth.idPrefix("doc-42-")), isSuccess( "

[1]

" + "
  1. Example ↑

")); } @Test public void textBoxesAreRead() throws IOException { assertThat( convertToHtml("text-box.docx"), isSuccess("

Datum plane

")); } @Test public void canUseCustomStyleMap() throws IOException { assertThat( convertToHtml("underline.docx", mammoth -> mammoth.addStyleMap("u => em")), isSuccess("

The Sunset Tree

")); } @Test public void mostRecentlyAddedStyleMapTakesPrecedence() throws IOException { assertThat( convertToHtml("underline.docx", mammoth -> mammoth.addStyleMap("u => em").addStyleMap("u => strong")), isSuccess("

The Sunset Tree

")); } @Test public void rulesFromPreviouslyAddedStyleMapsStillTakeEffectIfNotOverriden() throws IOException { assertThat( convertToHtml("underline.docx", mammoth -> mammoth.addStyleMap("u => em").addStyleMap("strike => del")), isSuccess("

The Sunset Tree

")); } @Test public void errorIsRaisedIfStyleMapCannotBeParsed() throws IOException { RuntimeException exception = assertThrows( ParseException.class, () -> new DocumentConverter().addStyleMap("p =>\np[style-name=] =>")); assertThat( exception.getMessage(), equalTo( "error reading style map at line 2, character 14: expected token of type STRING but was of type SYMBOL\n\n" + "p[style-name=] =>\n" + " ^")); } @Test public void canDisableDefaultStyleMap() throws IOException { assertThat( convertToHtml("simple-list.docx", mammoth -> mammoth.disableDefaultStyleMap()), isResult( equalTo("

Apple

Banana

"), list("Unrecognised paragraph style: List Paragraph (Style ID: ListParagraph)"))); } @Test public void embeddedStyleMapIsUsedIfPresent() throws IOException { assertThat( convertToHtml("embedded-style-map.docx"), isSuccess("

Walking on imported air

") ); } @Test public void explicitStyleMapTakesPrecedenceOverEmbeddedStyleMap() throws IOException { assertThat( convertToHtml("embedded-style-map.docx", options -> options.addStyleMap("p => p")), isSuccess("

Walking on imported air

") ); } @Test public void explicitStyleMapIsCombinedWithEmbeddedStyleMap() throws IOException { assertThat( convertToHtml("embedded-style-map.docx", options -> options.addStyleMap("r => strong")), isSuccess("

Walking on imported air

") ); } @Test public void embeddedStyleMapsCanBeDisabled() throws IOException { assertThat( convertToHtml("embedded-style-map.docx", options -> options.disableEmbeddedStyleMap()), isSuccess("

Walking on imported air

") ); } @Test public void embeddedStyleMapCanBeWrittenAndThenRead() throws IOException { InMemoryArchive archive = InMemoryArchive.fromStream(new FileInputStream(TestData.file("single-paragraph.docx"))); EmbeddedStyleMap.embedStyleMap(archive, "p => h1"); assertThat( new DocumentConverter().convertToHtml(new ByteArrayInputStream(archive.toByteArray())), isSuccess("

Walking on imported air

") ); } @Test public void canExtractRawTextFromFile() throws IOException { assertThat( new DocumentConverter().extractRawText(TestData.file("simple-list.docx")), isSuccess("Apple\n\nBanana\n\n")); } @Test public void canExtractRawTextFromStream() throws IOException { assertThat( new DocumentConverter().extractRawText(TestData.file("simple-list.docx").toURI().toURL().openStream()), isSuccess("Apple\n\nBanana\n\n")); } private Result convertToHtml(String name) throws IOException { File file = TestData.file(name); return new DocumentConverter().convertToHtml(file); } private Result convertToHtml(String name, Function configure) throws IOException { File file = TestData.file(name); return configure.apply(new DocumentConverter()).convertToHtml(file); } }