Início Rápido

Renderize seu primeiro documento em minutos

Esta demonstração leva um aplicativo ASP.NET Core de um Program.cs vazio até um documento renderizado no navegador: registro no servidor, o pacote completo do Viewer (barra de ferramentas, montagem do Viewer e fitas opcionais de Busca/Anotação), referências de recursos, inicialização do cliente, abertura do documento e execução.

O aplicativo host deve direcionar‑se a um runtime que suporte netstandard2.1 — .NET Core 3.0 ou posterior. Os aplicativos de referência enviados com esta versão direcionam‑se a net8.0.

Configuração do servidor

AddDoconut() registra os serviços; UseDoconutResources() e UseDoconut() conectam o middleware. A chamada aos recursos deve ser a primeira. As chamadas de sessão também são necessárias — a segurança de documento Doconut valida cada solicitação de página contra o estado de sessão ASP.NET. O estado de sessão precisa de um cache: AddDistributedMemoryCache() fornece um. Um app MVC ou Razor Pages já o registra, mas um app minimal‑API não, e sem ele o app falha na inicialização com Unable to resolve service for type 'IDistributedCache'. Já registrou o Doconut durante a Instalação? Pule para a próxima seção.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // o estado de sessão precisa de um cache (MVC/Razor Pages já adicionam um)
builder.Services.AddSession(); // a segurança de documento Doconut depende do estado de sessão ASP.NET

app.UseSession();          // chame UseSession() antes de UseDoconut()
app.UseDoconutResources(); // deve ser registrado antes de UseDoconut()
app.UseDoconut();

A aplicação de referência mantém o formato histórico da solicitação DocImage.axd e monta o middleware de documento em um branch correspondente. Esta é a configuração a ser copiada quando você está migrando um aplicativo .NET Standard existente, porque o cliente continua chamando a mesma URL de sempre:

csharp
builder.Services.AddDoconut(options =>
{
    options.UnsafeMode      = false;   // mantém a verificação de sessão do Doconut habilitada
    options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // o estado de sessão precisa de um cache (MVC/Razor Pages já adicionam um)
builder.Services.AddSession(options =>
{
    options.IdleTimeout        = TimeSpan.FromSeconds(3600);
    options.Cookie.HttpOnly    = true;
    options.Cookie.IsEssential = true;
});

// Necessário para codificações multibyte em alguns formatos de documento.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources();   // ANTES do branch de documento
app.MapWhen(
    context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
    branch => branch.UseDoconut());

UseDoconut() não aceita opções nesta versão — tudo foi movido para AddDoconut(). MiddlewarePath é um valor de coordenação; ele não mapeia um branch do ASP.NET Core por si só. ResourcesPath serve o pacote incorporado em /doconut-res, e o caminho de recurso de imagem do widget é, portanto, ResPath: 'doconut-res/images'.

Adicione o visualizador a uma página

O Viewer é o núcleo obrigatório da página. Sua superfície de renderização usa dois divs aninhados:

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

Trate a barra de ferramentas, as montagens de módulos e a superfície do Viewer como uma única composição de página. Busca e Anotação inserem suas fitas incorporadas em montagens opcionais, mas esses módulos nunca são independentes: eles sempre se anexam ao Viewer na mesma página.

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Navegação do visualizador, zoom, botões de Busca e Anotação -->
</nav>

<div id="searchBarMount"></div>
<div id="annBarMount"></div>

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

Referenciar os recursos do visualizador

Em uma visualização Razor, o serviço Viewer injetado emite as tags <link> e <script> do visualizador em ordem de dependência — o widget é um plugin jQuery, portanto o jQuery deve ser carregado antes dos scripts do visualizador:

html
@inject Doconut.Viewer Viewer

@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
    IncludeBootstrapCss = true,
    IncludeViewerCss    = true
}))

@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
    IncludeJQuery        = true,
    IncludeBootstrap     = true,
    IncludeViewerScripts = true
}))

Para o pacote completo do Viewer, solicite os recursos do Viewer e dos módulos juntos:

html
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
    IncludeBootstrapCss  = true,
    IncludeViewerCss     = true,
    IncludeSearchCss     = true,
    IncludeAnnotationCss = true
}))

