การทำหมายเหตุ

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

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

html
<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 ใดก็ได้:

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

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

TypeNotes
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

MemberPurpose
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 ที่ฝังไว้

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

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

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

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/ข้อมูลไว้ภายนอกเซสชันของผู้ดู, จากนั้นโหลดเข้าสู่โทเคนใหม่

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