หมายเหตุ
เพิ่มการสนับสนุนหมายเหตุให้กับตัวดู
หมายเหตุใน 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, รวมถึงปุ่มที่เปิด Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>บันเดิลของหมายเหตุสร้าง DOM ของ Ribbon ภายใน annBarMount; คุณไม่จำเป็นต้องคัดลอกปุ่มหรือ markup ของไดอะล็อกของมัน. เริ่มต้น docViewer ก่อน, จากนั้นสร้าง Ribbon เฉพาะเมื่อเซิร์ฟเวอร์ยืนยันว่าหมายเหตุได้รับใบอนุญาต:
<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 จะส่งข้อมูลผ่าน middleware (AnnSave), ซึ่งเก็บไว้ในเซสชันเอกสารต่อหน้า. การโหลด (AnnLoad) เกิดขึ้นโดยอัตโนมัติเมื่อหน้าที่มีหมายเหตุถูกเรนเดอร์. คอลแบ็กสี่ตัว onAnn* ทำให้ Ribbon ซิงค์กับวงจรชีวิตของ viewer.
เปิดและปิดมันจากแถบเครื่องมือ Viewer ที่เป็นของโฮสต์ใดก็ได้:
annBar.open();
annBar.close();API สาธารณะของ Ribbon คือ:
| Method | วัตถุประสงค์ |
|---|---|
attach(objViewer) | เชื่อมต่อ Ribbon กับ viewer ที่ได้เริ่มต้นแล้ว; ต้องทำครั้งเดียว |
open() / close() | เริ่มหรือหยุดการแก้ไขหมายเหตุ |
reset() | คืนค่า Ribbon ให้เป็นสถานะปิด, ไม่อยู่ในโหมดแก้ไข |
isOpen() / annotating() | อ่านสถานะของ Ribbon / สถานะการแก้ไขหมายเหตุของ viewer |
reopenEditable() | โหลดหมายเหตุของหน้าปัจจุบันใหม่เป็นออบเจ็กต์ที่แก้ไขได้ |
updateActionState() | รีเฟรชความพร้อมของปุ่มบันทึก/ลบหลังจากการเปลี่ยนแปลงของโฮสต์ |
headerSlot() | รับช่องส่วนหัวเสริมแบบเลือกสำหรับควบคุมที่เป็นของโฮสต์ |
onStatus, onToast, onLayout, onEditStart, และ onEditEnd เป็นคอลแบ็กของโฮสต์ที่เป็นตัวเลือก. อ็อบเจ็กต์ endpoints สามารถให้บริการเพิ่มเติมเช่น exportPdf, exportPng, imageUpload, และ imageList; ควบคุมที่ไม่มี endpoint ที่กำหนดจะถูกซ่อน. สำหรับลำดับการเริ่มต้นของ 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 และ
// renderer ทำให้พวกมันกลายเป็นการส่งออกเป็นภาพ/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 relative จะถูกแก้ไขโดยอ้างอิงกับโฮสต์ของคำขอเมื่อเพิ่มการทำเครื่องหมาย (การดึงรูปภาพจะเกิดขึ้นเฉพาะในขั้นตอนการบันทึก) — ต้องสามารถเข้าถึงได้จากเซิร์ฟเวอร์ (เช่น ไฟล์ภายใต้ wwwroot ที่ให้บริการโดย UseStaticFiles) |
API ของ 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 ของ PNG ต่อหน้า
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 เมื่อจำเป็นต้องมีไฟล์แบน.
- ปิดเซสชันเอกสาร.
ห้ามใช้โทเค็น viewer ที่ไม่โปร่งใสเป็นตัวระบุการทำเครื่องหมายถาวร. ให้เชื่อมข้อมูลการทำเครื่องหมายที่คงสภาพกับเอกสารและตัวระบุเวอร์ชันของคุณเอง.
หมายเหตุด้านความปลอดภัยและการเรนเดอร์
- คำขอการทำหมายเหตุใช้การรักษาความปลอดภัย session/token เดียวกับคำขอหน้า
- URL
ImageAnnotationแบบสัมพันธ์จะถูกแก้ไขจากโฮสต์ของคำขอและต้องสามารถเข้าถึงได้โดยเซิร์ฟเวอร์ในเวลาการเรนเดอร์ - ตรวจสอบและควบคุม URL รูปภาพที่ผู้ใช้ให้มาเพื่อหลีกเลี่ยงการปลอมแปลงคำขอฝั่งเซิร์ฟเวอร์
- การส่งออกใช้การตัดสินใจเกี่ยวกับใบอนุญาต/ลายน้ำแบบกำหนดเองเดียวกับการเรนเดอร์หน้าบนหน้าจอ
- ข้อมูลฟรีแฮนด์ขนาดใหญ่และการส่งออกความละเอียดสูงจะเพิ่มการใช้หน่วยความจำ; ทดสอบเอกสารและค่าซูมที่เป็นจริง
การแก้ไขปัญหา
| อาการ | การตรวจสอบ |
|---|---|
| ริบบอนการทำหมายเหตุหายไป | Annotation ความสามารถและสี่แฟล็ก CSS/สคริปต์ของการทำหมายเหตุ |
| คอลแบ็กการบันทึกรายงานข้อผิดพลาด | Token/session การหมดอายุและมิดเดิลแวร์ BasePath |
| การทำหมายเหตุ C# ไม่แสดง | การจัดหมายเลขหน้าเป็นแบบหนึ่งฐานและข้อมูลถูกโหลดเข้าสู่โทเค็นที่ใช้งาน |
| การทำหมายเหตุรูปภาพปรากฏบนหน้าจอแต่ไม่อยู่ในการส่งออก | เซิร์ฟเวอร์สามารถเข้าถึง URL รูปภาพได้ในระหว่างการเรนเดอร์ |
| เอกสารที่เปิดใหม่ไม่มีการทำหมายเหตุ | บันทึก XML/ข้อมูลนอกเซสชันของ viewer แล้วโหลดเข้าสู่โทเค็นใหม่ |
หน้านี้เป็นประโยชน์หรือไม่?