package org.zwobble.mammoth.tests;
import org.junit.jupiter.api.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.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
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" +
"." +
"
" +
"" +
"
"));
}
@Test
public void endNotesAreAppendedToText() throws IOException {
assertThat(
convertToHtml("endnotes.docx", mammoth -> mammoth.idPrefix("doc-42-")),
isSuccess(
"Ouch" +
"[1]." +
"[2]
" +
" A tachyon walks into a bar. ↑
" +
" Fin. ↑
"));
}
@Test
public void whenStyleMappingIsDefinedForCommentReferencesThenCommentsAreIncluded() throws IOException {
assertThat(
convertToHtml("comments.docx", mammoth -> mammoth.idPrefix("doc-42-").addStyleMap("comment-reference => sup")),
isSuccess(
"Ouch" +
"." +
"
" +
"A tachyon walks into a bar. ↑
" +
"Fin. ↑
"
));
}
@Test
public void relationshipsAreReadForEachFileContainingBodyXml() throws IOException {
assertThat(
convertToHtml("footnote-hyperlink.docx", mammoth -> mammoth.idPrefix("doc-42-")),
isSuccess(
"
" +
"
"));
}
@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"));
}
@Test
public void canReadStrictFormat() throws IOException {
assertThat(
convertToHtml("strict-format.docx"),
isSuccess("Test
")
);
}
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);
}
}