@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
    IncludeJQuery            = true,
    IncludeBootstrap         = true,
    IncludeViewerScripts     = true,
    IncludeSearchScripts     = true,
    IncludeSearchBar         = true,
    IncludeAnnotationScripts = true,
    IncludeAnnotationBar     = true
}))

IncludeViewerCss e IncludeViewerScripts são as flags principais obrigatórias. Nunca publique um exemplo da Ribbon de Pesquisa ou Anotação sem elas, a montagem do Viewer e uma instância docViewer. ReferenceCss e ReferenceScripts omitem os recursos de um módulo opcional quando a licença atual não concede essa capacidade; o Viewer principal ainda inicia.

Essas chamadas substituem arquivos de script copiados manualmente. Se a aplicação ainda carregar seus próprios docViewer.js, documentLinks.js ou docViewer.UI.js, remova essas tags assim que a página funcionar a partir dos recursos emitidos — veja o guia de migração.

Inicializar o visualizador

O widget do lado do cliente é um plugin jQuery. Este é um conjunto mínimo de opções reais de inicialização (não pseudocódigo):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   '/',
    ResPath:    'doconut-res/images',
    onViewerReady: function () {
        // as páginas estão visíveis; seguro ocultar um spinner de carregamento aqui
    },
    // Encaminha eventos do ciclo de vida da anotação para a ribbon incorporada quando ela está presente.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Dois detalhes decidem se esta página funciona de fato:

  • Callbacks são opções. onViewerReady, onViewerBusy, onCopy e onAutoLoadStatus são passados no objeto de opções. O visualizador anterior derivava nomes de funções globais a partir do id do contêiner e as chamava; este não faz isso. Um manipulador declarado apenas como função global nunca é executado, e nada é relatado.
  • ResPath aponta para o prefixo dos recursos, não para a raiz da aplicação. Com o ResourcesPath padrão, esse valor é doconut-res/images. Se você alterou o ResourcesPath, use seu próprio prefixo.

Ambos falham silenciosamente — veja a Solução de Problemas e o guia de migração.

A capitalização das opções é realmente mista — showThumbs, autoLoad e pageZoom são camelCase, mas FitType, BasePath e ResPath são PascalCase. Não há regra consistente; se a capitalização estiver errada, a opção será silenciosamente ignorada (o widget recai para seu padrão em vez de lançar erro).

Montar o pacote completo do Viewer

Ambas as aplicações de referência instalam as seguintes partes juntas em uma única página:

Parte do pacoteRequisitoComo está conectado
Recursos do Viewer, montagem e objViewerObrigatórioRenderizador de documento central
Barra de ferramentas do ViewerObrigatório na composição de referênciaMarcação do host; botões chamam o mesmo objViewer
Ribbon de PesquisaOpcional, módulo licenciadodoconutSearchBar(...).attach(objViewer)
Ribbon de AnotaçãoOpcional, módulo licenciadodoconutAnnotationBar(...).attach(objViewer)

Embora a barra de ferramentas principal do Viewer seja marcação do host, ela é instalada ao lado do Viewer e nunca deve ser documentada como um controle isolado. Isso mantém seu layout, rótulos, ícones e regras de autorização sob o controle da sua aplicação enquanto cada botão controla a mesma instância do 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.GotoPage(objViewer.TotalPages())">Last</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>
    <button type="button" onclick="objViewer.FitType('height')">Fit height</button>
    <button type="button" id="openSearch">Search</button>
    <button type="button" id="openAnnotations">Annotations</button>
</nav>

Mantenha a ordem de inicialização do pacote usada por ambas as aplicações de referência:

  1. Emita CSS para o Viewer e módulos licenciados.
  2. Renderize a barra de ferramentas do Viewer, as montagens de Pesquisa/Anotação e a montagem do Viewer juntos.
  3. Emita scripts para o Viewer e módulos licenciados.
  4. Carregue o script auxiliar da barra de ferramentas da própria aplicação host.
  5. Inicialize docViewer e mantenha o objViewer resultante.
  6. Inicialize cada Ribbon de Pesquisa ou Anotação licenciado.
  7. Chame attach(objViewer) em cada Ribbon.
  8. Abra o documento e retenha seu token para solicitações do Viewer e dos módulos.

