Quick Start

Render your first document in MVC 5 or Web Forms

This walkthrough takes an ASP.NET application on .NET Framework 4.7.2 from an installed package to a document rendered in the browser: Global.asax, an open endpoint, the viewer's assets, the widget, and the token that connects them. It assumes the Web.config registrations from Installation are in place — without the DocImage.axd handler and the resource module, nothing below can reach the server.

Compose Doconut in Global.asax

A complete Application_Start for an MVC 5 application:

csharp
// Global.asax.cs - compose once per worker process, release on shutdown.
protected void Application_Start()
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

    Doconut.DoconutHost.Initialize(options =>
    {
        options.LicensePath = HostingEnvironment.MapPath("~/wwwroot/Doconut.Viewer.lic");
        options.UnsafeMode  = false; // a page is served only to the session that opened the document
    });

    // MVC only: keep the two Doconut endpoints out of routing.
    RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    RouteTable.Routes.IgnoreRoute("doconut-res/{*pathInfo}");
}

protected void Application_End()
{
    Doconut.DoconutHost.Shutdown();
}
  • Encoding.RegisterProvider makes the legacy code pages that some document formats use available to the process. Call it once, before the first document opens.
  • UnsafeMode = false is the default, written out so nobody flips it by accident: DocImage.axd serves a page only to the ASP.NET session that opened the document.
  • The two IgnoreRoute calls keep MVC routing away from DocImage.axd and /doconut-res/*, which the handler and the module serve. A Web Forms application has no MVC routes and needs neither line.
  • DoconutHost.Shutdown stops Doconut's background work and releases its services when the application pool recycles. It is safe to call even if Initialize never ran.

Open a document

Opening a document returns an opaque token. The widget sends that token with every page request.

In MVC 5, a controller action resolves the Viewer service from DoconutHost.Services — MVC 5 has no constructor injection for it:

csharp
// POST /Document/Open
[HttpPost]
public async Task<ActionResult> Open()
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();

    // The token is opaque - hand it to the viewer, never log or persist it.
    string token = await viewer.OpenDocumentAsync(Server.MapPath("~/files/Sample.pdf"));
    return Json(new { token });
}

The document must be opened inside a request that has session state, because opening it is what grants the current ASP.NET session access to its pages. MVC controllers and Web Forms pages have session state by default. A custom IHttpHandler does not, unless it implements System.Web.SessionState.IRequiresSessionState. Open a document from a handler without it, and the viewer shows "You Are Not Authorized To View This Page." on every page.

In Web Forms, the page itself can open the document in its code-behind. OpenDocument is the synchronous form of OpenDocumentAsync, intended for code-behind that does not use asynchronous pages:

csharp
// Default.aspx.cs
protected string Token;

protected void Page_Load(object sender, EventArgs e)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    Token = viewer.OpenDocument(Server.MapPath("~/files/Sample.pdf"));
}

Treat the token like a bearer credential: never log it or persist it; hand it only to the widget. It stops working when the document session expires — open the document again to get a new one.

Reference the viewer assets

Viewer.ReferenceCss and Viewer.ReferenceScripts emit the <link> and <script> tags for the embedded assets, in dependency order, pointing at /doconut-res/. The widget is a jQuery plugin, so jQuery comes first.

MVC 5. Views have no @inject, so produce the tags in the controller and pass them on ViewBag:

csharp
public ActionResult Index()
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    ViewBag.ViewerCss = viewer.ReferenceCss(new CssConfig
    {
        IncludeBootstrapCss = true,
        IncludeViewerCss    = true
    });
    ViewBag.ViewerScripts = viewer.ReferenceScripts(new ScriptConfig
    {
        IncludeJQuery        = true,
        IncludeBootstrap     = true,
        IncludeViewerScripts = true
    });
    return View();
}
html
@Html.Raw(ViewBag.ViewerCss)
...
@Html.Raw(ViewBag.ViewerScripts)

Keep that work in the controller. MVC 5 compiles views when they are first served, and without an extra compiler package that compiler only understands C# 5 — no ?., no $"...", no nameof. A view that uses them compiles cleanly in Visual Studio, then answers an empty 500 in the browser.

Web Forms. Expose the same strings from the code-behind and write them with <%= %>:

html
<head>
    <%= ViewerCss %>
</head>
<body>
    ...
    <%= ViewerScripts %>
</body>

IncludeViewerCss and IncludeViewerScripts are the mandatory core. To add the Search and Annotation ribbons, also set IncludeSearchCss, IncludeAnnotationCss, IncludeSearchScripts, IncludeSearchBar, IncludeAnnotationScripts and IncludeAnnotationBar. Both methods leave out a ribbon's files when the license does not grant that capability; the viewer itself still starts.

Add the viewer to a page

The rendering surface is two nested divs:

html
<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

Give the outer div a real height — the viewer fills its container and measures it when it starts. The viewer does not draw its own toolbar: GotoPage, Next, Zoom, FitType and the rest are methods your page wires to its own buttons, placed beside the viewer:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <button type="button" onclick="objViewer.GotoPage(1)">First</button>
    <button type="button" onclick="objViewer.Next(false)">Previous</button>
    <button type="button" onclick="objViewer.Next(true)">Next</button>
    <button type="button" onclick="objViewer.Zoom(false)">Zoom out</button>
    <button type="button" onclick="objViewer.Zoom(true)">Zoom in</button>
    <button type="button" onclick="objViewer.FitType('width')">Fit width</button>
</nav>

Initialise the viewer and show the document

After the viewer scripts, create the widget, then hand it the token:

javascript
var objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   '/',                   // '/' means DocImage.axd at the site root
    ResPath:    'doconut-res/images',  // no leading '/'
    onViewerReady: function () {
        // pages are visible; hide a loading indicator here
    },
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

fetch('/Document/Open', { method: 'POST' })
    .then(function (resp) { return resp.json(); })
    .then(function (data) { objViewer.View(data.token); });

In Web Forms, where the page opened the document itself, the last three lines become objViewer.View('<%= Token %>');.

Four details here fail without an error message:

  • BasePath: '/' addresses DocImage.axd at the site root. For an application in a virtual directory, use its name without slashes ('myapp' requests /myapp/DocImage.axd). A leading slash ('/myapp') produces //myapp/DocImage.axd, which the browser reads as a request to another host.
  • ResPath: 'doconut-res/images' — the icons live one folder below the resource root. With 'doconut-res', every icon returns 404: no toolbar glyphs and no loading spinner, while the document itself still renders. A leading slash breaks it the same way as BasePath.
  • Callbacks are options. onViewerReady, onError and the rest go in the object passed to docViewer(...). A global function named after the viewer, such as ctlDoc_ViewerReady, is never called.
  • Keep the viewer, then call View separately. docViewer(...) returns the object with Zoom, Next and FitType; View(...) returns something else. var objViewer = $('#div_ctlDoc').docViewer({...}).View(token) renders the document, then fails with objViewer.Zoom is not a function on the first toolbar click.

Option names mix camelCase (showThumbs, autoLoad, pageZoom) and PascalCase (FitType, BasePath, ResPath). A misspelled option is ignored, and the widget uses its default.

The one-line alternative

For a page that only needs to show a document, with no toolbar of your own, Viewer.RenderViewer writes the whole initialisation script — docViewer(...) with BasePath: '/' and ResPath: 'doconut-res/images', then .View(token). It exists only in this package:

csharp
// Web Forms code-behind, after opening the document
ViewerInit = viewer.RenderViewer("div_ctlDoc", Token);
html
<div id="div_ctlDoc" style="height: 100vh"></div>
<%= ViewerScripts %>
<%= ViewerInit %>

Pass a ViewerConfig as the third argument to change the zoom, the fit, or the thumbnail panel. Because the generated script keeps no reference to the viewer, use the hand-written initialisation above when your page needs its own toolbar.

Close the document

Call objViewer.Close(true) when the user leaves the viewer or opens a replacement: it clears the widget and posts a close request to DocImage.axd. objViewer.Close() with no argument clears only the widget. On the server, viewer.CloseDocument(token) removes the document session at once, releases its rendering resources, and withdraws the session's access to its pages. Sliding expiration eventually does the same, but closing large documents explicitly frees memory sooner.

Run it

Put a PDF at ~/files/Sample.pdf, start the site in IIS Express (F5 in Visual Studio), and open the page that hosts the widget. The first page renders, with thumbnails on the left. If it does not, see Troubleshooting.

What you get without a license

A missing license does not throw. The viewer renders normally, but every page carries an evaluation watermark. See License Setup for where Doconut looks for your license.

Apakah halaman ini membantu?