Viewer

The main document viewer class

Viewer (namespace Doconut) is the public entry point for opening documents from MVC controllers, Web Forms pages, and handlers. It is a transient service composed by DoconutHost.Initialize; resolve it where you use it:

csharp
var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();

new Viewer() also works once DoconutHost.Initialize has run — it resolves the same services — which keeps code migrated from the classic package compiling. Before Initialize, it throws InvalidOperationException.

Viewer holds no per-request document state and intentionally does not implement IDisposable: document sessions live independently of it, so disposing a Viewer could never close an open document (see How the Viewer Works).

OpenDocumentAsync

Opens a document and returns the session token the widget uses for every later request. When UnsafeMode is false (the default), the token is granted to the ASP.NET session of the current request — call it from a request that has session state (see Sessions & Security).

OverloadUse 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 (an upload, a database, a blob). fileInfo must carry the right extension — it selects the format
csharp
// 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 (HttpPostedFileBase in MVC; HttpPostedFile in Web Forms)
using (var ms = new MemoryStream())
{
    await file.InputStream.CopyToAsync(ms);
    ms.Position = 0;
    token = await viewer.OpenDocumentAsync(ms, new FileInfo(Path.GetFileName(file.FileName)));
}

Exceptions to handle:

  • LicenseException — a license was found but rejected (the message carries the reason), or the format needs a plugin capability the license does not grant.
  • FormatNotSupportedException — no viewer handles the file's extension.
  • InvalidDataException — the content is corrupt or does not match its extension.

OpenDocument (synchronous)

For Web Forms code-behind and other code that does not use asynchronous pages:

text
string OpenDocument(string filePath, BaseConfig? config = null, DocOptions? options = null)
string OpenDocument(Stream stream, string fileName, BaseConfig? config = null, DocOptions? options = null)
string OpenDocument(byte[] bytes, string fileName, BaseConfig? config = null, DocOptions? options = null)

Each returns the token and also stores it in viewer.Token. fileName must carry the right extension. These wrappers are safe to call from a Web Forms page: the open path never resumes on the ASP.NET request context, so waiting on it cannot deadlock. The same is not true of every asynchronous Doconut API — see the Web Forms section of Converter Plugin.

CloseDocument

text
void CloseDocument(string token)

Removes the session (releasing the document engine immediately), removes the token's grant from the current request's ASP.NET session, and revokes the token. Optional — sliding expiration performs the same cleanup — but recommended for large documents.

Session queries

MemberReturns
int GetPageCount(string token)Total pages of the open document
(int Width, int Height) GetPageDimensions(string token, int page = 1)Default rendered page size at 100% zoom — the coordinate space for annotations built in code
IReadOnlyList<string> GetWorksheetNames(string token)A spreadsheet's rendered worksheets, in workbook order; empty for other documents

Each throws if the token is unknown or expired.

DocOptions

Per-open, format-independent options (namespace Doconut):

TypePropertyDefaultDescription
stringPassword""Password for protected documents (copied into the format config automatically).
stringWatermark""Custom watermark text drawn on rendered pages — see below.
intTimeOut60Session sliding expiration, in minutes.
stringBrowserId""Web farms: the browser a signed ticket is bound to. Set it with DoconutFarmBinding.EnsureBrowserId (see Sessions & Security).
stringUserId""Web farms: the signed-in user a ticket is bound to, from DoconutFarmBinding.ResolveUserId.
intImageResolution0Obsolete. Set ImageResolution on the format config instead.
boolIsSecuredtrueReserved; not enforced. Access control is DoconutOptions.UnsafeMode.
boolIsWebFarm / string WebFarmPathfalse / ""Kept for code migrated from the classic package. They do not switch farm mode on — AddDoconutDistributed does.
boolEditModefalseReserved for the separately distributed editor; leave false.

Custom watermark

DocOptions.Watermark takes six tilde-separated fields. An optional leading ^ requests the all-corners layout:

text
^Text~Color~FontSize~FontName~Opacity~Angle
csharp
string token = await viewer.OpenDocumentAsync(
    path,
    new PdfConfig(),
    new DocOptions
    {
        Watermark = "^Confidential~Red~24~Verdana~80~-45",
        TimeOut = 30
    });
FieldExampleMeaning
Leading ^^Optional all-corners layout. Without it, the normal placement is used.
TextConfidentialText drawn on each page. It must not be empty.
ColorRedA named color.
FontSize24Font size; invalid numeric input falls back to the default.
FontNameVerdanaFont family. It must be installed on the server — Windows Server has fewer fonts than a desktop.
Opacity800 to 255. It must parse.
Angle-45Rotation 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 a visible Invalid Watermark text instead of silently disappearing.

License decision

License stateCustom value suppliedRendered result
Valid paid viewer licenseNoClean page
Valid paid viewer licenseYesCustom watermark
Active Temporary/Demo licenseNoClean page
Active Temporary/Demo licenseYesCustom watermark
Missing, rejected, expired, wrong-version, or invalid-domain licenseEitherEvaluation watermark; the custom value does not replace it

The same decision applies to served pages and to annotation exports. A custom watermark is a feature of a licensed application, not a way to replace the evaluation watermark.

Annotations API

Server-side annotation loading and export. The walkthrough is in Annotations.

MemberPurpose
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 the encoded 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

DICOM metadata

text
Task<DicomMetadata?> GetDicomMetadataAsync(string token, CancellationToken ct = default)

On this package it always returns null, and the widget's metadata request answers 501 Not Implemented: reading DICOM tags is not available on .NET Framework. DICOM rendering — pages, frames, and animation — works. See DICOM Plugin.

Resource helpers — ReferenceCss / ReferenceScripts

Emit the <link> and <script> tags for the resources the resource module serves under DoconutOptions.ResourcesPath, in dependency order. The files for license-gated features (Search, Annotation) are emitted only when the license grants them.

text
string ReferenceCss(CssConfig? config = null)       // null → Bootstrap + viewer + search + annotation
string ReferenceScripts(ScriptConfig? config = null)
bool IsSearchEnabled { get; }                       // license state, for guarding ribbon init code
bool IsAnnotationEnabled { get; }

CssConfig flags: IncludeBootstrapCss, IncludeViewerCss, IncludeSearchCss (search-gated), IncludeAnnotationCss (annotation-gated).

ScriptConfig flags: IncludeJQuery (required by all others), IncludeBootstrap, IncludeViewerScripts (the core viewer scripts), IncludeSearchScripts and IncludeSearchBar (search-gated), IncludeAnnotationScripts and IncludeAnnotationBar (annotation-gated).

Both return HTML: write them with @Html.Raw(...) in MVC 5 (produce them in the controller — views have no @inject) or <%= %> in Web Forms. Quick Start shows both.

RenderViewer

text
string RenderViewer(string elementId, string token, ViewerConfig? config = null)

Returns a <script> block that initialises the widget on the element with id elementId and shows the document: docViewer({...}) built from the ViewerConfig (default: BasePath: '/', ResPath: 'doconut-res/images'), then .View(token). Write it after the viewer scripts. The generated script keeps no reference to the viewer object, so a page with its own toolbar should initialise the widget by hand instead. See ViewerConfig.

Web farm

string? AccessToken holds the signed ticket issued by the most recent open when distributed mode is active and UnsafeMode is false; it is null on a single server. Send it to the widget with the token: objViewer.View(token, access).

หน้านี้เป็นประโยชน์หรือไม่?