Anmärkningar

Lägg till stöd för anmärkningar i visaren

Anmärkningar i Doconut fungerar i två riktningar: användare ritar dem i webbläsarwidgeten och servern sparar dem per sida, eller så bygger din kod dem och laddar dem i en öppen session. I båda fallen renderas de på sidorna och kan brännas in i PDF- eller PNG-exporter.

Stöd för anmärkningar styrs av licensfunktionen Annotation (tilldelas automatiskt under en aktiv temporär licens).

Aktivera anmärknings‑UI

Anmärkning är en Viewer-modul, inte ett fristående verktygsfält. Sidan måste inkludera Viewer-resurserna, Viewer-verktygsfältet, Viewer-mounten och en initierad objViewer; Annotations‑Ribbonen monteras sedan och fästs på samma instans.

Skicka med anmärkningsfilerna tillsammans med visarfilerna. De är licensstyrda, så deras taggar visas endast när funktionen är beviljad. I MVC 5 byggs taggarna i kontrollern (vyer har ingen @inject):

csharp
var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
ViewBag.ViewerCss = viewer.ReferenceCss(new CssConfig
{
    IncludeViewerCss     = true,
    IncludeAnnotationCss = true    // jquery-ui.min.css + annotationBar.css
});
ViewBag.ViewerScripts = viewer.ReferenceScripts(new ScriptConfig
{
    IncludeJQuery            = true,
    IncludeViewerScripts     = true,
    IncludeAnnotationScripts = true,  // jquery-ui, raphael.js, annotation.js
    IncludeAnnotationBar     = true   // the embedded annotation ribbon
});
ViewBag.IsAnnotationEnabled = viewer.IsAnnotationEnabled;

Behåll hela Viewer-sammansättningen i markupen:

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>

Annotations‑filerna genererar Ribbonen i annBarMount; du kopierar inte dess knappar eller dialoger. Initiera docViewer först, skapa sedan Ribbonen endast när servern bekräftar att Anmärkning är licensierad:

html
@Html.Raw(ViewBag.ViewerScripts)
<script>
    var annBar = null;
    var currentToken = '';

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

    @if ((bool)ViewBag.IsAnnotationEnabled)
    {
        <text>
    annBar = $('#annBarMount').doconutAnnotationBar({
        docId: 'ctlDoc',
        getRequestParams: function () { return { token: currentToken }; },
        onLayout: function () { requestAnimationFrame(function () { objViewer.Refit(); }); }
    });
    annBar.attach(objViewer);
        </text>
    }
</script>

Utan funktionen utelämnar ReferenceScripts Ribbon‑skriptet, så server‑side if‑satsen skyddar ett oövervakat doconutAnnotationBar(...)‑anrop från att bryta sidan. I Web Forms skriver du samma skydd som <% if (IsAnnotationEnabled) { %> … <% } %>.

Sparande från Ribbonen postar data till DocImage.axd (AnnSave), som lagrar den i dokumentsessionen per sida. Laddning (AnnLoad) sker automatiskt när en sida med anmärkningar renderas.

Öppna och stäng Ribbonen från ditt eget verktygsfält med annBar.open() och annBar.close(). Dess publika API:

MetodSyfte
attach(objViewer)Koppla Ribbonen till den initierade visaren; krävs en gång
open() / close()Starta eller avsluta anmärkningsredigering
reset()Återställ Ribbonen till dess stängda, icke‑redigerande tillstånd
isOpen() / annotating()Läs Ribbonens tillstånd / visarens anmärkningsredigeringstillstånd
reopenEditable()Läs om den aktuella sidans anmärkningar som redigerbara objekt
updateActionState()Uppdatera spara‑ och raderingskontrollerna efter värdförändringar
headerSlot()Den valfria rubrikplatsen för värdeägda kontroller

onStatus, onToast, onLayout, onEditStart och onEditEnd är valfria värd‑callback‑funktioner. endpoints‑objektet kan också tillhandahålla exportPdf, exportPng, imageUpload och imageList; kontroller utan en konfigurerad endpoint förblir dolda.

Två typer av återuppringning, kopplade på två sätt

Annotationskoden använder två återuppringningsstilar, och varje stil misslyckas tyst när den kopplas på det andra sättet:

  • onAnnLoaded, onAnnSaved, onAnnSaveError, onAnnClosed är alternativ till docViewer({...}), precis som varje visningsåteruppringning.
  • Created, Changed, Deleted och Properties är globala funktioner som hittas efter namn: visnings‑ID:t prefixas till händelsen. För visaren i #div_ctlDoc är ID:t ctlDoc, så funktionerna är ctlDoc_Created, ctlDoc_Changed, ctlDoc_Deleted och ctlDoc_Properties. En felstavad eller omdöpt funktion anropas helt enkelt aldrig.

SaveAnnotations() returnerar inte om den sparade: resultatet kommer via onAnnSaved eller onAnnSaveError. Kod som läser dess returvärde rensar aldrig flaggan för "osparade ändringar".

Bygg annotationer i C#

Hämta en manager bunden till den öppna sessionen, lägg till annotationer och ladda dem (med using Doconut.Annotations; för typerna och using System.Drawing; för Rectangle och Color):

csharp
// POST /Annotations/LoadSample?token=...
[HttpPost]
public ActionResult LoadSample(string token)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<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 viewer fetches them, and the renderer burns them into
    // image/PDF exports.
    viewer.LoadAnnotationData(token, manager);
    return new HttpStatusCodeResult(200);
}

Annotationstyper

Alla typer finns i Doconut.Annotations och ärver från BaseAnnotation (sidnummer plus avgränsande Rectangle):

