Anotace

Přidejte podporu anotací do prohlížeče

Anotace v Doconut fungují ve dvou směrech: uživatelé je kreslí ve widgetu prohlížeče a server je ukládá po stránkách, nebo je váš kód vytváří a načítá do otevřené relace. V každém případě se vykreslují na stránkách a mohou být vloženy do exportů PDF nebo PNG.

Podpora anotací je řízena licenční schopností Annotation (udělenou automaticky při aktivní dočasné licenci).

Povolení uživatelského rozhraní anotací

Anotace jsou modulem Vieweru, nikoli samostatným nástrojovým panelem. Stránka musí zahrnovat zdroje Vieweru, nástrojový panel Vieweru, umístění Vieweru a inicializovaný objViewer; poté je pás Anotací namontován a připojen k téže instanci.

Vydávejte soubory anotací spolu se soubory vieweru. Jsou řízeny licencí, takže jejich značky se zobrazí pouze když je schopnost udělena. V MVC 5 vytvořte značky v kontroleru (pohledy nemají @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;

Uchovejte kompletní složení Vieweru v markupu:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Ovládací prvky Vieweru, včetně tlačítka, které otevírá Anotaci -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>

Souborů anotací generují pás uvnitř annBarMount; nekopírujte jeho tlačítka ani dialogy. Nejprve inicializujte docViewer, pak vytvořte pás pouze když server potvrdí, že anotace jsou licencovány:

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>

Bez této schopnosti ReferenceScripts vynechá skript pásu, takže podmínka na straně serveru if chrání nechráněné volání doconutAnnotationBar(...) před rozbitím stránky. Ve Web Forms napište stejnou ochranu jako <% if (IsAnnotationEnabled) { %> … <% } %>.

Ukládání z pásu odesílá data na DocImage.axd (AnnSave), který je ukládá v relaci dokumentu po stránkách. Načítání (AnnLoad) probíhá automaticky, když se vykreslí stránka s anotacemi.

Otevřete a zavřete pás z vlastního nástrojového panelu pomocí annBar.open() a annBar.close(). Jeho veřejné API:

MetodaÚčel
attach(objViewer)Připojí pás k inicializovanému vieweru; vyžadováno jednou
open() / close()Vstoupit do nebo opustit režim úprav anotací
reset()Vrátí pás do zavřeného, needitačního stavu
isOpen() / annotating()Přečte stav pásu / stav úprav anotací ve vieweru
reopenEditable()Znovu načte anotace aktuální stránky jako editovatelné objekty
updateActionState()Obnoví ovládací prvky pro uložení a smazání po změnách hosta
headerSlot()Volitelný slot hlavičky pro ovládací prvky vlastněné hostem

onStatus, onToast, onLayout, onEditStart a onEditEnd jsou volitelné zpětné volání hosta. Objekt endpoints může také poskytovat exportPdf, exportPng, imageUpload a imageList; ovládací prvky bez nastaveného koncového bodu zůstávají skryté.

Dva typy zpětných volání, zapojené dvěma způsoby

Kód anotací používá dva styly zpětných volání a každý selže tiše, když je zapojen opačným způsobem:

  • onAnnLoaded, onAnnSaved, onAnnSaveError, onAnnClosed jsou volby docViewer({...}), jako každé zpětné volání prohlížeče.
  • Created, Changed, Deleted a Properties jsou globální funkce nalezené podle názvu: ID prohlížeče je předponou události. Pro prohlížeč v #div_ctlDoc je ID ctlDoc, takže funkce jsou ctlDoc_Created, ctlDoc_Changed, ctlDoc_Deleted a ctlDoc_Properties. Špatně napsaná nebo přejmenovaná funkce prostě není nikdy zavolána.

SaveAnnotations() nevrací, zda se uložilo: výsledek přichází přes onAnnSaved nebo onAnnSaveError. Kód, který čte její návratovou hodnotu, nikdy neodstraní příznak „neuložených změn“.

Vytváření anotací v C#

Získejte správce svázaného s otevřenou relací, přidejte anotace a načtěte je (s using Doconut.Annotations; pro typy a using System.Drawing; pro Rectangle a 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);
}

Typy anotací

Všechny typy jsou v Doconut.Annotations a dědí z BaseAnnotation (číslo stránky plus ohraničující Rectangle):

TypPoznámky
StampAnnotationTextová razítko s velikostí písma, okrajem, barvou; podporuje Opacity, Rotate
NoteAnnotationLepící poznámka s textem, barvou pozadí, velikostí písma, TitleColor
RectangleAnnotationBarvy okraje a výplně, Title/ShowTitle
CircleAnnotationOkraj a výplň, ShowBorder
EllipseAnnotationOkraj a výplň, ShowBorder
TriangleAnnotationBarva okraje, BackColor, ShowBorder
LineAnnotationPřímka s šířkou a barvou
ArrowAnnotationČára s šipkou; nastavitelný Direction (ArrowDirection, směry kompasu, výchozí E)
FreehandAnnotationVolná čára z kódovaných bodů FreehandData
ImageAnnotationObrázek z URL. Relativní URL je vyřešena vůči hostiteli aktuálního požadavku, když je anotace přidána — viz poznámka o exportu níže

