Annotations

Ajouter la prise en charge des annotations au visualiseur

Les annotations dans Doconut fonctionnent dans deux sens : les utilisateurs les dessinent dans le widget du navigateur et le serveur les persiste par page, ou votre code les crée de manière programmatique et les charge dans une session ouverte. Dans les deux cas, elles sont rendues sur les pages et peuvent être intégrées aux exportations PDF/PNG.

La prise en charge des annotations est conditionnée par la capacité de licence Annotation (accordée automatiquement avec une licence Temporaire active).

Activer l'interface d'annotation

L'annotation est un module du Viewer, pas une barre d'outils autonome. La page complète doit inclure les ressources du Viewer, la barre d'outils du Viewer, le montage du Viewer, et le objViewer initialisé ; le ruban d'annotation est ensuite monté et attaché à cette même instance.

Émettez les bundles d'annotation en même temps que les bundles du viewer — ils sont conditionnés par la licence, ainsi les balises n'apparaissent que lorsque la capacité est disponible :

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
}))

Conservez la composition complète du Viewer visible dans le balisage :

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>

Le bundle d'annotation génère le DOM du ruban à l'intérieur de annBarMount ; vous n'avez pas besoin de copier ses boutons ou le balisage du dialogue. Initialise d'abord docViewer, puis créez le ruban uniquement lorsque le serveur confirme que l'annotation est sous licence :

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>

L'enregistrement depuis le ruban envoie les données via le middleware (AnnSave), qui les stocke dans la session du document par page. Le chargement (AnnLoad) se produit automatiquement lorsqu'une page contenant des annotations est rendue. Les quatre rappels onAnn* maintiennent le ruban synchronisé avec le cycle de vie du viewer.

Ouvrez‑le et fermez‑le depuis n'importe quelle barre d'outils du Viewer détenue par l'hôte :

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

L'API publique du ruban est :

MéthodeObjectif
attach(objViewer)Connecte le ruban au viewer initialisé ; requis une fois
open() / close()Entrer ou quitter le mode d'édition d'annotation
reset()Ramène le ruban à son état fermé, non‑édition
isOpen() / annotating()Lire l'état du ruban / l'état d'édition d'annotation du viewer
reopenEditable()Recharger les annotations de la page actuelle en tant qu'objets éditables
updateActionState()Actualiser la disponibilité des contrôles d'enregistrement/suppression après les changements de l'hôte
headerSlot()Obtenir le slot d'extension d'en-tête optionnel pour les contrôles détenus par l'hôte

onStatus, onToast, onLayout, onEditStart et onEditEnd sont des rappels d'hôte optionnels. L'objet endpoints peut en outre fournir exportPdf, exportPng, imageUpload et imageList ; les contrôles sans point de terminaison configuré restent cachés. Pour la séquence de démarrage combinée du Viewer, de la Recherche et de l'Annotation, voir Démarrage rapide.

Le bundle d'annotation ajoute les outils d'édition du navigateur, mais les données appartiennent toujours à la session de document côté serveur identifiée par le jeton. Réouvrir la source crée une nouvelle session ; conservez le XML ou l'enveloppe d'annotation encodée dans votre application si les annotations doivent survivre au-delà de la durée de vie de la session.

Créer des annotations en C#

Obtenez un gestionnaire lié à la session ouverte, ajoutez des annotations et chargez‑les (avec using Doconut.Annotations; pour les types et using System.Drawing; pour 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();
});

Types d'annotation

Tous les types se trouvent dans Doconut.Annotations et héritent de BaseAnnotation (numéro de page + Rectangle de délimitation) :

TypeNotes
StampAnnotationTampon texte avec taille de police, bordure, couleur ; prend en charge Opacity, Rotate
NoteAnnotationNote autocollante avec texte, couleur d'arrière‑plan, taille de police, TitleColor
RectangleAnnotationCouleurs de bordure + remplissage, Title/ShowTitle
CircleAnnotationBordure + remplissage, ShowBorder
EllipseAnnotationBordure + remplissage, ShowBorder
TriangleAnnotationCouleur de bordure, BackColor, ShowBorder
LineAnnotationLigne droite avec épaisseur et couleur
ArrowAnnotationLigne avec pointe de flèche ; Direction réglable (type ArrowDirection, points cardinaux, défaut E)
FreehandAnnotationTrait libre à partir de points FreehandData encodés
ImageAnnotationImage depuis une URL. Une URL relative est résolue par rapport à l'hôte de la requête lorsque l'annotation est ajoutée (seul le téléchargement de l'image se produit au moment de l'intégration) — elle doit être accessible depuis le serveur (par ex. un fichier sous wwwroot servi par UseStaticFiles)

L'API AnnotationManager

MembreObjectif
Add(BaseAnnotation)Mettre une annotation en file d'attente
GetAnnotations() / GetAnnotations(int page)Inspecter ce que le gestionnaire contient
ClearAnnotations() / ClearAnnotations(int page)Supprimer tout / par page
GetAnnotationData() / GetAnnotationData(int page)Chaîne de données d'annotation encodée — une enveloppe Base64 (ce que le widget consomme)
GetAnnotationXml()Forme XML

Viewer reflète les opérations de chargement/lecture sur une session : LoadAnnotationData(token, manager) ou LoadAnnotationData(token, encodedData) (l'enveloppe Base64 provenant de GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).

Exporter avec les annotations intégrées

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");
});

Les exportations utilisent le même moteur d'intégration que le rendu à l'écran, ainsi ce que les utilisateurs voient est ce que le fichier contient.

Flux de travail de persistance

  1. Ouvrez le document et obtenez son jeton.
  2. Chargez le XML ou les données encodées précédemment stockées dans ce jeton.
  3. Laissez le widget lire et éditer les annotations de la session.
  4. Récupérez le XML avec GetAnnotationXML(token) lorsque votre application décide de le persister.
  5. Exportez PDF/PNG lorsqu'un livrable aplati est requis.
  6. Fermez la session du document.

N'utilisez pas le jeton opaque du viewer comme identifiant permanent d'annotation. Associez les données d'annotation persistées à vos propres identifiants de document et de version.

Notes de sécurité et de rendu

  • Les requêtes d'annotation utilisent la même sécurité de session/jeton que les requêtes de page.
  • Une URL ImageAnnotation relative est résolue à partir de l'hôte de la requête et doit rester accessible au serveur au moment de l'intégration.
  • Validez et contrôlez toute URL d'image fournie par l'utilisateur afin d'éviter les falsifications de requêtes côté serveur.
  • Les exportations appliquent la même décision de licence/filigrane personnalisé que le rendu de page à l'écran.
  • Les gros chargements de tracés libres et les exportations haute résolution augmentent l'utilisation de mémoire ; testez des documents réalistes et des valeurs de zoom.

Dépannage

SymptômeVérification
Le ruban d'annotation est manquantCapacité Annotation et les quatre indicateurs CSS/script d'annotation
Le rappel d'enregistrement signale une erreurExpiration du jeton/de la session et BasePath du middleware
Les annotations C# n'apparaissent pasLa numérotation des pages commence à 1 et les données ont été chargées dans le jeton actif
L'annotation d'image apparaît à l'écran mais pas dans l'exportLe serveur peut atteindre l'URL de l'image lors de l'intégration
Le document rouvert n'a aucune annotationPersistiez le XML/les données en dehors de la session du viewer, puis chargez‑les dans le nouveau jeton

Cette page vous a-t-elle été utile ?