Adnotacje

Dodaj obsługę adnotacji do przeglądarki

Adnotacje w Doconut działają w dwóch kierunkach: użytkownicy rysują je w widgetcie przeglądarki, a serwer przechowuje je na stronę, lub Twój kod tworzy je programowo i ładuje do otwartej sesji. W obu przypadkach są renderowane na stronach i mogą być wbudowane w eksporty PDF/PNG.

Obsługa adnotacji jest uzależniona od możliwości licencyjnej Annotation (przyznawana automatycznie w ramach aktywnej tymczasowej licencji).

Włącz interfejs UI adnotacji

Adnotacja jest modułem przeglądarki, a nie samodzielnym paskiem narzędzi. Pełna strona musi zawierać zasoby przeglądarki, pasek narzędzi przeglądarki, miejsce montowania przeglądarki oraz zainicjowany objViewer; wówczas wstążka adnotacji jest montowana i dołączana do tej samej instancji.

Emituj pakiety adnotacji razem z pakietami przeglądarki — są one uzależnione od licencji, więc tagi pojawiają się tylko wtedy, gdy możliwość jest dostępna:

html
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
    IncludeViewerCss     = true,
    IncludeAnnotationCss = true   // jquery-ui.min.css + annotationBar.css
}))

@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
    IncludeJQuery             = true,
    IncludeViewerScripts      = true,
    IncludeAnnotationScripts  = true, // jquery-ui, raphael.js, annotation.js
    IncludeAnnotationBar      = true  // the embedded annotation ribbon
}))

Utrzymaj pełną kompozycję przeglądarki widoczną w znacznikach:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Viewer controls, including the button that opens Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>

Pakiet adnotacji generuje DOM wstążki wewnątrz annBarMount; nie musisz kopiować jego przycisków ani znaczników dialogu. Zainicjuj najpierw docViewer, a następnie utwórz wstążkę tylko wtedy, gdy serwer potwierdzi, że adnotacje są licencjonowane:

html
<script>
    let annBar = null;
    let currentToken = '';

    const objViewer = $('#div_ctlDoc').docViewer({
        BasePath: '/doconut',
        ResPath: '/doconut-res/images',
        onAnnLoaded:    () => annBar?.handleAnnLoaded(),
        onAnnSaved:     () => annBar?.handleAnnSaved(),
        onAnnSaveError: () => annBar?.handleAnnSaveError(),
        onAnnClosed:    () => annBar?.handleAnnClosed(),
        onError:        (message) => console.error('Viewer error:', message)
    });

    @if (Viewer.IsAnnotationEnabled)
    {
        <text>
    annBar = $('#annBarMount').doconutAnnotationBar({
        docId: 'ctlDoc',
        getRequestParams: () => ({ token: currentToken }),
        onStatus: (message) => console.log(message),
        onToast: (message, type) => console.log(type, message),
        onLayout: () => requestAnimationFrame(() => objViewer.Refit())
    });
    annBar.attach(objViewer);
        </text>
    }
</script>

Zapisywanie z wstążki wysyła dane przez middleware (AnnSave), które przechowuje je w sesji dokumentu na każdej stronie. Ładowanie (AnnLoad) odbywa się automatycznie, gdy strona z adnotacjami jest renderowana. Cztery wywołania zwrotne onAnn* utrzymują wstążkę w synchronizacji z cyklem życia przeglądarki.

Otwórz i zamknij ją z dowolnego paska narzędzi przeglądarki zarządzanego przez hosta:

javascript
annBar.open();
annBar.close();

Publiczne API wstążki to:

MetodaCel
attach(objViewer)Podłącza wstążkę do zainicjowanego przeglądarki; wymagane jednorazowo
open() / close()Rozpoczyna lub kończy edycję adnotacji
reset()Przywraca wstążkę do zamkniętego, nieedytującego stanu
isOpen() / annotating()Odczytuje stan wstążki / stan edycji adnotacji w przeglądarce
reopenEditable()Ponownie ładuje adnotacje bieżącej strony jako edytowalne obiekty
updateActionState()Odświeża dostępność przycisków zapisu/usuwania po zmianach w hoście
headerSlot()Pobiera opcjonalny slot rozszerzenia nagłówka dla kontrolek zarządzanych przez hosta

onStatus, onToast, onLayout, onEditStart i onEditEnd są opcjonalnymi wywołaniami zwrotnymi hosta. Obiekt endpoints może dodatkowo udostępniać exportPdf, exportPng, imageUpload i imageList; kontrolki bez skonfigurowanego punktu końcowego pozostają ukryte. Aby zobaczyć sekwencję uruchamiania połączonych przeglądarki, wyszukiwania i adnotacji, zobacz Szybki start.

Pakiet adnotacji dodaje narzędzia autorskie w przeglądarce, ale dane nadal należą do sesji dokumentu po stronie serwera, identyfikowanej tokenem. Ponowne otwarcie źródła tworzy nową sesję; zachowaj XML lub zakodowaną powłokę adnotacji w swojej aplikacji, jeśli adnotacje mają przetrwać poza okresem życia sesji.

Tworzenie adnotacji w C#

Uzyskaj menedżera powiązanego z otwartą sesją, dodaj adnotacje i załaduj je (z using Doconut.Annotations; dla typów oraz using System.Drawing; dla Rectangle/Color):

