Load external resources

A document can reference resources that are stored outside of it, for example:

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

Starting with version 26.10, GroupDocs.Annotation does not load such resources by default. Opening, previewing or saving a document makes no network or file-share request for the resources the document references. A skipped image is rendered as an empty (transparent) placeholder, and the link itself is kept, so a saved document still references the original resource.

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

The behaviour is controlled by three members of the LoadOptions class:

MemberDefaultDescription
SkipExternalResourcestrueSkip the external resources referenced by the document. Set it to false to load them all.
WhitelistedResourcesemptyResources that are loaded even when SkipExternalResources is true. Each entry is a host name or an absolute URI prefix.
ExternalResourceHandlernullCalled for every external resource before it is loaded. It can allow the resource, skip it, or supply its content.

The options apply to every operation: Get, Add, Save, GeneratePreview and GetDocumentInfo, for Word processing, HTML, e-mail (EML, MSG, MHT), presentation and spreadsheet documents.

Note
Images of an ImageAnnotation are not covered by these options: ImagePath is a resource that your code specifies, not one that the document references. See Network access and data privacy for details.

Skip external resources (default)

No code is needed: external resources are skipped unless you allow them. The following example generates a preview of a Word document with linked pictures without loading them:

using (Annotator annotator = new Annotator("remote-resources.docx"))
{
    PreviewOptions previewOptions = new PreviewOptions(pageNumber =>
        File.Create($"result_{pageNumber}.png"));
    previewOptions.PreviewFormat = PreviewFormats.PNG;

    annotator.Document.GeneratePreview(previewOptions); // linked pictures are rendered as empty placeholders
}

Allow trusted locations only

Add the trusted locations to WhitelistedResources. SkipExternalResources stays true, so everything else is still skipped. An entry matches as follows:

  • Host name, for example "example.com": matches that host and its subdomains.
  • Absolute URI prefix, for example "https://cdn.example.com/images/": the scheme, host and port must match exactly, and the path matches only on a segment boundary, so /images admits /images/x.png but not /images-private/x.png.
LoadOptions loadOptions = new LoadOptions
{
    WhitelistedResources = new List<string> { "cdn.example.com", "https://intranet.example.com/images/" }
};

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

Inspect, allow or substitute each resource

Derive a class from ExternalResourceHandler and override OnLoading. The handler is called for every external resource. ExternalResourceLoadingArgs provides:

  • Uri - the resource address as written in the document;
  • Skipped - pre-set from SkipExternalResources and WhitelistedResources; set it to true to skip the resource or to false to load it;
  • Data - set it to supply the resource content yourself; nothing is requested then.

The following example serves a local copy of a picture instead of downloading it, and skips everything else:

class LocalCopyResourceHandler : ExternalResourceHandler
{
    public override void OnLoading(ExternalResourceLoadingArgs args)
    {
        if (args.Uri == "https://example.com/images/banner.png")
            args.Data = File.ReadAllBytes("banner.png"); // served locally, nothing is requested
        else
            args.Skipped = true;
    }
}

LoadOptions loadOptions = new LoadOptions { ExternalResourceHandler = new LocalCopyResourceHandler() };

using (Annotator annotator = new Annotator("remote-resources.docx", loadOptions))
{
    annotator.Document.GeneratePreview(new PreviewOptions(pageNumber => File.Create($"result_{pageNumber}.png")));
}
Note
Prefer an allow-list over a deny-list. Blocking an address such as http://169.254.169.254/ by pattern is fragile: DNS names, redirects, IPv6 literals and decimal-encoded IP addresses all defeat naive filtering.

Load all external resources

Set SkipExternalResources to false to restore the behaviour of versions before 26.10. Do this only for documents you trust: a document from an untrusted source can make your server request internal addresses (server-side request forgery), and a UNC path can expose the Windows credentials of the process.

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

More resources

Advanced usage topics

GitHub examples

You may easily run the code above and see the feature in action in our GitHub examples:

Close
Loading

Analyzing your prompt, please hold on...

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