TypAnteckningar
StampAnnotationTextstämpel med teckenstorlek, kant, färg; stöder Opacity, Rotate
NoteAnnotationKlistrig lapp med text, bakgrundsfärg, teckenstorlek, TitleColor
RectangleAnnotationKant- och fyllnadsfärger, Title/ShowTitle
CircleAnnotationKant och fyllning, ShowBorder
EllipseAnnotationKant och fyllning, ShowBorder
TriangleAnnotationKantfärg, BackColor, ShowBorder
LineAnnotationRaka linjen med bredd och färg
ArrowAnnotationLinje med pilspets; inställningsbar Direction (ArrowDirection, kompassriktningar, standard E)
FreehandAnnotationFri penseldrag från kodade FreehandData-punkter
ImageAnnotationBild från en URL. En relativ URL löses upp mot den aktuella begärans värd när annotationen läggs till — se exportnoteringen nedan

AnnotationManager‑API

MedlemSyfte
Add(BaseAnnotation)Köa en annotation
GetAnnotations() / GetAnnotations(int page)Inspektera vad manageren innehåller
ClearAnnotations() / ClearAnnotations(int page)Ta bort alla / per sida
GetAnnotationData() / GetAnnotationData(int page)Kodat annoteringsdata — omslaget som widgeten konsumerar
GetAnnotationXml()XML‑form

Viewer speglar laddnings‑ och läsoperationerna mot en session: LoadAnnotationData(token, manager) eller LoadAnnotationData(token, encodedData), LoadAnnotationXML(token, xml), och GetAnnotationXML(token).

Export med inbrända annotationer

csharp
// PDF of all pages with annotations rendered onto them
public async Task<ActionResult> ExportPdf(string token)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    byte[] pdf = await viewer.ExportAnnotationsToPdfAsync(token, zoom: 100, ct: Response.ClientDisconnectedToken);
    return File(pdf, "application/pdf", "export.pdf");
}

// Or a ZIP of per-page PNGs
public async Task<ActionResult> ExportPngZip(string token)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    byte[] zip = await viewer.ExportAnnotationsToPngZipAsync(token, zoom: 100, ct: Response.ClientDisconnectedToken);
    return File(zip, "application/zip", "annotations-png.zip");
}

Export använder samma brännare som skärmrendering, och samma licens‑ och anpassade vattenstämpel‑beslut.

Bildanteckningar på en intranätvärd exporteras som "Bild blockerad"

Visaren och exporten hämtar en ImageAnnotations bild från olika platser:

YtaVem laddar ner bildenPå en intranät- eller utvecklingsvärd
Visaren i webbläsarenAnvändarens webbläsareBilden visas
ExportAnnotationsToPdfAsync / …PngZipAsyncServernEn röd "Bild blockerad" platshållare

När servern hämtar en bild för att bränna in den, avvisar den adresser som pekar tillbaka till privata nätverk: loopback (localhost, 127.0.0.1), 0.0.0.0, link‑local (169.254.x.x), de privata intervallen 10.x, 172.16–31.x och 192.168.x, carrier‑grade NAT (100.64.0.0/10) samt IPv6 link‑local och unique‑local adresser. Detta skyddar servern från att användas för att nå interna system via en konstruerad bild‑URL, och det finns ingen omkopplare för att stänga av detta.

En relativ bild‑URL löses mot värden för den aktuella begäran, så på localhost, på en intranät‑server eller på någon webbplats som nås via en privat adress, visas bilden i visaren och exporten innehåller platshållaren istället. Det är skyddet som fungerar, inte ett renderingsfel. Om exporten måste innehålla bilden, servera den från en värd med en publik adress och använd en absolut URL. En bild‑URL som inte kan lösas alls får den anteckningen att försvinna utan fel — kontrollera de renderade pixlarna eller nätverksloggen, inte en statuskod.

Arbetsflöde för beständighet

  1. Öppna dokumentet och hämta dess token.
  2. Ladda tidigare lagrad XML eller kodad data i den token.
  3. Låt widgeten läsa och redigera sessions‑anteckningarna.
  4. Hämta XML med GetAnnotationXML(token) när din applikation beslutar att bestå.
  5. Exportera PDF eller PNG när en platt leverans krävs.
  6. Stäng dokumentsessionen.

Använd inte visartoken som en permanent identifierare för anteckningar — den dör med sessionen, och en återställning av applikationspoolen avslutar varje session på en enskild server. Spara anteckningsdata mot dina egna dokument‑ och versionsidentifierare.

Säkerhet och renderingsanteckningar

  • Anteckningsförfrågningar använder samma sessions‑ och token‑säkerhet som sidförfrågningar.
  • Validera och kontrollera alla användargenererade bild‑URL:er.
  • Stora handritade payloads och högupplösta export ökar minnesanvändning; testa realistiska dokument och zoomvärden.

Felsökning

SymtomKontroll
Annoteringsverktygsfältet saknasAnnotation‑kapaciteten och de fyra annoterings‑CSS/script‑flaggorna
Sparande rapporterar ett felToken‑ eller sessionsutgång, och widgetens BasePath
ctlDoc_Created (eller en annan händelsefunktion) körs aldrigFunktionsnamnet: visare‑id + _ + händelse, som en global funktion
C#‑anteckningar visas inteSidnumren är en‑baserade, och data laddades in i den aktiva token
En bildanteckning visas på skärmen men inte i exportenBildens värd har en privat eller loopback‑adress — se ovan
Ett återöppnat dokument har inga anteckningarSpara XML eller data utanför visarsessionen, ladda sedan in dem i den nya token

Var den här sidan hjälpsam?