csharp
app.MapPost("/api/annotations/load-sample", (string token, Viewer viewer) =>
{
    // Bound to the open session's page dimensions
    var manager = viewer.GetAnnotationManager(token);
    var pageCount = viewer.GetPageCount(token);

    // One stamp per page
    for (int page = 1; page <= pageCount; page++)
    {
        manager.Add(new StampAnnotation(page, new Rectangle(30, 20, 240, 90),
            $"PAGE {page}", 28, 4, Color.Maroon)
        {
            Opacity = 60,
            Rotate  = -8
        });
    }

    manager.Add(new NoteAnnotation(1, new Rectangle(420, 150, 220, 120),
        "Loaded from C# code.", Color.FromArgb(255, 255, 255, 170), 14));

    // Load into the session — the widget fetches them via AnnLoad and the
    // renderer burns them into image/PDF exports.
    viewer.LoadAnnotationData(token, manager);
    return Results.Ok();
});

Typy adnotacji

Wszystkie typy znajdują się w Doconut.Annotations i dziedziczą po BaseAnnotation (numer strony + otaczający Rectangle):

TypUwagi
StampAnnotationPieczęć tekstowa z rozmiarem czcionki, obramowaniem, kolorem; obsługuje Opacity, Rotate
NoteAnnotationNotatka samoprzylepna z tekstem, kolorem tła, rozmiarem czcionki, TitleColor
RectangleAnnotationObramowanie + wypełnienie, Title/ShowTitle
CircleAnnotationObramowanie + wypełnienie, ShowBorder
EllipseAnnotationObramowanie + wypełnienie, ShowBorder
TriangleAnnotationKolor obramowania, BackColor, ShowBorder
LineAnnotationProsta linia o określonej szerokości i kolorze
ArrowAnnotationLinia z grotami; konfigurowalny Direction (typ ArrowDirection, punkty kompasu, domyślnie E)
FreehandAnnotationSwobodny odcinek z zakodowanymi punktami FreehandData
ImageAnnotationObraz z adresu URL. Względny URL jest rozwiązywany względem hosta żądania w momencie dodawania adnotacji (pobranie obrazu odbywa się dopiero przy wbudowywaniu) — musi być dostępny dla serwera (np. plik w wwwroot serwowany przez UseStaticFiles)

API AnnotationManager

CzłonekCel
Add(BaseAnnotation)Kolejkuje adnotację
GetAnnotations() / GetAnnotations(int page)Sprawdza, co menedżer przechowuje
ClearAnnotations() / ClearAnnotations(int page)Usuwa wszystkie / per‑strona
GetAnnotationData() / GetAnnotationData(int page)Zakodowany ciąg danych adnotacji — envelope w formacie Base64 (co konsumpuje widget)
GetAnnotationXml()Forma XML

Viewer odzwierciedla operacje ładowania/odczytu względem sesji: LoadAnnotationData(token, manager) lub LoadAnnotationData(token, encodedData) (envelopę w formacie Base64 z GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).

Eksport z wbudowanymi adnotacjami

csharp
// PDF of all pages with annotations rendered onto them
app.MapGet("/api/annotations/export-pdf", async (string token, Viewer viewer) =>
{
    byte[] pdf = await viewer.ExportAnnotationsToPdfAsync(token, zoom: 100);
    return Results.File(pdf, "application/pdf", "export.pdf");
});

// Or a ZIP of per-page PNGs
app.MapGet("/api/annotations/export-png-zip", async (string token, Viewer viewer) =>
{
    byte[] zip = await viewer.ExportAnnotationsToPngZipAsync(token, zoom: 100);
    return Results.File(zip, "application/zip", "annotations-png.zip");
});

Eksporty używają tego samego mechanizmu co renderowanie na ekranie, więc to, co widzą użytkownicy, znajduje się w pliku.

Przebieg utrwalania

  1. Otwórz dokument i uzyskaj jego token.
  2. Załaduj wcześniej zapisany XML lub zakodowane dane do tego tokena.
  3. Pozwól widgetowi odczytać i edytować adnotacje sesji.
  4. Pobierz XML za pomocą GetAnnotationXML(token), gdy Twoja aplikacja zdecyduje się na utrwalenie.
  5. Wyeksportuj PDF/PNG, gdy wymagany jest spłaszczony plik dostarczany.
  6. Zamknij sesję dokumentu.

Nie używaj nieprzezroczystego tokena przeglądarki jako trwałego identyfikatora adnotacji. Powiąż utrwalone dane adnotacji ze swoimi własnymi identyfikatorami dokumentu i wersji.

Uwagi dotyczące bezpieczeństwa i renderowania

  • Żądania adnotacji używają takiego samego zabezpieczenia sesji/tokenu jak żądania stron.
  • Względny adres URL ImageAnnotation jest rozwiązywany względem hosta żądania i musi być dostępny dla serwera w momencie wbudowywania.
  • Waliduj i kontroluj każdy adres URL obrazu podany przez użytkownika, aby uniknąć fałszywych żądań po stronie serwera.
  • Eksporty stosują tę samą decyzję o licencji/dodatkowym znaku wodnym co renderowanie stron na ekranie.
  • Duże ładunki danych freehand oraz eksporty wysokiej rozdzielczości zwiększają zużycie pamięci; testuj realistyczne dokumenty i wartości przybliżenia.

Rozwiązywanie problemów

ObjawSprawdź
Wstążka adnotacji jest nieobecnaMożliwość Annotation oraz cztery flagi CSS/skryptów adnotacji
Wywołanie zwrotne zapisu zgłasza błądWygaśnięcie tokena/sesji oraz middleware BasePath
Adnotacje C# nie pojawiają sięNumeracja stron zaczyna się od 1, a dane zostały załadowane do aktywnego tokena
Adnotacja obrazu pojawia się na ekranie, ale nie w eksporcieSerwer może uzyskać dostęp do URL obrazu podczas wbudowywania
Ponownie otwarty dokument nie ma adnotacjiZachowaj XML/dane poza sesją przeglądarki, a następnie załaduj je do nowego tokena

Czy ta strona była pomocna?