Migration Notes

Migrating to v26.10 (external resources)

Version 26.10 tightens how documents load the resources they reference. Code compiles unchanged, but a conversion that relied on the earlier rules can produce different output or fail. With the default SkipExternalResources = true:

Before 26.10Since 26.10What to do
Local files outside the document’s folder loadedThey are skippedAdd their folder to WhitelistedResources as a file: prefix, such as file:///C:/assets/, or copy the files next to the document
UNC paths (\\server\share) loaded while SkipExternalResources was falseThey never load, whatever the optionsCopy the files to a local folder, or serve them over https and whitelist that prefix
WhitelistedResources entries matched as fragments of the URL, such as jpg or example.comEach entry must be an absolute URI prefix; any other entry makes the load fail with InvalidLoadOptionsExceptionRewrite the entries, see Migrating whitelist entries from earlier versions
Scripts in HTML, MHTML and SVG documents ranNo script runsSet ExecuteScripts = true on WebLoadOptions or SvgLoadOptions for documents you trust
SVG and SVGZ documents converted without load options used PageDescriptionLanguageLoadOptions, which has no resource controlThey use SvgLoadOptions, which skips external resourcesPass SvgLoadOptions with a whitelist, or with SkipExternalResources = false
XBRL and iXBRL taxonomies loaded from any locationA taxonomy outside the document’s folder makes the load fail with InvalidLoadOptionsExceptionPass FinanceLoadOptions with SkipExternalResources = false
3D material libraries, textures and buffers loaded from any locationThey load only from the model’s own folder, and never from a remote locationAdd their folder to ThreeDLoadOptions.WhitelistedResources as a file: prefix

The same rules now also apply where they did not before: images outside the document’s folder are no longer embedded when HTML or e-mail is converted to a spreadsheet format, Markdown documents no longer fetch remote images, and XML converted through an XSL-FO stylesheet no longer loads external images.

For example, an XBRL filing that references a remote taxonomy converts again with:

using (Converter converter = new Converter("filing.xbrl", (LoadContext loadContext) => new FinanceLoadOptions { SkipExternalResources = false }))
{
    converter.Convert("filing.xlsx", new SpreadsheetConvertOptions());
}

To allow resources by your own rules instead, set an ExternalResourceHandler. See Skip loading of external resources.

Migrating to v26.9 (removed obsolete members)

Version 26.9 removes the members that were marked obsolete in earlier versions. Code that still uses them no longer compiles; replace them as follows:

Removed in 26.9Replacement
IConverterListener and ConverterSettings.ListenerConversionEvents.OnConversionStarted / OnConversionProgress / OnConversionCompleted
ConverterSettings.OnConversionFailedConversionEvents.OnDocumentFailed
ConverterSettings.OnConversionByPageFailedConversionEvents.OnPageFailed
ConverterSettings.OnCompressionCompletedConversionEvents.OnCompressionCompleted
Fluent .OnCompressionCompleted(...) after .Compress(...) (IConversionCompressResultCompleted)FluentConverter.WithEvents(e => e.OnCompressionCompleted = ...)
Fluent interfaces IConversionHandlerSetup, IConversionHandlerFailed, IConversionHandlerCompletedIConversionHandlersStage
Fluent interfaces IConversionByPageHandlerSetup, IConversionByPageHandlerFailed, IConversionByPageHandlerCompletedIConversionByPageHandlersStage
CadConvertOptions.PageSize / PageWidth / PageHeightCadConvertOptions.SizeSettings (PageSizeOptions)
EBookConvertOptions.PageSize / PageWidth / PageHeightEBookConvertOptions.SizeSettings (PageSizeOptions)

WithOptions(...) in the fluent API now returns IConversionHandlersStage (or IConversionByPageHandlersStage for page-by-page conversion). Chained calls such as .WithOptions(...).OnConversionCompleted(...).OnConversionFailed(...).Convert() compile unchanged; only code that stores an intermediate result in a variable of one of the removed interface types needs the new type.

Page size is now set only through SizeSettings.

Before (26.8 and earlier):

var options = new CadConvertOptions { PageWidth = 800, PageHeight = 600 };

After:

var options = new CadConvertOptions
{
    SizeSettings = new PageSizeOptions { PageWidth = 800, PageHeight = 600 }
};

On .NET 6 and later, the exception classes in GroupDocs.Conversion.Exceptions no longer declare the legacy serialization constructor (SerializationInfo, StreamingContext) or override GetObjectData; on .NET Framework they are unchanged. This only affects code that derives from these exception types and calls the serialization constructor.

Migrating to ConversionEvents (v26.6)

Version 26.6 introduces the ConversionEvents aggregator — a single typed object that replaces three previously separate registration paths: the per-handler properties on ConverterSettings, the IConverterListener assigned to ConverterSettings.Listener, and the fluent chain methods placed after WithOptions(...) or Compress(...). The obsolete surfaces are removed in version 26.9 — see Migrating to v26.9.

Per-result events were renamed at the same time — the noun moved from “Conversion” to “Document” or “Page” so the pipeline-lifecycle group could reuse “Conversion”:

Old nameNew name
OnConversionCompleted (per-document)OnDocumentConverted
OnConversionFailed (per-document)OnDocumentFailed
OnConversionByPageCompletedOnPageConverted
OnConversionByPageFailedOnPageFailed
OnCompressionCompleted(unchanged)

The OnConversionCompleted name is reused for the new lifecycle event (Action, no parameters, fires once at pipeline end). The old per-document variant is now OnDocumentConverted.

Classic API

Before — handlers were set as individual properties on ConverterSettings:

var settings = new ConverterSettings();
settings.OnConversionFailed       = (ctx, ex) => Log(ex);
settings.OnConversionByPageFailed = (ctx, ex) => Log(ex);

using (var converter = new Converter("sample.docx", () => settings))
{
    converter.Convert("converted.pdf", new PdfConvertOptions());
}

After — handlers are aggregated in ConversionEvents and passed as the third factory:

var events = new ConversionEvents
{
    OnDocumentConverted = ctx       => Console.WriteLine($"Done: {ctx.SourceFileName}"),
    OnDocumentFailed    = (ctx, ex) => Log(ex),
    OnPageFailed        = (ctx, ex) => Log(ex),
};

using (var converter = new Converter(
    "sample.docx",
    () => new ConverterSettings(),
    () => events))
{
    converter.Convert("converted.pdf", new PdfConvertOptions());
}

Existing Converter constructor signatures are preserved — the events: factory is exposed as an additional overload, not added to the existing ones.

Fluent API

Before — handlers were registered via late-stage chain methods placed after ConvertTo(...).WithOptions(...):

FluentConverter
    .Load("sample.docx")
    .ConvertTo("converted.pdf").WithOptions(new PdfConvertOptions())
    .OnConversionCompleted(ctx => Console.WriteLine($"Done: {ctx.SourceFileName}"))
    .OnConversionFailed((ctx, ex) => Log(ex))
    .Convert();

After — handlers are aggregated in a single WithEvents(...) entry-stage call placed before Load(...):

FluentConverter
    .WithEvents(e =>
    {
        e.OnDocumentConverted = ctx       => Console.WriteLine($"Done: {ctx.SourceFileName}");
        e.OnDocumentFailed    = (ctx, ex) => Log(ex);
    })
    .Load("sample.docx")
    .ConvertTo("converted.pdf").WithOptions(new PdfConvertOptions())
    .Convert();

Fluent API — compression result-delivery

Before — the compressed archive stream was handled via .OnCompressionCompleted(...) placed after .Compress(...):

FluentConverter
    .Load("documents.rar")
    .ConvertTo((SaveContext _) => new MemoryStream()).WithOptions(new PdfConvertOptions())
    .Compress(new CompressionConvertOptions { Format = CompressionFileType.Zip })
    .OnCompressionCompleted(stream => HandleArchive(stream))
    .Convert();

After — the handler is registered at the entry stage via WithEvents(...):

FluentConverter
    .WithEvents(e => e.OnCompressionCompleted = stream => HandleArchive(stream))
    .Load("documents.rar")
    .ConvertTo((SaveContext _) => new MemoryStream()).WithOptions(new PdfConvertOptions())
    .Compress(new CompressionConvertOptions { Format = CompressionFileType.Zip })
    .Convert();

The chain method was obsolete from v26.6 and is removed in v26.9, together with the IConversionCompressResultCompleted interface that declared it.

Pipeline lifecycle — replacing IConverterListener

Before (26.8 and earlier) — implement IConverterListener and assign the instance to ConverterSettings.Listener:

public class MyListener : IConverterListener
{
    public void Started()              => Console.WriteLine("Conversion started");
    public void Progress(byte percent) => Console.WriteLine($"Progress: {percent}%");
    public void Completed()            => Console.WriteLine("Conversion finished");
}

var settings = new ConverterSettings { Listener = new MyListener() };
using (var converter = new Converter("sample.docx", () => settings))
{
    converter.Convert("converted.pdf", new PdfConvertOptions());
}

After — register lifecycle handlers directly on ConversionEvents:

var events = new ConversionEvents
{
    OnConversionStarted   = ()      => Console.WriteLine("Conversion started"),
    OnConversionProgress  = percent => Console.WriteLine($"Progress: {percent}%"),
    OnConversionCompleted = ()      => Console.WriteLine("Conversion finished"),
};

using (var converter = new Converter(
    "sample.docx",
    () => new ConverterSettings(),
    () => events))
{
    converter.Convert("converted.pdf", new PdfConvertOptions());
}

ConverterSettings.Listener and the IConverterListener interface were obsolete from v26.6 and are removed in v26.9.

Per-call vs global precedence

Handlers registered through ConversionEvents are global and fire on every Convert(...) call. The Convert(...) overloads that accept an Action<ConvertedContext> register a per-call handler that wins over the global OnDocumentConverted for that single call only — the precedence rule is (perCall ?? global)?.Invoke(...). Between consecutive runs on the same converter, only the per-call slot is reset; the global events bag is preserved.

See Conversion events for the full reference.

Close
Loading

Analyzing your prompt, please hold on...

An error occurred while retrieving the results. Please refresh the page and try again.