using Xunit;
using System.IO;
using Xunit.Sdk;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Mammoth.Tests {
public class DocumentConverterTests {
[Fact]
public void DocxContainingOneParagraphIsConvertedToSingleParagraphElement() {
AssertSuccessfulConversion(
ConvertToHtml("single-paragraph.docx"),
"
Walking on imported air
");
}
[Fact]
public void CanReadFilesWithUtf8Bom() {
AssertSuccessfulConversion(
ConvertToHtml("utf8-bom.docx"),
"This XML has a byte order mark.
");
}
[Fact]
public void EmptyParagraphsAreIgnoredByDefault() {
AssertSuccessfulConversion(
ConvertToHtml("empty.docx"),
"");
}
[Fact]
public void EmptyParagraphsArePreservedIfIgnoreEmptyParagraphsIsFalse() {
AssertSuccessfulConversion(
ConvertToHtml("empty.docx", converter => converter.PreserveEmptyParagraphs()),
"");
}
[Fact]
public void SimpleListIsConvertedToListElements() {
AssertSuccessfulConversion(
ConvertToHtml("simple-list.docx"),
"");
}
[Fact]
public void WordTablesAreConvertedToHtmlTables() {
AssertSuccessfulConversion(
ConvertToHtml("tables.docx"),
"Above
" +
"" +
"Top left | Top right |
" +
"Bottom left | Bottom right |
" +
"
" +
"Below
");
}
[Fact]
public void InlineImagesReferencedByPathRelativeToPartAreIncludedInOutput() {
AssertSuccessfulConversion(
ConvertToHtml("tiny-picture.docx"),
"
");
}
[Fact]
public void InlineImagesReferencedByPathRelativeToBaseAreIncludedInOutput() {
AssertSuccessfulConversion(
ConvertToHtml("tiny-picture-target-base-relative.docx"),
"
");
}
[Fact]
public void WhenExternalFileAccessIsEnabledThenImagesStoredOutsideOfDocumentAreIncludedInOutput() {
AssertSuccessfulConversion(
ConvertToHtml("external-picture.docx", mammoth => mammoth.EnableExternalFileAccess()),
"
");
}
[Fact]
public void WhenExternalFileAccessIsEnabledThenWarnIfDocumentHasImagesStoredOutsideOfDocumentWhenPathOfDocumentIsUnknown() {
using (var file = File.OpenRead(TestFilePath("external-picture.docx"))) {
var result = new DocumentConverter()
.EnableExternalFileAccess()
.ConvertToHtml(file);
Assert.Equal("", result.Value);
Assert.Equal(new[]{"could not open external image 'tiny-picture.png': path of document is unknown, but is required for relative URI"}, result.Warnings);
}
}
[Fact]
public void GivenExternalFileAccessIsDisabledByDefaultThenWarnIfDocumentHasImagesStoredOutsideOfDocument()
{
using (var file = File.OpenRead(TestFilePath("external-picture.docx"))) {
var result = new DocumentConverter().ConvertToHtml(file);
Assert.Equal("", result.Value);
Assert.Equal(new[]{"could not open external image 'tiny-picture.png': external file access is disabled"}, result.Warnings);
}
}
[Fact]
public void WhenExternalFileAccessIsEnabledThenWarnIfImagesStoredOutsideOfDocumentAreNotFound() {
var tempDirectory = Path.Combine(Path.GetTempPath(), "mammoth-" + Guid.NewGuid());
Directory.CreateDirectory(tempDirectory);
try {
var documentPath = Path.Combine(tempDirectory, "external-picture.docx");
File.Copy(TestFilePath("external-picture.docx"), documentPath);
var result = new DocumentConverter().ConvertToHtml(documentPath);
Assert.Equal("", result.Value);
Assert.StartsWith("could not open external image 'tiny-picture.png':", result.Warnings.Single());
} finally {
Directory.Delete(tempDirectory, recursive: true);
}
}
[Fact]
public void ImageConversionCanBeCustomised() {
AssertSuccessfulConversion(
ConvertToHtml("tiny-picture.docx", mammoth => mammoth.ImageConverter(ConvertImage)),
"
"
);
}
private IDictionary ConvertImage(IImage image) {
using (var stream = image.GetStream()) {
var base64 = StreamToBase64(stream);
var src = base64.Substring(0, 2) + "," + image.ContentType;
return new Dictionary { { "src", src } };
}
}
private static string StreamToBase64(System.IO.Stream stream) {
var memoryStream = new System.IO.MemoryStream();
stream.CopyTo(memoryStream);
return System.Convert.ToBase64String(memoryStream.ToArray());
}
[Fact]
public void ContentTypesAreRead() {
AssertSuccessfulConversion(
ConvertToHtml("tiny-picture-custom-content-type.docx"),
"
");
}
[Fact]
public void FootnotesAreAppendedToText() {
AssertSuccessfulConversion(
ConvertToHtml("footnotes.docx", mammoth => mammoth.IdPrefix("doc-42-")),
"Ouch" +
"." +
"
" +
"" +
"
");
}
[Fact]
public void EndNotesAreAppendedToText() {
AssertSuccessfulConversion(
ConvertToHtml("endnotes.docx", mammoth => mammoth.IdPrefix("doc-42-")),
"Ouch" +
"[1]." +
"[2]
" +
" A tachyon walks into a bar. ↑
" +
" Fin. ↑
");
}
[Fact]
public void RelationshipsAreReadForEachFileContainingBodyXml() {
AssertSuccessfulConversion(
ConvertToHtml("footnote-hyperlink.docx", mammoth => mammoth.IdPrefix("doc-42-")),
"
" +
"
");
}
[Fact]
public void TextBoxesAreRead() {
AssertSuccessfulConversion(
ConvertToHtml("text-box.docx"),
"Datum plane
");
}
[Fact]
public void CanUseCustomStyleMap() {
AssertSuccessfulConversion(
ConvertToHtml("underline.docx", mammoth => mammoth.AddStyleMap("u => em")),
"The Sunset Tree
");
}
[Fact]
public void MostRecentlyAddedStyleMapTakesPrecedence() {
AssertSuccessfulConversion(
ConvertToHtml("underline.docx", mammoth => mammoth.AddStyleMap("u => em").AddStyleMap("u => strong")),
"The Sunset Tree
");
}
[Fact]
public void RulesFromPreviouslyAddedStyleMapsStillTakeEffectIfNotOverridden() {
AssertSuccessfulConversion(
ConvertToHtml("underline.docx", mammoth => mammoth.AddStyleMap("u => em").AddStyleMap("strike => del")),
"The Sunset Tree
");
}
[Fact]
public void ErrorIsRaisedIfStyleMapCannotBeParsed() {
var exception = Assert.ThrowsAny(() =>
new DocumentConverter().AddStyleMap("p =>\np[style-name=] =>"));
Assert.Equal(
"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" +
" ^",
exception.Message);
}
[Fact]
public void CanDisableDefaultStyleMap() {
var result = ConvertToHtml("simple-list.docx", mammoth => mammoth.DisableDefaultStyleMap());
Assert.Equal(new[]{"Unrecognised paragraph style: List Paragraph (Style ID: ListParagraph)"}, result.Warnings);
Assert.Equal("Apple
Banana
", result.Value);
}
[Fact]
public void EmbeddedStyleMapIsUsedIfPresent() {
AssertSuccessfulConversion(
ConvertToHtml("embedded-style-map.docx"),
"Walking on imported air
"
);
}
[Fact]
public void ExplicitStyleMapTakesPrecedenceOverEmbeddedStyleMap() {
AssertSuccessfulConversion(
ConvertToHtml("embedded-style-map.docx", mammoth => mammoth.AddStyleMap("p => p")),
"Walking on imported air
"
);
}
[Fact]
public void ExplicitStyleMapIsCombinedWithEmbeddedStyleMap() {
AssertSuccessfulConversion(
ConvertToHtml("embedded-style-map.docx", mammoth => mammoth.AddStyleMap("r => strong")),
"Walking on imported air
"
);
}
[Fact]
public void embeddedStyleMapsCanBeDisabled() {
AssertSuccessfulConversion(
ConvertToHtml("embedded-style-map.docx", mammoth => mammoth.DisableEmbeddedStyleMap()),
"Walking on imported air
"
);
}
[Fact]
public void CanExtractRawTextFromFile() {
AssertSuccessfulConversion(
new DocumentConverter().ExtractRawText(TestFilePath("simple-list.docx")),
"Apple\n\nBanana\n\n");
}
[Fact]
public void CanExtractRawTextFromStream() {
using (var file = File.OpenRead(TestFilePath("simple-list.docx"))) {
AssertSuccessfulConversion(
new DocumentConverter().ExtractRawText(file),
"Apple\n\nBanana\n\n");
}
}
private void AssertSuccessfulConversion(IResult result, string expectedValue) {
if (result.Warnings.Count > 0) {
throw new XunitException("Unexpected warnings: " + string.Join(", ", result.Warnings));
}
Assert.Equal(expectedValue, result.Value);
}
private IResult ConvertToHtml(string name) {
return ConvertToHtml(name, converter => converter);
}
private IResult ConvertToHtml(string name, Func configure) {
return configure(new DocumentConverter()).ConvertToHtml(TestFilePath(name));
}
private string TestFilePath(string name) {
return Path.Combine("../../../TestData", name);
}
}
}