Table of Contents

Add your own formats

Every format DocWright reads or writes goes through a format registry. Add a reader for your own format and Convert, Load and ConvertToImages accept it. Add a writer and Save writes it.

A reader

This reader turns a CSV file into a document holding one table:

using DocWright.Core;
using DocWright.Core.Diagnostics;
using DocWright.Core.Formats;
using DocWright.Core.Primitives;
using DocWright.Dom;
using DocWright.Dom.Editing;

/// <summary>Reads a CSV file as a Word document holding one table.</summary>
internal sealed class CsvDocumentReader : IDocumentReader
{
    public DocumentFormatDescriptor Format { get; } = new("csv", "Comma-separated values", "text/csv", ".csv");

    // Sniffing must not consume the stream: the registry restores the position, but only read what you need.
    public bool CanRead(Stream stream)
    {
        var buffer = new byte[256];
        int read = stream.Read(buffer, 0, buffer.Length);
        string head = System.Text.Encoding.UTF8.GetString(buffer, 0, read);
        return head.Contains(',') && !head.Contains('<') && !head.StartsWith("{\\rtf", StringComparison.Ordinal);
    }

    public IDocWrightDocument Read(Stream input, DocumentReadOptions options, IConversionDiagnostics diagnostics)
    {
        using var reader = new StreamReader(input);
        List<string[]> rows = [];
        while (reader.ReadLine() is { } line)
        {
            rows.Add(line.Split(','));
        }

        if (rows.Count == 0)
        {
            throw new InvalidDocumentException("The CSV file is empty.");   // typed DocWright exceptions
        }

        var document = new WordDocument();
        Section section = document.AppendSection();
        int columns = rows.Max(r => r.Length);
        Table table = section.AppendTable(rows.Count, columns, Twips.FromInches(6.5 / columns));
        for (int r = 0; r < rows.Count; r++)
        {
            for (int c = 0; c < rows[r].Length; c++)
            {
                table.Rows[r].Cells[c].AppendParagraph(rows[r][c]);
            }
        }

        return document;
    }
}

Register it, and CSV converts like any other document:

using DocWright;

var converter = new DocWrightConverter();
converter.FormatRegistry.AddReader(new CsvDocumentReader());   // probed after the built-ins

using (FileStream input = File.OpenRead("customers.csv"))
using (FileStream output = File.Create("customers.pdf"))
{
    converter.Convert(input, output);                           // CSV is now detected like DOCX
}
customers.csv → customers.pdf
A CSV file rendered to PDF as a table

A reader should:

  1. Sniff without consuming: CanRead looks at the start of the stream. The registry restores the position afterwards.
  2. Return a WordDocument, built with the editing API.
  3. Honour DocumentReadOptions: StrictMode and Limits.
  4. Report, don't drop: send anything you can't represent to IConversionDiagnostics.
  5. Throw DocWright's exception types, such as InvalidDocumentException, so callers handle your format like the built-in ones.

Readers are probed in registration order, after the built-ins. A writer implements IDocumentWriter, declares its extensions in its DocumentFormatDescriptor, and receives DocumentWriteOptions.FormatSettings. Prefix your keys, for example myformat:, as the built-in writers do.

Configuring a built-in reader or writer

The registry holds one reader and one writer per format, and refuses a second. A default converter already has every built-in, so adding a configured HtmlWriter to it throws:

Output

A writer for format 'html' is already registered. (Parameter 'writer')

Instead, either call the configured writer directly (writer.Write(document, stream, …)), use SaveOptions.FormatSettings for writers that support it, or build your own registry:

using DocWright;
using DocWright.Core.Formats;
using DocWright.Formats.Docx;
using DocWright.Formats.Html;

// A built-in reader or writer with non-default options: build the registry yourself.
// The default registry already holds every built-in and refuses a second one per format.
var registry = new DocumentFormatRegistry();
registry.AddReader(new DocxReader());
registry.AddWriter(new DocxWriter());
registry.AddWriter(new HtmlWriter(new HtmlWriteOptions { WriteDocumentShell = false }));

var configured = new DocWrightConverter(registry);   // reads DOCX; writes DOCX and body-only HTML

A registry you build holds only what you add: that converter can't save .rdl. PDF and image output don't go through the registry, so they always work.