GroupDocs.Annotation for .NET 26.10 Release Notes

GroupDocs.Annotation for .NET 26.10 is a security-focused release.

  • External resources are no longer loaded by default. Opening, previewing or saving a document no longer downloads the remote images, style sheets or linked files it references. New LoadOptions members let you allow specific hosts, inspect every request or restore the previous behaviour. This is a behaviour change — please read External resources are no longer loaded by default before upgrading.
  • The bundled document engines are updated to their October 2026 releases.
  • System.Security.Cryptography.Pkcs is raised to 8.0.2 (CVE-2026-84008, CVE-2026-70162).
  • Fixes:
    • EML messages that could not be opened from a stream;
    • documents annotated by old versions that could not be opened on .NET 8;
    • presentations whose annotations could not be read back after re-saving;
    • PDF pages that were not rotated by 90 degrees.

Full list of changes in this release

KeyCategorySummary
ANNOTATIONNET-2663⚠️ EnhancementExternal resources are no longer loaded by default (new LoadOptions controls)
ANNOTATIONNET-2687✨ EnhancementUpgrade the underlying document engines
ANNOTATIONNET-2672🔒 SecurityUpdate System.Security.Cryptography.Pkcs to 8.0.2 (CVE-2026-84008 / CVE-2026-70162)
ANNOTATIONNET-2102🔧 FixFix FileTypeNotSupportedException when extracting annotations from EML documents
ANNOTATIONNET-1903🔧 FixFix documents annotated by older versions failing to open on .NET 8
ANNOTATIONNET-2689🔧 FixFix PDF page rotation by 90 degrees being lost on save

Behaviour Changes

External resources are no longer loaded by default

A document can reference resources outside itself, for example:

  • an <img src="https://..."> in an HTML page or an e-mail body;
  • a linked (not embedded) picture in a Word document;
  • a remote style sheet;
  • an external workbook link.

Until now, opening, previewing or saving such a document made GroupDocs.Annotation request those resources from the network or from a file share. The caller had no way to switch this off.

On a server that processes untrusted documents, this is a server-side request forgery (SSRF) risk. A document can make the server request internal addresses such as cloud metadata endpoints or localhost services. A UNC path can also leak the Windows (NTLM) credentials of the process. The requests also break offline and air-gapped installations.

