การทำหมายเหตุ
เพิ่มการสนับสนุนการทำหมายเหตุให้กับตัวดู
การทำหมายเหตุใน Doconut ทำงานในสองทิศทาง: ผู้ใช้วาดหมายเหตุในวิดเจ็ตของเบราว์เซอร์และเซิร์ฟเวอร์บันทึกไว้ต่อหน้า, หรือโค้ดของคุณสร้างหมายเหตุแบบโปรแกรมและโหลดเข้าสู่เซสชันที่เปิดอยู่ ไม่ว่ากรณีใดก็จะถูกแสดงบนหน้าและสามารถฝังลงในไฟล์ PDF/PNG เมื่อส่งออกได้
การสนับสนุนการทำหมายเหตุถูกจำกัดโดยความสามารถของใบอนุญาต Annotation (ได้รับโดยอัตโนมัติเมื่อมีใบอนุญาต Temporary ที่ใช้งานอยู่)
เปิดใช้งาน UI การทำหมายเหตุ
การทำหมายเหตุเป็นโมดูลของ Viewer, ไม่ใช่แถบเครื่องมือแบบสแตนด์อโลน หน้าเว็บทั้งหมดต้องรวมทรัพยากร Viewer, แถบเครื่องมือ Viewer, จุดเชื่อมต่อ Viewer, และ objViewer ที่ได้เริ่มต้นแล้ว; จากนั้น Ribbon ของการทำหมายเหตุจะถูกเมานท์และผูกกับอินสแตนซ์เดียวกันนั้น
ส่งออกชุดไฟล์การทำหมายเหตุพร้อมกับชุดไฟล์ Viewer — ทั้งสองชุดไฟล์ถูกจำกัดด้วยใบอนุญาต, ดังนั้นแท็กจะปรากฏเฉพาะเมื่อความสามารถพร้อมใช้งาน:
@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 ทั้งหมดมองเห็นได้ใน markup:
<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>ชุดไฟล์การทำหมายเหตุสร้าง Ribbon DOM ภายใน annBarMount; คุณไม่จำเป็นต้องคัดลอกปุ่มหรือ markup ของไดอะล็อก. เริ่มต้น docViewer ก่อน, แล้วสร้าง Ribbon เฉพาะเมื่อเซิร์ฟเวอร์ยืนยันว่าการทำหมายเหตุได้รับใบอนุญาตแล้ว:
<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>การบันทึกจาก Ribbon จะส่งข้อมูลผ่าน middleware (AnnSave), ซึ่งจะเก็บไว้ในเซสชันเอกสารต่อหน้า. การโหลด (AnnLoad) จะทำโดยอัตโนมัติเมื่อหน้าที่มีหมายเหตุถูกเรนเดอร์. คอลแบ็กสี่ตัว onAnn* ทำให้ Ribbon ซิงโครไนซ์กับวงจรชีวิตของ viewer
เปิดและปิด Ribbon จากแถบเครื่องมือ Viewer ที่เป็นของ host ใดก็ได้:
annBar.open();
annBar.close();API สาธารณะของ Ribbon มีดังนี้:
| วิธีการ | วัตถุประสงค์ |
|---|---|
attach(objViewer) | เชื่อมต่อ Ribbon กับ viewer ที่ได้เริ่มต้นแล้ว; จำเป็นต้องทำครั้งเดียว |
open() / close() | เริ่มหรือหยุดการแก้ไขหมายเหตุ |
reset() | รีเซ็ต Ribbon ให้กลับสู่สถานะปิดและไม่อยู่ในโหมดแก้ไข |
isOpen() / annotating() | อ่านสถานะของ Ribbon / สถานะการแก้ไขหมายเหตุของ viewer |
reopenEditable() | โหลดหมายเหตุของหน้าปัจจุบันใหม่เป็นวัตถุที่สามารถแก้ไขได้ |
updateActionState() | อัปเดตความพร้อมของการควบคุมบันทึก/ลบหลังจากการเปลี่ยนแปลงของ host |
headerSlot() | รับช่องส่วนหัวเสริม (optional) สำหรับควบคุมที่เป็นของ host |
onStatus, onToast, onLayout, onEditStart, และ onEditEnd เป็นคอลแบ็กของ host ที่เป็นทางเลือก. อ็อบเจ็กต์ endpoints สามารถให้บริการเพิ่มเติมเช่น exportPdf, exportPng, imageUpload, และ imageList; ควบคุมที่ไม่มีการกำหนด endpoint จะถูกซ่อน. สำหรับลำดับการเริ่มต้นของ Viewer, Search, และ Annotation ที่รวมกัน, ดูที่ เริ่มต้นอย่างเร็ว.
ชุดไฟล์การทำหมายเหตุเพิ่มเครื่องมือการเขียนในเบราว์เซอร์, แต่ข้อมูลยังคงเป็นของเซสชันเอกสารฝั่งเซิร์ฟเวอร์ที่ระบุด้วย token. การเปิดซ้ำแหล่งข้อมูลจะสร้างเซสชันใหม่; ควรบันทึก XML หรือ envelope ของหมายเหตุที่เข้ารหัสไว้ในแอปพลิเคชันของคุณหากต้องการให้หมายเหตุคงอยู่หลังจากเซสชันสิ้นสุด.
สร้าง annotation ใน C#
รับตัวจัดการที่ผูกกับเซสชันที่เปิดอยู่, เพิ่ม annotation, และโหลดพวกมัน (โดยใช้ 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();
});ประเภทของ Annotation
All types live in Doconut.Annotations and inherit from BaseAnnotation (page number + bounding Rectangle):
| Type | Notes |
|---|---|
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 relative จะถูกแก้ไขโดยอิงกับโฮสต์ของคำขอเมื่อเพิ่ม annotation (การดึงรูปภาพเกิดขึ้นเฉพาะตอนบันทึก) — ต้องสามารถเข้าถึงได้จากเซิร์ฟเวอร์ (เช่นไฟล์ภายใต้ wwwroot ที่ให้บริการโดย UseStaticFiles) |
API ของ AnnotationManager
| Member | Purpose |
|---|---|
Add(BaseAnnotation) | คิว annotation |
GetAnnotations() / GetAnnotations(int page) | ตรวจสอบสิ่งที่ตัวจัดการเก็บไว้ |
ClearAnnotations() / ClearAnnotations(int page) | ลบทั้งหมด / ตามหน้า |
GetAnnotationData() / GetAnnotationData(int page) | สตริงข้อมูล annotation ที่เข้ารหัส — ซองข้อมูล Base64 (สิ่งที่ widget ใช้) |
GetAnnotationXml() | รูปแบบ XML |
Viewer mirrors the load/read operations against a session: LoadAnnotationData(token, manager) or LoadAnnotationData(token, encodedData) (the Base64 wire envelope from GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).
ส่งออกพร้อม annotation ที่ฝังไว้
// 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");
});Exports use the same burner as on-screen rendering, so what users see is what the file contains.
กระบวนการทำงานของการคงสภาพ
- เปิดเอกสารและรับ token ของมัน
- โหลด XML หรือข้อมูลที่เข้ารหัสที่เก็บไว้ก่อนหน้านี้เข้าสู่ token นั้น
- ให้ widget อ่านและแก้ไข annotation ของเซสชัน
- ดึง XML ด้วย
GetAnnotationXML(token)เมื่อแอปพลิเคชันของคุณตัดสินใจบันทึก - ส่งออก PDF/PNG เมื่อจำเป็นต้องมีไฟล์แบน
- ปิดเซสชันเอกสาร
Do not use the opaque viewer token as a permanent annotation identifier. Associate persisted annotation data with your own document and version identifiers.
หมายเหตุด้านความปลอดภัยและการเรนเดอร์
- คำขอการทำหมายเหตุใช้การรักษาความปลอดภัยของเซสชัน/โทเคนเดียวกับคำขอหน้า
- URL ของ
ImageAnnotationที่เป็นแบบสัมพันธ์จะถูกแก้ไขจากโฮสต์ของคำขอและต้องสามารถเข้าถึงได้โดยเซิร์ฟเวอร์ในช่วงเวลาการเรนเดอร์ - ตรวจสอบและควบคุม URL รูปภาพที่ผู้ใช้ส่งมาเพื่อหลีกเลี่ยงการปลอมแปลงคำขอจากฝั่งเซิร์ฟเวอร์
- การส่งออกใช้การตัดสินใจเรื่องใบอนุญาต/ลายน้ำแบบกำหนดเองเช่นเดียวกับการเรนเดอร์หน้าบนหน้าจอ
- การส่งออกที่มีข้อมูลฟรีแฮนด์ขนาดใหญ่และความละเอียดสูงจะเพิ่มการใช้หน่วยความจำ; ควรทดสอบเอกสารและค่าซูมที่เป็นจริง
การแก้ไขปัญหา
| อาการ | การตรวจสอบ |
|---|---|
| แถบริบบิ้นการทำหมายเหตุหายไป | ความสามารถ Annotation และสี่แฟล็ก CSS/สคริปต์ของการทำหมายเหตุ |
| คอลแบ็กการบันทึกรายงานข้อผิดพลาด | การหมดอายุของโทเคน/เซสชันและมิดเดิลแวร์ BasePath |
| การทำหมายเหตุ C# ไม่แสดง | การนับหน้าตั้งแต่หนึ่งและข้อมูลถูกโหลดเข้าสู่โทเคนที่ใช้งานอยู่ |
| การทำหมายเหตุรูปภาพแสดงบนหน้าจอแต่ไม่อยู่ในการส่งออก | เซิร์ฟเวอร์สามารถเข้าถึง URL ของรูปภาพในระหว่างการเรนเดอร์ |
| เอกสารที่เปิดใหม่ไม่มีการทำหมายเหตุ | บันทึก XML/ข้อมูลไว้ภายนอกเซสชันของผู้ดู, จากนั้นโหลดเข้าสู่โทเคนใหม่ |
หน้านี้เป็นประโยชน์หรือไม่?