As proteções do lado do servidor são importantes: quando uma capacidade opcional não está disponível, seu script não é emitido, portanto sua função de plug-in jQuery não existe.

html
<script>
    let currentToken = '';

    const refitViewer = () =>
        requestAnimationFrame(() => objViewer.Refit());

    @if (Viewer.IsSearchEnabled)
    {
        <text>
    searchBar = $('#searchBarMount').doconutSearchBar({
        docId: 'ctlDoc',
        getRequestParams: () => ({ token: currentToken }),
        onLayout: refitViewer
    });
    searchBar.attach(objViewer);
        </text>
    }

    @if (Viewer.IsAnnotationEnabled)
    {
        <text>
    annBar = $('#annBarMount').doconutAnnotationBar({
        docId: 'ctlDoc',
        getRequestParams: () => ({ token: currentToken }),
        onLayout: refitViewer
    });
    annBar.attach(objViewer);
        </text>
    }

    document.getElementById('openSearch').addEventListener('click', () => {
        if (!searchBar) return;
        searchBar.isOpen() ? searchBar.close() : searchBar.open();
    });

    document.getElementById('openAnnotations').addEventListener('click', () => {
        if (!annBar) return;
        annBar.isOpen() ? annBar.close() : annBar.open();
    });
</script>

Ambos os componentes incorporados geram seu próprio DOM de Ribbon. A Pesquisa contém grupos Encontrar, Opções e Resultados. A Anotação contém suas ferramentas de autoria, controles de estilo, ações de salvar e ações opcionais de exportação/imagem. As barras expõem open(), close(), reset() e isOpen(); sempre chame attach(objViewer) uma vez após criá‑las.

O exemplo acima omite callbacks opcionais do host e endpoints de exportação/imagem da Anotação para manter a inicialização mínima. Veja Pesquisa e Anotações para a configuração completa de recursos específicos, ou Temas Personalizados para estilizar ou substituir a barra de ferramentas do Viewer de propriedade do host.

Abrir um documento

O lado do servidor possui um único endpoint: o serviço Viewer injetado abre o documento e retorna um token de sessão. A abertura é assíncrona nesta versão.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // O token é opaco — entregue ao widget, nunca registre ou persista.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

O cliente obtém esse token e o entrega ao widget com objViewer.View(token):

javascript
fetch('/api/open', { method: 'POST' })
    .then(resp => resp.json())
    .then(data => {
        currentToken = data.token;
        objViewer.View(currentToken);
    });

Não há nada a preparar antes da primeira abertura. Se você está avançando a aplicação, exclua qualquer chamada a InitCache() e a bandeira que a protege — o método não existe mais.

Fechar o documento

Chame objViewer.Close() quando o usuário sair do visualizador ou abrir um documento de substituição. Em fluxos de trabalho dirigidos pelo servidor, viewer.CloseDocument(token) remove imediatamente a sessão em cache, libera o motor de renderização, exclui seu marcador de segurança e revoga o token. A expiração deslizante eventualmente realiza a mesma limpeza, mas o fechamento explícito é recomendado para documentos grandes.

O fluxo de solicitação concluído é:

text
AddDoconut + middleware
    -> render CSS/scripts and mount div
    -> initialize docViewer
    -> OpenDocumentAsync
    -> return opaque token
    -> objViewer.View(token)
    -> page/search/annotation requests
    -> Close / CloseDocument

Trate o token como uma credencial de portador: nunca o registre, nunca o persista, entregue‑o apenas ao widget. Ele identifica uma sessão de documento ativa no servidor e deixa de funcionar quando essa sessão expira — reabra o documento para obter um novo.

Executar

Coloque um PDF em wwwroot/files/Sample.pdf, execute dotnet run e abra a página que hospeda o widget. A primeira página é renderizada no visualizador, com um painel de miniaturas à esquerda. Se isso não acontecer, veja Solução de Problemas.

O que você obtém sem licença

Uma licença ausente não gera exceção. O visualizador renderiza normalmente, mas cada página traz uma marca d'água de avaliação. Veja Configuração de Licença para saber como o Doconut encontra uma licença e o que muda quando isso acontece.

Esta página foi útil?