Viewer
The main document viewer class
Viewer (namespace Doconut) is the public entry point for opening documents from Razor pages, MVC controllers, Blazor components, or minimal APIs. It is sealed, registered as a transient service by AddDoconut(), and resolved via constructor injection — never construct it directly.
The previous .NET Standard library exposed a public constructor taking the memory cache, the HTTP context accessor, and one or more license paths. That constructor is gone; the service graph supplies those dependencies.
Viewer holds no per-request state and intentionally does not implement IDisposable: document sessions live independently in the session cache, so disposing the service could never tear down an open document (see Core Concepts → How the Viewer Works).
OpenDocumentAsync
Opens a document and returns the session token the client widget uses for all subsequent requests. Every open is asynchronous — there is no synchronous OpenDocument.
| Overload | Use when |
|---|---|
Task<string> OpenDocumentAsync(string filePath, DocOptions? options = null, CancellationToken ct = default) | Opening from disk with automatic format detection and the format's default config |
Task<string> OpenDocumentAsync(string filePath, BaseConfig? config, DocOptions? options = null, CancellationToken ct = default) | You need per-format rendering options (PdfConfig, WordConfig, …) |
Task<string> OpenDocumentAsync(Stream stream, FileInfo fileInfo, BaseConfig? config = null, DocOptions? options = null, CancellationToken ct = default) | The document isn't a file on disk (upload, database, blob). fileInfo must carry the correct extension — it drives format detection |
// Simple open
string token = await viewer.OpenDocumentAsync(path);
// With per-format config and options
token = await viewer.OpenDocumentAsync(
path,
new PdfConfig { AllowSearch = true, AllowCopy = true },
new DocOptions { TimeOut = 30 });
// From an upload
await using var ms = new MemoryStream();
await file.CopyToAsync(ms);
ms.Position = 0;
token = await viewer.OpenDocumentAsync(ms, new FileInfo(file.FileName));Exceptions to handle:
LicenseException— a found license is rejected (the message carries the rejection reason), or the format needs a plugin capability that is no longer granted. Calendar expiry without a rejection message degrades to watermarked rendering instead of throwing.FormatNotSupportedException—Document format '<extension>' is not supported.InvalidDataException— the file content is corrupt or doesn't match its extension.
CloseDocument
void CloseDocument(string token)Removes the session from the cache (disposing the document engine immediately), deletes the security marker, and revokes the access grant. Optional — sliding expiration performs the same cleanup — but recommended for large documents.
GetPageCount
int GetPageCount(string token)Total pages of the open session. Throws if the token is unknown or expired.
Capability flags
| Member | Meaning |
|---|---|
bool IsSearchEnabled | The current license grants Search |
bool IsAnnotationEnabled | The current license grants Annotation |
Use these in a Razor view to render a licensed Ribbon only when the server will actually serve it — see Quick Start.
DocOptions
Per-open, format-independent options (namespace Doconut):
| Type | Property | Default | Description |
|---|---|---|---|
string | Password | "" | Password for protected documents (copied into the format config automatically). |
int | ImageResolution | 0 | Obsolete. Kept for compatibility only — set ImageResolution on the format config instead. |
string | Watermark | "" | Custom watermark text drawn on rendered pages. Format string: "^Text~Color~FontSize~FontName~Opacity~Angle", e.g. "^Sample Copy~Red~24~Verdana~80~-45". |
int | TimeOut | 60 | Session sliding-expiration in minutes. |
bool | IsSecured | true | Not currently enforced — reserved. Token binding is controlled globally by DoconutOptions.UnsafeMode (see Core Concepts → Sessions & Security). |
The class also exposes specialized properties that are intentionally outside the normal single-host viewing flow:
| Type | Property | Default | Description |
|---|---|---|---|
bool | IsWebFarm | false | Marks the open operation as a shared-storage scenario. Note the casing — the previous library spelled this IsWebfarm. |
string | WebFarmPath | "" | Shared path used by that workflow. Previously spelled WebfarmPath. Empty in the normal single-host viewer, and not needed on the publish path described in Distributed Deployments. |
bool | EditMode | false | Reserved for a separately distributed editing workflow; leave false for the standard viewer. |
Custom watermark
DocOptions.Watermark uses six tilde-separated fields. An optional leading ^ requests the all-corners layout:
^Text~Color~FontSize~FontName~Opacity~Anglestring token = await viewer.OpenDocumentAsync(
path,
new PdfConfig(),
new DocOptions
{
Watermark = "^Confidential~Red~24~Verdana~80~-45",
TimeOut = 30
});| Field | Example | Meaning |
|---|---|---|
Leading ^ | ^ | Optional all-corners layout. Without it, normal watermark placement is used. |
| Text | Confidential | Text rendered on each page. It must not be empty. |
| Color | Red | Named color understood by the drawing layer. |
| FontSize | 24 | Font size; invalid numeric input falls back to the renderer default. |
| FontName | Verdana | Requested font family. Ensure it is installed in the deployment environment. |
| Opacity | 80 | Byte value from 0 to 255. It must parse successfully. |
| Angle | -45 | Rotation angle in degrees; invalid numeric input falls back to the default. |
The parser expects exactly six fields after the optional ^. An invalid definition is replaced by the SDK's visible Invalid Watermark fallback instead of silently disappearing.
License decision
| License state | Custom value supplied | Rendered result |
|---|---|---|
| Valid paid viewer license | No | Clean page |
| Valid paid viewer license | Yes | Custom watermark |
| Active Temporary/Demo base viewer | No | Clean base-viewer page |
| Active Temporary/Demo base viewer | Yes | Custom watermark when the clean base-viewer path applies |
| Missing, rejected, expired, wrong-version, or invalid-domain license | Either | Enforcement/evaluation watermark; the custom value does not override it |
| Plugin rendering under evaluation rules | Either | Evaluation watermark |
The same decision is applied to served page images and annotation exports. Animated GIF output is stamped frame by frame. A custom watermark is therefore a licensed application feature, not a way to replace or suppress the evaluation watermark.
Annotations API
Server-side annotation loading and export. The full walkthrough lives in Guides → Annotations; the surface is:
| Member | Purpose |
|---|---|
AnnotationManager GetAnnotationManager(string token) | Manager bound to the open session's page dimensions |
AnnotationManager GetAnnotationManager(string token, int pageWidth, int pageHeight) | Manager with explicit page dimensions |
AnnotationManager GetAnnotationManager(int pageWidth, int pageHeight) | Session-independent manager |
void LoadAnnotationData(string token, AnnotationManager manager) | Load annotations built in C# into the session |
void LoadAnnotationData(string token, string annotationData) | Load annotations from the encoded page/Base64 envelope returned by AnnotationManager.GetAnnotationData() |
void LoadAnnotationXML(string token, XmlDocument annotationXml) | Load annotations from XML |
XmlDocument GetAnnotationXML(string token) | Export the session's annotations as XML |
Task<byte[]> ExportAnnotationsToPdfAsync(string token, int zoom = 100, CancellationToken ct = default) | PDF with annotations burned in |
Task<int> ExportAnnotationsToPngAsync(…) | PNG files with annotations burned in |
Task<byte[]> ExportAnnotationsToPngZipAsync(string token, int zoom = 100, CancellationToken ct = default) | ZIP of per-page PNGs with annotations burned in |
Every one of these takes the session token. The previous library's session-free
GetAnnotationManager(pageWidth, pageHeight) shape survives for building annotations
outside a session, but loading them still requires a token.
DICOM metadata
Task<DicomMetadata?> GetDicomMetadataAsync(string token, CancellationToken ct = default)The method is present for API alignment, but this package cannot supply technical DICOM
tags: the metadata reader ships only a .NET 8 build. It returns null for DICOM and
non-DICOM sessions; on a DICOM session it also writes a one-time warning explaining the
platform limitation. Page, frame, and animation rendering remain supported.
Resource helpers — ReferenceCss / ReferenceScripts
Emit the <link>/<script> tags for the embedded resources served by UseDoconutResources(), in correct dependency order. Bundles for license-gated features such as search and annotation are emitted only when the license enables them, keeping the client UI consistent with server behavior.
string ReferenceCss(CssConfig? config = null) // null → Bootstrap + viewer + search + annotation
string ReferenceScripts(ScriptConfig? config = null)CssConfig flags: IncludeBootstrapCss, IncludeViewerCss, IncludeSearchCss (search-gated), IncludeAnnotationCss (annotation-gated).
ScriptConfig flags: IncludeJQuery (required by all others), IncludeBootstrap, IncludeViewerScripts (core: docViewer.js + splitter + links), IncludeSearchScripts and IncludeSearchBar (search-gated), IncludeAnnotationScripts and IncludeAnnotationBar (annotation-gated).
@inject Doconut.Viewer Viewer
@Html.Raw(Viewer.ReferenceCss(new CssConfig { IncludeBootstrapCss = true, IncludeViewerCss = true }))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig { IncludeJQuery = true, IncludeViewerScripts = true }))These helpers are the supported way to deliver the browser resources. Applications that
copied docViewer.js, documentLinks.js, and docViewer.UI.js into wwwroot should
remove those copies once the page renders from the emitted tags.
หน้านี้เป็นประโยชน์หรือไม่?