Starting with 26.10, external resources are skipped by default:

  • No requests. Annotator makes no network or file-share request for resources referenced by the document. This applies to every operation: Get, Add, Save, GeneratePreview and GetDocumentInfo.

  • Formats covered.

    • Word-processing documents: DOCX, DOC, RTF, ODT and others.
    • HTML documents.
    • E-mail documents: EML, MSG and MHT.
    • Presentations.
    • Spreadsheets.
  • Skipped images. A skipped image is rendered as an empty (transparent) placeholder. The link itself is kept, so a saved document still references the original image.

  • What counts as external. These are all treated as external and skipped:

    • absolute URLs, such as http(s)://, ftp:// and file://;
    • UNC paths and protocol-relative URLs (//host/...);
    • rooted local paths.

    Resources stored inside the document are always loaded, for example cid: parts of an e-mail, data: URIs and embedded pictures.

  • Image annotations are not affected. ImageAnnotation.ImagePath is a resource you specify yourself, not one the document references, so it is still loaded from a URL. The library also no longer installs a process-wide ServicePointManager.ServerCertificateValidationCallback when it downloads that image. Standard certificate validation applies, and your application’s own callback is left untouched.

Action required. If your documents rely on remote images or style sheets being rendered (in previews or in saved output), allow them explicitly with the new LoadOptions members:

MemberDefaultDescription
LoadOptions.SkipExternalResourcestrueSkip external resources referenced by the document. Set it to false to restore the pre-26.10 behaviour.
LoadOptions.WhitelistedResourcesemptyResources loaded even when SkipExternalResources is true. Each entry is a host name, or an absolute URI prefix.
LoadOptions.ExternalResourceHandlernullCalled for every external resource before it is loaded. The handler can allow it, skip it, or supply its content.

A WhitelistedResources entry matches as follows:

  • Host name, for example "example.com". It matches that host and its subdomains.
  • Absolute URI prefix, for example "https://cdn.example.com/images/".
    • The scheme, host and port must match exactly, so https://a.com does not admit https://a.com.evil.net.
    • The path matches only on a segment boundary, so /images admits /images/x.png but not /images-private/x.png.

Allow trusted hosts only (recommended):

LoadOptions loadOptions = new LoadOptions
{
    // SkipExternalResources stays true (the default)
    WhitelistedResources = { "cdn.example.com", "https://intranet.example.com/images/" }
};

using (Annotator annotator = new Annotator("input.html", loadOptions))
{
    annotator.Save("output.html"); // only images from the two whitelisted locations are loaded
}

Inspect, allow or substitute each resource with a handler:

class AuditingResourceHandler : ExternalResourceHandler
{
    public override void OnLoading(ExternalResourceLoadingArgs args)
    {
        // args.Skipped is pre-set from SkipExternalResources and WhitelistedResources
        Console.WriteLine($"{args.Uri}: {(args.Skipped ? "skipped" : "loaded")}");

        // serve a local copy instead of going to the network
        if (args.Uri == "https://example.com/logo.png")
            args.Data = File.ReadAllBytes("logo.png");
    }
}

using (Annotator annotator = new Annotator("input.docx", new LoadOptions { ExternalResourceHandler = new AuditingResourceHandler() }))
{
    annotator.Document.GeneratePreview(new PreviewOptions(pageNumber => File.Create($"page_{pageNumber}.png")));
}

Restore the previous behaviour (not recommended for untrusted documents):

using (Annotator annotator = new Annotator("input.eml", new LoadOptions { SkipExternalResources = false }))
{
    annotator.Save("output.eml");
}

Enhancements

Upgrade the underlying document engines

The bundled document engines have been updated to their latest (October 2026) releases. These include the word-processing, spreadsheet, presentation, PDF, diagram, e-mail, imaging, CAD and graphics rendering engines.

Note: As with every engine upgrade, regenerated documents and page previews may differ slightly at the byte or pixel level while remaining visually equivalent. For example, the PDF engine now re-encodes embedded images, and the word-processing engine may write a non-breaking space where it previously wrote a regular one. If your pipeline compares output byte-for-byte against stored baselines, refresh those baselines after upgrading.

Note for .NET 6 and .NET 8 projects: the runtime packages now depend on SkiaSharp 4.152.1 (previously 3.119.0). If your project references SkiaSharp 3.x directly, update it to 4.x. Several Microsoft.Extensions.* and System.* dependencies have moved to their 10.x versions. On .NET 6 these packages may report that .NET 6 is not a supported target framework. The warning does not affect GroupDocs.Annotation, which is tested on .NET 6.

Security

Update System.Security.Cryptography.Pkcs to 8.0.2

The engines bring in System.Security.Cryptography.Pkcs 8.0.0 or 8.0.1, which are affected by CVE-2026-84008 and CVE-2026-70162. GroupDocs.Annotation now references the patched 8.0.2 directly on every target framework. On .NET 6 the matching System.Formats.Asn1 8.0.2 is also referenced. No code changes are required.

Fixes

Fix FileTypeNotSupportedException when extracting annotations from EML documents

An EML message whose first line is MIME-Version: 1.0 was detected as an MHT web archive. Opening it from a stream therefore threw FileTypeNotSupportedException. The same message opened from a file path worked. MIME documents are now handled as e-mail, so the message opens the same way from a stream and from a file. As a side effect, MHT (.mht) files are now supported and are processed as e-mail documents.

using (Stream stream = File.OpenRead("message.eml"))
using (Annotator annotator = new Annotator(stream)) // previously threw FileTypeNotSupportedException
{
    var annotations = annotator.Get();
}

Fix documents annotated by older versions failing to open on .NET 8

Versions before 26.6 stored annotations inside the document using .NET BinaryFormatter, which is disabled on .NET 8. On the .NET 8 build, the Annotator constructor threw NotSupportedException for such a document. A typical example is a DOCX annotated by an older version and then saved as PDF. The document could not be opened at all, not even to add new annotations.

The document now opens on .NET 8:

  • Legacy annotations are skipped. The annotations written by the old version are ignored on .NET 8, and a warning is written to the log.
  • New annotations work. You can add new annotations and save the document as usual.
  • Saving drops the legacy annotations. Saving replaces the old annotation data. If you need to keep, edit or export those annotations, process the document on the .NET 6 or .NET Framework build, which still reads them, and save it there once. After that, the annotations are stored as XML and are readable on every framework.

Annotations written by 26.6 and later are not affected.

This release also fixes a related problem with presentations (PPTX) on all frameworks. Re-saving a presentation whose annotation data had become shorter left fragments of the previous data in the file. Its annotations could then not be read back.

// input.pdf carries annotations written by a pre-26.6 version
using (Annotator annotator = new Annotator("input.pdf")) // .NET 8: previously threw NotSupportedException
{
    annotator.Add(new AreaAnnotation { Box = new Rectangle(100, 100, 100, 100), PageNumber = 0 });
    annotator.Save("output.pdf");
}

Fix PDF page rotation by 90 degrees being lost on save

Rotating a PDF page with Annotator.Rotation and Annotator.ProcessPages worked for 180 and 270 degrees, but a 90-degree rotation had no effect. The page was saved unrotated, because the rotation was reset while the annotations were being placed. The requested rotation is now kept for every angle, and annotations on the page are positioned as before.

using (Annotator annotator = new Annotator("input.pdf"))
{
    annotator.ProcessPages = 1;                 // the page to rotate
    annotator.Rotation = RotationDocument.on90; // previously ignored
    annotator.Save("output.pdf");
}

Public API changes

Added:

  • GroupDocs.Annotation.Options.LoadOptions.SkipExternalResources (bool, default true)
  • GroupDocs.Annotation.Options.LoadOptions.WhitelistedResources (List<string>)
  • GroupDocs.Annotation.Options.LoadOptions.ExternalResourceHandler (ExternalResourceHandler)
  • GroupDocs.Annotation.Options.ExternalResourceHandler, a class with virtual void OnLoading(ExternalResourceLoadingArgs args)
  • GroupDocs.Annotation.Options.ExternalResourceLoadingArgs, with the members Uri, Skipped and Data

No public members were removed or changed.