Morph

June 6, 2026 · View on GitHub

Build status NuGet Status NuGet Status NuGet Status

A .NET library that converts Microsoft Word DOCX documents or HTML content into PNG images.

Open Source Maintenance Fee

This project participates in the Open Source Maintenance Fee. The source code is freely available under the terms of the license. To support sustainable maintenance, use of the project's official binary releases in revenue-generating activities and all government agencies requires adherence to the Open Source Maintenance Fee EULA. The fee is paid by sponsoring Papyrine.

This project uses SponsorCheck to surface a build-time reminder in consuming projects that are not yet sponsoring.

Requirements

  • .NET 10.0 or later
  • Cross-platform support: Windows, macOS, Linux

NuGet packages

DOCX or HTML to PNG

A single package per backend converts both Word documents and HTML content to images:

https://nuget.org/packages/Morph.Skia/

https://nuget.org/packages/Morph.ImageSharp/

Features

Text Formatting

  • Font families and sizes
  • Bold, italic, underline, strikethrough
  • Text colors and highlighting
  • All caps, small caps
  • Superscript, subscript
  • Character spacing

Paragraph Formatting

  • Text alignment (left, right, center, justified)
  • Indentation (first-line, hanging, left, right)
  • Spacing (before, after, line spacing)
  • Contextual spacing
  • Paragraph borders

Document Structure

  • Multiple sections with different margins/orientation
  • Page breaks (manual and automatic)
  • Section breaks (continuous, next page, odd/even)
  • Headers and footers
  • Page numbering
  • Line numbering

Tables

  • Complex table structures with merged cells
  • Cell borders and shading
  • Table styles
  • Nested tables
  • Column widths

Lists

  • Bullet lists
  • Numbered lists
  • Multi-level lists with various numbering styles
  • Custom list formatting

Graphics

  • Embedded images (JPEG, PNG)
  • Shapes (rectangles, circles, etc.)
  • Drawing objects
  • SVG content
  • Ink/handwriting annotations

Advanced Features

  • Theme support (colors, fonts)
  • Compatibility modes (Word 2007 and later)
  • Font resolution — name-table-driven, deterministic across platforms
  • Hyphenation
  • HTML content via AltChunk

Export fidelity (HTML / Markdown / PDF)

The HTML, Markdown and PDF exporters share the DOCX parser above, so the same content carries across — each within the limits of its format:

  • HTML — semantic tags (<strong>, <em>, <u>, <h1><h6>, <table>, <ul>/<ol>) over one embedded stylesheet, with inline overrides only where a run or paragraph deviates from the document defaults. Theme colours (including themeShade / themeTint), per-run fonts (with generic fallbacks) and sizes, the page background, paragraph spacing / indentation / alignment / borders, and table cell widths / shading / borders / vertical alignment are preserved; background shapes, gradients and accent panels are emitted as inline SVG behind the text.
  • Markdown — Pandoc-flavoured CommonMark with GFM pipe tables; adjacent runs are coalesced so emphasis stays well-formed and headings stay clean.
  • PDF — vector text via PdfSharp, paginated to match the source page layout.

Rendering Backends

Morph supports two rendering backends:

BackendPackage (DOCX + HTML)Pros
SkiaSharpMorph.SkiaMature, includes SVG support
ImageSharpMorph.ImageSharpFully managed (no native dependencies)

Usage

DOCX to PNG

The examples below use the SkiaSharp backend. To use ImageSharp instead, replace WordRender.Skia.DocumentConverter with WordRender.ImageSharp.DocumentConverter.

Basic Usage - Save to Files

var converter = new SkiaDocumentConverter();

var result = converter.ConvertToImages(
    "document.docx",
    "output-folder");

Console.WriteLine($"Generated {result.PageCount} pages");
foreach (var path in result.ImagePaths)
{
    Console.WriteLine($"Created: {path}");
}

snippet source | anchor

In-Memory Conversion

var converter = new SkiaDocumentConverter();

var imageData = converter.ConvertToImageData("document.docx");

foreach (var pngBytes in imageData)
{
    // Use the PNG byte array as needed
}

snippet source | anchor

Stream-Based Conversion

var converter = new SkiaDocumentConverter();

using var stream = File.OpenRead("document.docx");

// From stream to files
var result = converter.ConvertToImages(stream, "output-folder");

// Or from stream to memory
var imageData = converter.ConvertToImageData(stream);

snippet source | anchor

With Custom Options

var converter = new SkiaDocumentConverter();

