Plugin System
Extend the viewer with plugins
Doconut's core stays lean; optional functionality ships as plugins — separate NuGet packages that contribute viewers or services and are switched on by your license. This page explains the registration model, how license gating behaves at runtime, and how to plug in your own viewer.
Registering a plugin
Each plugin package exposes one plugin class. You register it once, at startup:
builder.Services.AddDoconut(options =>
{
options.AddPlugin<Doconut.Plugins.Converter.ConverterPlugin>();
options.AddPlugin<Doconut.Plugins.Dicom.DicomPlugin>();
});AddPlugin<TPlugin>() instantiates the plugin and invokes its Register callback against the plugin registry held on DoconutOptions. Everything a plugin contributes is tagged with the plugin's required capability. AddDoconut() validates registered plugins immediately: a missing license, legacy TRIAL file, or paid license without the capability fails startup with InvalidOperationException. A Temporary/Demo registration is retained across expiry, but its runtime capabilities are revoked after the expiry date.
The contract
A plugin implements a deliberately small interface:
public interface IDoconutPlugin
{
string Name { get; } // e.g. "Doconut DICOM Viewer"
LicenseCapability RequiredCapability { get; } // the license gate
void Register(IDoconutPluginBuilder builder); // contribute viewers/services
}Inside Register, the builder accepts two kinds of contributions:
builder.RegisterViewer(".dcm", () => new DicomViewer())— a viewer for a file extension,builder.RegisterService<TContract>(() => …)— a typed service other parts of the pipeline can look up.
Capabilities and gating
Capabilities are the license units. Converter and Dicom ship as opt-in plugins; Search and Annotation are built-in features gated the same way. The base viewer is not a capability — it is the prerequisite, exposed as IsViewerLicensed on the license service.
Startup validation normally prevents an unlicensed plugin from entering the request pipeline. The viewer factory also applies two defensive runtime rules, which matter if entitlement changes after startup:
- Plugin overrides a built-in viewer (a plugin claims an extension the built-in registry also handles): with the capability licensed, the plugin viewer wins; without it, Doconut silently falls back to the built-in viewer. Users still see their document — they just don't get the plugin feature.
- Plugin-only format (e.g.
.dcm— DICOM has no built-in viewer): without the capability the open call fails hard:
LicenseException: This document type requires the 'Dicom' plugin license.An active Temporary license grants every capability (with clean, unwatermarked base viewing). This is a classic source of go-live surprises: registering the same plugins with a purchased license that omits one of their capabilities makes AddDoconut() fail during startup. Compare IsCapabilityGranted(...) against your plan before deploying. The flip side: with no license at all, nothing is granted — a missing license is not a Temporary license.
The same gating shows up client-side: Viewer.ReferenceScripts() and ReferenceCss() emit the script/style bundles for the license-gated features (search, annotation, …) only when the license enables them, so the widget's UI stays consistent with what the server will actually do.
Feature and plugin map
The product UI uses “plugin” as a broad feature label, but server registration differs:
| Feature | How it is enabled | Capability | Contributes |
|---|---|---|---|
| Annotation | Built into the viewer; include annotation resources | Annotation | Browser authoring, session persistence, and burned-in exports |
| Search | Built into searchable format viewers; include search resources and enable extraction where required | Search | Native text index, highlights, and result navigation |
| Converter | Install Doconut.NET8.Converter and register ConverterPlugin | Converter | C# conversion service and optional web widget |
| DICOM | Install Doconut.NET8.Dicom and register DicomPlugin | Dicom | Medical-image viewing for .dcm and .ima |
Annotation and normal Search do not use AddPlugin<TPlugin>(); their bundles are emitted only when the license grants the corresponding capability. Converter and DICOM are opt-in IDoconutPlugin implementations.
Released plugin packages
| Plugin | Package | Capability | Contributes |
|---|---|---|---|
| Converter | Doconut.NET8.Converter | Converter | Document conversion capability |
| DICOM | Doconut.NET8.Dicom | Dicom | Medical-image viewing (.dcm — plugin-only format) |
Each has a dedicated page under Plugins with its configuration and usage.
Custom viewers — your own format handler
You can plug a viewer into the pipeline without writing a plugin package, straight from Program.cs:
builder.Services.AddDoconut(options =>
{
options.RegisterViewer(
".myext",
() => new MyCustomViewer(), // implements IFormatViewer
() => new ImageConfig { ImageResolution = 150 }); // optional default config
});Custom viewers take precedence over everything — built-ins and plugins alike — and are not license-gated (they are your code). The factory falls back to an ImageConfig when you don't supply a default config.
Takeaways
- Plugins are registered explicitly and their
LicenseCapabilityis validated duringAddDoconut()— missing or insufficient non-temporary entitlement fails fast. - Override-style plugins degrade gracefully; plugin-only formats fail with a
LicenseException. - An active Temporary license unlocks everything; production unlocks what you bought. Verify with
IDoconutLicenseServicebefore shipping.
Was this page helpful?