הערות
הוספת תמיכת הערות לצופה
הערות ב‑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">
<!-- בקרות הצופה, כולל הכפתור הפותח Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>חבילת ההערות מייצרת את ה‑DOM של ה‑Ribbon בתוך annBarMount; אין צורך להעתיק את הכפתורים או את סימון הדיאלוג שלה. אתחלו תחילה את docViewer, ולאחר מכן צרו את ה‑Ribbon רק כאשר השרת מאשר שה‑Annotation מורשה:
<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>השמירה מה‑Ribbon משדרת נתונים דרך המידלוור (AnnSave), השומר אותם במפגש המסמך לכל עמוד. הטעינה (AnnLoad) מתרחשת אוטומטית כאשר עמוד עם הערות מוצג. ארבעת הקולבקים onAnn* משמרים את ה‑Ribbon מסונכרן עם מחזור החיים של הצופה.
פתחו וסגרו אותו מכל סרגל כלים של Viewer שבבעלות המארח:
annBar.open();
annBar.close();ה‑API הציבורי של ה‑Ribbon הוא:
| מתודה | מטרה |
|---|---|
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 וה‑Annotation, ראו את התחלה מהירה.
חבילת ההערות מוסיפה את כלי העריכה בדפדפן, אך הנתונים עדיין שייכים למפגש המסמך בצד השרת המזוהה באמצעות הטוקן. פתיחה מחדש של המקור יוצרת מפגש חדש; שמרו את ה‑XML או את מעטפת ההערות המקודדת באפליקציה שלכם אם יש צורך שההערות ישרדו מעבר לחיי המפגש.
בניית אנוטציות ב‑C#
קבל מנהל הקשור למפגש הפתוח, הוסף אנוטציות, וטען אותן (עם using Doconut.Annotations; לסוגים ו-using System.Drawing; עבור Rectangle/Color):
app.MapPost("/api/annotations/load-sample", (string token, Viewer viewer) =>
{
// קשור לממדי העמוד של ההפעלה הפתוחה
var manager = viewer.GetAnnotationManager(token);
var pageCount = viewer.GetPageCount(token);
// חותמת אחת לכל עמוד
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));
// טען לתוך ההפעלה — הווידג'ט משיג אותם דרך AnnLoad ו‑
// המנוע משלב אותם ביצואי תמונה/PDF.
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) |
ממשק ה‑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 של כל העמודים עם ההערות משולבות עליהם
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");
});
// או קובץ ZIP של 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 והנתונים נטענו לתוך ה‑token הפעיל |
| האנוטציה של תמונה מופיעה על המסך אך לא בייצוא | השרת יכול להגיע ל‑URL של התמונה בזמן הבערה |
| מסמך שנפתח מחדש אין בו אנוטציות | שמור XML/נתונים מחוץ למפגש הצופה, ואז טען אותם לתוך ה‑token החדש |
האם דף זה היה מועיל?