הערות
הוסף תמיכה בהערות לצופה
הערות ב‑Doconut פועלות בשני כיוונים: משתמשים מציירים אותן בווידג'ט של הדפדפן והשרת שומר אותן לפי עמוד, או שהקוד שלך בונה אותן באופן תכנותי וטוען אותן למפגש פתוח. בכל מקרה הן מוצגות על העמודים וניתן לשלבן ביצוא PDF/PNG.
תמיכת ההערות מוגבלת על‑ידי יכולת הרישיון Annotation (ניתנת אוטומטית תחת רישיון זמני פעיל).
הפעל את ממשק ההערות
הערה היא מודול של Viewer, לא סרגל כלים עצמאי. על הדף המלא לכלול את משאבי ה‑Viewer, סרגל הכלים של Viewer, הרכבה של Viewer, ואת objViewer שהאותחל; לאחר מכן רצועת ההערה (Annotation Ribbon) מותקנת ומחוברת לאותו מופע.
הפץ את חבילות ההערה יחד עם חבילות הצופה — הן מוגבלות ברישיון, ולכן התגים מופיעים רק כאשר היכולת זמינה:
@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 המלאה גלויה במרקאפ:
<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, ולאחר מכן צור את הרצועה רק כאשר השרת מאשר שההערה מורשית:
<script>
let annBar = null;
let currentToken = '';
const objViewer = $('#div_ctlDoc').docViewer({
BasePath: '/',
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), השומר אותם במפגש המסמך לפי עמוד. טעינה (AnnLoad) מתרחשת אוטומטית כאשר עמוד עם הערות מוצג. ארבעת קריאות הקול onAnn* משמרות את סנכרון הרצועה עם מחזור החיים של הצופה.
פתח וסגור אותה מכל סרגל כלים של Viewer שבבעלות המארח:
annBar.open();
annBar.close();ממשק ה‑API הציבורי של הרצועה הוא:
| מתודה | מטרה |
|---|---|
attach(objViewer) | מחבר את ה‑Ribbon לצופה שהותחל; נדרש פעם אחת |
open() / close() | כניסה או יציאה ממצב עריכת ההערות |
reset() | מחזיר את ה‑Ribbon למצב סגור, ללא עריכה |
isOpen() / annotating() | קרא את מצב ה‑Ribbon / את מצב עריכת ההערות של הצופה |
reopenEditable() | טען מחדש את ההערות של העמוד הנוכחי כאובייקטים שניתנים לעריכה |
updateActionState() | רענן את זמינות בקרות השמירה/מחיקה לאחר שינויי המארח |
headerSlot() | קבל את משבצת ההרחבה האופציונלית בכותרת לבקרות של המארח |
onStatus, onToast, onLayout, onEditStart, ו‑onEditEnd הם קריאות חזרה אופציונליות של המארח. האובייקט endpoints יכול בנוסף לספק exportPdf, exportPng, imageUpload, ו‑imageList; בקרות ללא נקודת קצה מוגדרת נשארות מוסתרות. עבור רצף האתחול המשולב של Viewer, Search וההערה, ראה התחלה מהירה.
חבילת ההערה מוסיפה את כלי הכתיבה בדפדפן, אך הנתונים עדיין שייכים למפגש המסמך בצד השרת המזוהה על‑ידי הטוקן. פתיחה מחדש של המקור יוצרת מפגש חדש; שמור את ה‑XML או את מעטפת ההערה המקודדת באפליקציה שלך אם ההערות חייבות לשרוד מעבר לחיי המפגש.
בניית אנוטציות ב‑C#
קבל מנהל הקשור למפגש הפתוח, הוסף אנוטציות וטען אותן (עם using Doconut.Annotations; לסוגים ו‑using System.Drawing; עבור Rectangle/Color):
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. כתובת יחסית נפתרת ביחס למארח הבקשה כאשר האנוטציה מתווספת (רק משיכת התמונה מתרחשת בזמן השריפה) — היא חייבת להיות נגישה מהשרת (למשל קובץ תחת wwwroot המוגש על‑ידי UseStaticFiles) |
ממשק ה‑AnnotationManager
| חבר | מטרה |
|---|---|
Add(BaseAnnotation) | מוסיף אנוטציה לתור |
GetAnnotations() / GetAnnotations(int page) | בוחן מה המנהל מחזיק |
ClearAnnotations() / ClearAnnotations(int page) | מוחק את כולם / לפי עמוד |
GetAnnotationData() / GetAnnotationData(int page) | מחרוזת נתוני אנוטציה מקודדת — מעטפת Base64 (מה שהווידג'ט צורך) |
GetAnnotationXml() | צורת XML |
Viewer משקף את פעולות הטעינה/קריאה מול מפגש: LoadAnnotationData(token, manager) או LoadAnnotationData(token, encodedData) (מעטפת Base64 מ‑GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).
ייצוא עם אנוטציות משולבות
// 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");
});הייצוא משתמש באותו מנגנון שריפה כמו רינדור על המסך, ולכן מה שהמשתמשים רואים הוא מה שהקובץ מכיל.
זרימת עבודה של שמירת נתונים
- פתח את המסמך וקבל את הטוקן שלו.
- טען XML או נתונים מקודדים ששמרו מראש לתוך הטוקן.
- אפשר לווידג'ט לקרוא ולערוך את האנוטציות של המפגש.
- קבל XML באמצעות
GetAnnotationXML(token)כאשר האפליקציה שלך מחליטה לשמור. - ייצא PDF/PNG כאשר נדרש קובץ משטחים.
- סגור את מפגש המסמך.
אל תשתמש בטוקן הצופה האופקי כמזהה קבוע לאנוטציה. קשר את נתוני האנוטציה השמורים עם מזהי המסמך והגרסה שלך.
הערות אבטחה והצגה
- בקשות ההערה משתמשות באותה אבטחת session/token כמו בקשות דף.
- כתובת URL יחסית של
ImageAnnotationנפתרת מה‑host של הבקשה ויש להישאר נגישה לשרת בזמן הבערה. - יש לאמת ולשלוט בכל כתובת URL של תמונה שמסופקת על ידי המשתמש כדי למנוע זיוף בקשות בצד השרת.
- ייצוא משתמש באותו החלטת רישיון/סימן מים מותאם כמו הצגת דף על המסך.
- מטענים חופשיים גדולים וייצוא ברזולוציה גבוהה מגדילים את השימוש בזיכרון; יש לבדוק מסמכים מציאותיים וערכי זום.
פתרון בעיות
| תסמין | בדיקה |
|---|---|
| סרגל ההערות חסר | יכולת Annotation והארבעה דגלי CSS/סקריפט של ההערות |
| קריאת חזרה של שמירה מדווחת על שגיאה | תפוגת Token/session ו‑middleware BasePath |
| הערות C# אינן מופיעות | מספור העמודים מתחיל מ‑1 והנתונים נטענו לטוקן הפעיל |
| הערת תמונה מופיעה על המסך אך לא בייצוא | השרת יכול לגשת לכתובת URL של התמונה בזמן הבערה |
| מסמך שנפתח מחדש אין בו הערות | שמרו XML/נתונים מחוץ למפגש הצופה, ואז טענו אותם לטוקן החדש |
האם דף זה היה מועיל?