var options = new ImageExportOptions
{
    Dpi = 300,
    FontWidthScale = 1.07
};

var result = converter.ConvertToImages(
    "document.docx",
    "output-folder",
    options);

snippet source | anchor

DOCX to HTML / Markdown / PDF

Morph also serializes a DOCX directly to semantic HTML, Pandoc-flavoured Markdown, or a vector-text PDF — no backend choice needed for HTML / Markdown. Per-format options classes (HtmlExportOptions, MarkdownExportOptions, PdfExportOptions) carry the knobs relevant to each output.

The HTML output renders background shapes, gradients and accent panels from the source as inline SVG behind the text, so coloured backgrounds and decorative artwork survive the conversion.

Basic — HTML

var html = DocumentConverter.ConvertToHtml("document.docx");
File.WriteAllText("document.html", html);

snippet source | anchor

Basic — Markdown

var markdown = DocumentConverter.ConvertToMarkdown("document.docx");
File.WriteAllText("document.md", markdown);

snippet source | anchor

Basic — PDF

var outputPath = "document.pdf";
PdfDocumentConverter.ConvertToPdf("document.docx", outputPath);

snippet source | anchor

Parse once, export many

For multi-format export, WordDocument parses the source a single time and supports calling as many ExportToXxx methods as needed. The PDF extension method comes from Morph.Pdf; HTML and Markdown are built in.

// Parse once with WordDocument, then export to as many formats as you like — the source
// .docx is only opened and parsed a single time.
var document = new WordDocument("document.docx");

File.WriteAllText("document.html", document.ExportToHtml());
File.WriteAllText("document.md",   document.ExportToMarkdown());
document.ExportToPdf("document.pdf");   // extension method from Morph.Pdf

snippet source | anchor

Custom image handling

By default Morph inlines images as base64 data URIs. Pass an ImageHandler to write images to disk, upload them to a CDN, or reference them however suits the calling pipeline.

// Write images to a media folder and reference them relatively, instead of base64-inlining.
Directory.CreateDirectory("media");
var html = DocumentConverter.ConvertToHtml(
    "document.docx",
    new()
    {
        ImageHandler = image =>
        {
            var extension = image.ContentType switch
            {
                "image/svg+xml" => "svg",
                "image/jpeg"    => "jpg",
                _               => "png"
            };
            var path = $"media/image-{image.Index}.{extension}";
            File.WriteAllBytes(path, image.Data);
            return path;
        }
    });

snippet source | anchor

Warning callback

Some source features can't always be represented in the chosen output (ink strokes in HTML, foreground vector shapes in PDF, etc.). Pass an OnWarning callback to discover what was dropped.

// Discover features in the source that couldn't be fully represented in the output —
// unsupported elements (ink strokes, vector shapes), missing fonts, etc.
var warnings = new List<ExportWarning>();
var html = DocumentConverter.ConvertToHtml(
    "document.docx",
    new()
    {
        OnWarning = warning => warnings.Add(warning)
    });

foreach (var warning in warnings)
{
    Console.WriteLine($"[{warning.Kind}] {warning.Message}");
}

snippet source | anchor

PDF page range

Render only specific pages — useful for previews / thumbnails.

// Render only the first three pages of the document.
var firstThreePages = PdfDocumentConverter.ConvertToPdf(
    "document.docx",
    new()
    {
        Pages = new(Start: 1, End: 3)
    });

File.WriteAllBytes("document-preview.pdf", firstThreePages);

snippet source | anchor

HTML to PNG

To use ImageSharp instead, replace HtmlRender.Skia.HtmlConverter with HtmlRender.ImageSharp.HtmlConverter.

Basic Usage - Save to Files

var converter = new HtmlRender.Skia.HtmlConverter();

var result = await converter.ConvertToImages(
    "<h1>Hello</h1><p>World</p>",
    "output-folder");

Console.WriteLine($"Generated {result.PageCount} pages");
foreach (var path in result.ImagePaths)
{
    Console.WriteLine($"Created: {path}");
}

In-Memory Conversion

var converter = new HtmlRender.Skia.HtmlConverter();

var imageData = await converter.ConvertToImageData("<h1>Hello</h1><p>World</p>");

foreach (var pngBytes in imageData)
{
    // Use the PNG byte array as needed
}

Configuration Options

OptionTypeDefaultDescription
Dpiint150Image resolution in dots per inch

Font-related options (FontDirectory, FontFallback, DefaultFont, FontWidthScale, DeterministicRendering) are documented in docs/fonts.md.

Icon

Impossible Star designed by Rflor from The Noun Project.