הערות

הוספת תמיכה בהערות לצופה

הערות ב‑Doconut פועלות בשני כיוונים: משתמשים מציירים אותן בווידג'ט של הדפדפן והשרת שומר אותן לכל עמוד, או שהקוד שלך בונה אותן תכנותית וטוען אותן למפגש פתוח. בכל מקרה הן מוצגות על העמודים וניתן לשלב אותן בייצוא PDF/PNG.

תמיכת ההערות מתאפשרת רק כאשר יש רישיון Annotation (ניתן אוטומטית ברישיון זמני פעיל).

הפעלת ממשק המשתמש של ההערה

הערה היא מודול של Viewer, לא סרגל נפרד. על הדף המלא לכלול את משאבי ה‑Viewer, סרגל ה‑Viewer, מיקום ה‑Viewer, וה‑objViewer שהותחל; לאחר מכן מתקין את ריבון ההערה ומחבר אותו לאותו מופע.

הטען את חבילות ההערה יחד עם חבילות הצופה — הן מגוונות לפי רישיון, ולכן התגים מופיעים רק כאשר היכולת זמינה:

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

שמור על הרכבה מלאה של Viewer גלויה במרקאפ:

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>

חבילת ההערה מייצרת את ריבון ה‑DOM בתוך annBarMount; אין צורך להעתיק את הכפתורים או את מרקאפ הדיאלוג שלה. אתחל תחילה את docViewer, ואז צור את הריבון רק כאשר השרת מאשר שההערה מורשית:

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>

שמירה מהריבון מפרסמת נתונים דרך המידלוור (AnnSave), אשר שומר אותם ב‑session של המסמך לכל עמוד. טעינה (AnnLoad) מתבצעת אוטומטית כאשר עמוד עם הערות מוצג. ארבעת ה‑callbacks onAnn* משמרים את הריבון מסונכרן עם מחזור החיים של הצופה.

פתח וסגור אותו מכל סרגל Viewer שבבעלות המארח:

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

ה‑API הציבורי של הריבון הוא:

מתודהמטרה
attach(objViewer)מחבר את הריבון לצופה שהותחל; נדרש פעם אחת
open() / close()נכנס או יוצא ממצב עריכת ההערה
reset()מחזיר את הריבון למצב סגור, ללא עריכה
isOpen() / annotating()קורא את מצב הריבון / את מצב עריכת ההערה של הצופה
reopenEditable()טוען מחדש את ההערות של העמוד הנוכחי כאובייקטים ניתנים לעריכה
updateActionState()מרענן זמינות פקודות שמירה/מחיקה לאחר שינויי המארח
headerSlot()מקבל את משבצת ההרחבה האופציונלית לכותרת עבור פקודות שבבעלות המארח

onStatus, onToast, onLayout, onEditStart, ו‑onEditEnd הם callbacks אופציונליים של המארח. האובייקט endpoints יכול בנוסף לספק exportPdf, exportPng, imageUpload, ו‑imageList; פקודות ללא נקודת קצה מוגדרת נשארות מוסתרות. לרצף האתחול המשולב של Viewer, Search וה‑Annotation, ראה התחלה מהירה.

חבילת ההערה מוסיפה את כלי העריכה בדפדפן, אך הנתונים עדיין שייכים ל‑session של המסמך בצד השרת שמזוהה על‑ידי הטוקן. פתיחה מחדש של המקור יוצרת session חדש; שמור את ה‑XML או את מעטפת ההערה המקודדת באפליקציה שלך אם ההערות חייבות לשרוד מעבר לחיי ה‑session.

בניית הערות ב‑ C#

קבל מנהל קשור ל‑session הפתוח, הוסף הערות, וטען אותן (עם using Doconut.Annotations; עבור הסוגים ו‑using System.Drawing; עבור 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();
});

סוגי הערות

כל הסוגים נמצאים ב‑Doconut.Annotations ויורשים מ‑BaseAnnotation (מספר עמוד + Rectangle גבול):

