หมายเหตุ

เพิ่มการสนับสนุนหมายเหตุให้กับตัวดู

หมายเหตุใน Doconut ทำงานในสองทิศทาง: ผู้ใช้วาดมันในวิดเจ็ตของเบราว์เซอร์และเซิร์ฟเวอร์บันทึกไว้ต่อหน้า, หรือโค้ดของคุณสร้างมันโดยโปรแกรมและโหลดเข้าเซสชันที่เปิดอยู่ ไม่ว่าด้วยวิธีใดก็จะถูกแสดงบนหน้าและสามารถฝังลงในไฟล์ PDF/PNG ที่ส่งออกได้.

การสนับสนุนหมายเหตุถูกจำกัดโดยความสามารถของใบอนุญาต Annotation (ได้รับโดยอัตโนมัติภายใต้ใบอนุญาต Temporary ที่ใช้งานอยู่).

เปิดใช้งาน UI ของหมายเหตุ

หมายเหตุเป็นโมดูลของ Viewer ไม่ใช่แถบเครื่องมือแยกส่วน หน้าเต็มต้องรวมทรัพยากร Viewer, แถบเครื่องมือ Viewer, การเมานท์ Viewer, และ objViewer ที่ได้เริ่มต้นแล้ว; จากนั้น Ribbon ของหมายเหตุจะถูกเมานท์และแนบกับอินสแตนซ์เดียวกันนั้น.

ส่งออกชุดบันเดิลของหมายเหตุพร้อมกับบันเดิลของ Viewer — พวกมันถูกจำกัดโดยใบอนุญาต, ดังนั้นแท็กจะปรากฏเฉพาะเมื่อความสามารถพร้อมใช้งาน:

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 ทั้งหมดให้มองเห็นใน markup:

html
<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 เฉพาะเมื่อเซิร์ฟเวอร์ยืนยันว่าหมายเหตุได้รับใบอนุญาต:

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>

การบันทึกจาก Ribbon จะส่งข้อมูลผ่าน middleware (AnnSave), ซึ่งเก็บไว้ในเซสชันเอกสารต่อหน้า. การโหลด (AnnLoad) เกิดขึ้นโดยอัตโนมัติเมื่อหน้าที่มีหมายเหตุถูกเรนเดอร์. คอลแบ็กสี่ตัว onAnn* ทำให้ Ribbon ซิงค์กับวงจรชีวิตของ viewer.

เปิดและปิดมันจากแถบเครื่องมือ Viewer ที่เป็นของโฮสต์ใดก็ได้:

javascript
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):

csharp
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).

ส่งออกพร้อมการทำเครื่องหมายที่ฝังไว้

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

การส่งออกใช้เครื่องมือบันทึกเดียวกับการเรนเดอร์บนหน้าจอ, ดังนั้นสิ่งที่ผู้ใช้เห็นคือสิ่งที่ไฟล์มีอยู่.

กระบวนการทำงานของการคงสภาพ

  1. เปิดเอกสารและรับโทเค็นของมัน.
  2. โหลด XML หรือข้อมูลที่เข้ารหัสที่เก็บไว้ก่อนหน้านี้เข้าสู่โทเค็นนั้น.
  3. ให้วิดเจ็ตอ่านและแก้ไขการทำเครื่องหมายของเซสชัน.
  4. ดึง XML ด้วย GetAnnotationXML(token) เมื่อแอปพลิเคชันของคุณตัดสินใจบันทึก.
  5. ส่งออก PDF/PNG เมื่อจำเป็นต้องมีไฟล์แบน.
  6. ปิดเซสชันเอกสาร.

ห้ามใช้โทเค็น viewer ที่ไม่โปร่งใสเป็นตัวระบุการทำเครื่องหมายถาวร. ให้เชื่อมข้อมูลการทำเครื่องหมายที่คงสภาพกับเอกสารและตัวระบุเวอร์ชันของคุณเอง.

หมายเหตุด้านความปลอดภัยและการเรนเดอร์

  • คำขอการทำหมายเหตุใช้การรักษาความปลอดภัย session/token เดียวกับคำขอหน้า
  • URL ImageAnnotation แบบสัมพันธ์จะถูกแก้ไขจากโฮสต์ของคำขอและต้องสามารถเข้าถึงได้โดยเซิร์ฟเวอร์ในเวลาการเรนเดอร์
  • ตรวจสอบและควบคุม URL รูปภาพที่ผู้ใช้ให้มาเพื่อหลีกเลี่ยงการปลอมแปลงคำขอฝั่งเซิร์ฟเวอร์
  • การส่งออกใช้การตัดสินใจเกี่ยวกับใบอนุญาต/ลายน้ำแบบกำหนดเองเดียวกับการเรนเดอร์หน้าบนหน้าจอ
  • ข้อมูลฟรีแฮนด์ขนาดใหญ่และการส่งออกความละเอียดสูงจะเพิ่มการใช้หน่วยความจำ; ทดสอบเอกสารและค่าซูมที่เป็นจริง

การแก้ไขปัญหา

อาการการตรวจสอบ
ริบบอนการทำหมายเหตุหายไปAnnotation ความสามารถและสี่แฟล็ก CSS/สคริปต์ของการทำหมายเหตุ
คอลแบ็กการบันทึกรายงานข้อผิดพลาดToken/session การหมดอายุและมิดเดิลแวร์ BasePath
การทำหมายเหตุ C# ไม่แสดงการจัดหมายเลขหน้าเป็นแบบหนึ่งฐานและข้อมูลถูกโหลดเข้าสู่โทเค็นที่ใช้งาน
การทำหมายเหตุรูปภาพปรากฏบนหน้าจอแต่ไม่อยู่ในการส่งออกเซิร์ฟเวอร์สามารถเข้าถึง URL รูปภาพได้ในระหว่างการเรนเดอร์
เอกสารที่เปิดใหม่ไม่มีการทำหมายเหตุบันทึก XML/ข้อมูลนอกเซสชันของ viewer แล้วโหลดเข้าสู่โทเค็นใหม่

หน้านี้เป็นประโยชน์หรือไม่?