API AnnotationManageru

ČlenÚčel
Add(BaseAnnotation)Přidá anotaci do fronty
GetAnnotations() / GetAnnotations(int page)Prohlédne, co správce obsahuje
ClearAnnotations() / ClearAnnotations(int page)Odstraní všechny / podle stránky
GetAnnotationData() / GetAnnotationData(int page)Kódovaná data anotací — obálka, kterou widget spotřebuje
GetAnnotationXml()XML forma

Viewer zrcadlí operace načítání a čtení vůči relaci: LoadAnnotationData(token, manager) nebo LoadAnnotationData(token, encodedData), LoadAnnotationXML(token, xml) a GetAnnotationXML(token).

Export s vloženými anotacemi

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

Exporty používají stejný proces vypalování jako vykreslování na obrazovce a stejná rozhodnutí o licenci a vlastním vodoznaku.

Anotace obrázků na intranetovém hostiteli exportují jako „Image blocked“

Prohlížeč a export získávají obrázek ImageAnnotation z různých míst:

RozhraníKdo stahuje obrázekNa intranetovém nebo vývojovém hostiteli
Prohlížeč v prohlížečiProhlížeč uživateleObrázek se zobrazí
ExportAnnotationsToPdfAsync / …PngZipAsyncServerČervený "Image blocked" zástupný obrázek

Když server načte obrázek, aby jej vložil, odmítá adresy, které směřují zpět do soukromých sítí: smyčkovou (localhost, 127.0.0.1), 0.0.0.0, link‑local (169.254.x.x), soukromé rozsahy 10.x, 172.16–31.x a 192.168.x, carrier‑grade NAT (100.64.0.0/10) a IPv6 link‑local a unique‑local adresy. To chrání server před použitím k přístupu k interním systémům prostřednictvím upravené URL obrázku a neexistuje žádný přepínač, který by to vypnul.

Relativní URL obrázku se rozřeší vůči hostiteli aktuálního požadavku, takže na localhost, na intranetovém serveru nebo na jakémkoli webu dosažitelém přes soukromou adresu se obrázek zobrazí v prohlížeči a export místo toho obsahuje zástupný obrázek. To je fungující ochrana, ne selhání vykreslování. Pokud exporty musí obsahovat obrázek, poskytujte jej z hostitele s veřejnou adresou a použijte absolutní URL. URL obrázku, kterou nelze vůbec rozřešit, způsobí, že se daná anotace ztratí bez chyby — zkontrolujte vykreslené pixely nebo síťový log, ne stavový kód.

Pracovní postup uchovávání

  1. Otevřete dokument a získejte jeho token.
  2. Načtěte dříve uložené XML nebo kódovaná data do tohoto tokenu.
  3. Nechte widget načíst a upravit anotace relace.
  4. Získejte XML pomocí GetAnnotationXML(token), když se vaše aplikace rozhodne data uložit.
  5. Exportujte PDF nebo PNG, když je vyžadován plochý výstup.
  6. Ukončete relaci dokumentu.

Neužívejte token prohlížeče jako trvalý identifikátor anotace — zaniká s relací a recyklace aplikačního poolu ukončí každou relaci na jednom serveru. Ukládejte data anotací pod své vlastní identifikátory dokumentu a verze.

Poznámky o zabezpečení a vykreslování

  • Požadavky na anotace používají stejnou bezpečnost relace a tokenu jako požadavky na stránky.
  • Ověřujte a kontrolujte jakoukoli uživatelem dodanou URL obrázku.
  • Velké volné kresby a exporty ve vysokém rozlišení zvyšují spotřebu paměti; testujte realistické dokumenty a hodnoty přiblížení.

Odstraňování potíží

PříznakKontrola
Chybí páska AnotaceAnnotation schopnost a čtyři CSS/script příznaky anotací
Ukládání hlásí chybuVypršení tokenu nebo relace a BasePath widgetu
ctlDoc_Created (nebo jiná funkce události) se nikdy nespustíNázev funkce: id prohlížeče + _ + událost, jako globální funkce
Anotace v C# se neobjevíČísla stránek jsou od jedné a data byla načtena do aktivního tokenu
Anotace obrázku se zobrazuje na obrazovce, ale ne v exportuHostitel obrázku má soukromou nebo smyčkovou adresu — viz výše
Znovu otevřený dokument nemá žádné anotaceUložte XML nebo data mimo relaci prohlížeče a poté je načtěte do nového tokenu

Byla tato stránka užitečná?