סוגהערות
StampAnnotationחותמת טקסט עם גודל גופן, גבול, צבע; תומך ב‑Opacity, Rotate
NoteAnnotationפתק דביק עם טקסט, צבע רקע, גודל גופן, TitleColor
RectangleAnnotationגבול + צבעי מילוי, Title/ShowTitle
CircleAnnotationגבול + מילוי, ShowBorder
EllipseAnnotationגבול + מילוי, ShowBorder
TriangleAnnotationצבע גבול, BackColor, ShowBorder
LineAnnotationקו ישר עם עובי וצבע
ArrowAnnotationקו עם ראש חץ; ניתן להגדיר Direction (סוג ArrowDirection, נקודות מצפן, ברירת מחדל E)
FreehandAnnotationקו חופשי מנקודות FreehandData מקודדות
ImageAnnotationתמונה מ‑URL. URL יחסי נפתר יחסית למארח הבקשה כאשר מוסיפים את ההערה (רק שליפת התמונה מתרחשת בזמן השריפה) — הוא חייב להיות נגיש מהשרת (למשל קובץ תחת wwwroot המוגש על‑ידי UseStaticFiles)

API של AnnotationManager

חברמטרה
Add(BaseAnnotation)מוסיף הערה לתור
GetAnnotations() / GetAnnotations(int page)מציג את מה שהמנהל מחזיק
ClearAnnotations() / ClearAnnotations(int page)מוחק הכל / לפי עמוד
GetAnnotationData() / GetAnnotationData(int page)מחרוזת נתוני ההערה המקודדת — מעטפת Base64 (מה שהווידג'ט צורך)
GetAnnotationXml()צורת XML

Viewer משקף את פעולות הטעינה/קריאה מול session: LoadAnnotationData(token, manager) או LoadAnnotationData(token, encodedData) (מעטפת Base64 מ‑GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).

ייצוא עם הערות משולבות

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

הייצוא משתמש באותו מנגנון שריפה כמו רינדור על המסך, ולכן מה שהמשתמשים רואים הוא מה שהקובץ מכיל.

תהליך שמירת נתונים

  1. פתח את המסמך וקבל את הטוקן שלו.
  2. טען XML או נתונים מקודדים שנשמרו קודם לכן לתוך הטוקן.
  3. אפשר לווידג'ט לקרוא ולערוך את ההערות ב‑session.
  4. קבל XML באמצעות GetAnnotationXML(token) כאשר האפליקציה שלך מחליטה לשמור.
  5. ייצא PDF/PNG כאשר נדרש קובץ משטחים.
  6. סגור את session המסמך.

אל תשתמש בטוקן הצופה השקוף כמזהה קבוע של ההערה. קשר את נתוני ההערה השמורים למזהי המסמך והגרסה שלך.

הערות אבטחה והצגה

  • בקשות הערה משתמשות באותה בטיחות של session/טוקן כמו בקשות עמוד.
  • URL של ImageAnnotation יחסי נפתר ממארח הבקשה וחייב להישאר נגיש לשרת בזמן השריפה.
  • אמת ושלוט בכל URL תמונה שמסופק על‑ידי משתמש כדי למנוע זיוף בקשות צד שרת.
  • ייצוא מתחשב באותו רישיון/סימן מים מותאם כמו רינדור עמוד על המסך.
  • עומסים גדולים של נתוני חופשיים וייצוא ברזולוציה גבוהה מגדילים שימוש בזיכרון; יש לבדוק מסמכים וערכי זום ריאליים.

פתרון בעיות

תסמיןבדיקה
ריבון ההערה חסריכולת Annotation והדגלים ארבעת של CSS/Script של ההערה
קריאת שמירת callback מדווחת על שגיאהפקיעת טוקן/סשן וה‑BasePath של המידלוור
הערות C# אינן מופיעותמספרי העמודים מתחילים מ‑1 והנתונים נטענו לטוקן הפעיל
הערת תמונה מוצגת על המסך אך לא בייצואהשרת יכול להגיע ל‑URL של התמונה בזמן השריפה
מסמך שנפתח מחדש ללא הערותשמור XML/נתונים מחוץ ל‑session של הצופה, ואז טען אותם לטוקן החדש

האם דף זה היה מועיל?