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" + "[1]." + "[2]

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

  2. " + "
  3. Fin. ↑

"); } [Fact] public void EndNotesAreAppendedToText() { AssertSuccessfulConversion( ConvertToHtml("endnotes.docx", mammoth => mammoth.IdPrefix("doc-42-")), "

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

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

  2. " + "
  3. Fin. ↑

"); } [Fact] public void RelationshipsAreReadForEachFileContainingBodyXml() { AssertSuccessfulConversion( ConvertToHtml("footnote-hyperlink.docx", mammoth => mammoth.IdPrefix("doc-42-")), "

[1]

" + "
  1. Example ↑

"); } [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); } } }