💡 ตัวอย่างทำงานเต็มที่พร้อมใช้งานบน GitHub:
extract-annotations-from-pdf-using-groupdocs-parser-dotnet

Introduction

PDF ที่ผ่านการตรวจสอบมักจะมีข้อมูลมากกว่าข้อความที่มองเห็นได้ – โน้ตสติ๊กกี้, ข้อความที่ไฮไลท์, และคอมเมนต์แบบอินไลน์ที่ผู้ตรวจสอบทิ้งไว้ การเลื่อนดูทุกหน้าเพื่อค้นหาเหล่านี้ไม่สามารถทำได้อย่างมีประสิทธิภาพเมื่อเอกสารผ่านหลายรอบของข้อเสนอแนะ GroupDocs.Parser เป็นไลบรารี .NET ที่อ่าน annotation ที่ฝังอยู่ในเอกสารโดยอัตโนมัติ ทำให้คอมเมนต์ที่กระจัดกระจายของผู้ตรวจสอบกลายเป็นข้อมูลโครงสร้างที่โค้ดของคุณสามารถประมวลผลได้ บทแนะนำนี้จะแสดงวิธีดึง annotation จาก PDF ทั้งไฟล์, แยกตามหน้า, ดึงพร้อมกับข้อความของเอกสาร, และส่งออกผลลัพธ์เป็น CSV หรือ JSON

ฉันเจอปัญหานี้ขณะสร้างระบบติดตามการตรวจสอบสำหรับทีมเอกสาร: โน้ตปล่อยเวอร์ชัน 40 หน้าได้ผ่านผู้ตรวจสอบสามคน และการเปิดไฟล์เพื่อค้นหาคอมเมนต์ทุกอันใช้เวลานานกว่าการแก้ไขปัญหาที่พวกเขาแจ้งไว้ การดึง annotation ด้วยไม่กี่บรรทัดของโค้ดทำให้งานเสร็จในสองนาที

ในส่วนต่อไปนี้คุณจะได้เรียนรู้วิธี:

  • ดึง annotation ทั้งหมดจาก PDF ด้วยการเรียกครั้งเดียว
  • แท็กแต่ละ annotation ด้วยหน้าที่มันอยู่
  • ดึงข้อความของเอกสารและข้อความของ annotation มาร่วมกันในหนึ่งการอ่าน
  • แปลงผลลัพธ์เป็น CSV หรือ JSON เพื่อใช้ต่อในเครื่องมืออื่น

Why Extracting PDF Annotations Matters

การอ่าน PDF annotation ด้วยโปรแกรมมีประโยชน์สำหรับ:

  • กระบวนการตรวจสอบ: รวบรวมคอมเมนต์ของผู้ตรวจสอบทั้งหมดโดยไม่ต้องเปิดไฟล์ในโปรแกรมดู PDF
  • การทำงานร่วมกัน: แสดงส่วนที่ไฮไลท์หรือมีโน้ตโดยตรงในเครื่องมือของคุณ
  • การตรวจสอบ: เก็บบันทึกของการทำเครื่องหมายบนเอกสารตามเวลา แม้หลังจากที่เอกสารถูกแบนหรือสรุปแล้ว

GroupDocs.Parser เพิ่มการดึง annotation แบบเนทีฟสำหรับไฟล์ PDF ในเวอร์ชัน 26.7 ผ่านเมธอด GetAnnotations พร้อมตัวเลือกใหม่ IncludeAnnotations บน TextOptions เพื่อดึงข้อความ annotation เข้าไปในผลลัพธ์ข้อความปกติ

Prerequisites

  • .NET 6.0 หรือใหม่กว่า
  • GroupDocs.Parser for .NET 26.7+ (temporary license)
  • ไฟล์ PDF ที่มี annotation อยู่แล้ว (เช่น document-with-annotations.pdf)

ติดตั้งผ่าน NuGet:

dotnet add package GroupDocs.Parser

How do I extract annotations from a PDF document?

Answer: โหลดไฟล์ด้วย Parser แล้วเรียก GetAnnotations() สำหรับทั้งเอกสารหรือ GetAnnotations(pageIndex) สำหรับหน้าเดียว แต่ละผลลัพธ์เป็นคอลเลกชันของอ็อบเจ็กต์ AnnotationItem ที่มีคุณสมบัติ Value เก็บข้อความคอมเมนต์ หากต้องการเห็นคอมเมนต์รวมกับเนื้อหาเอกสารปกติ ให้ตั้งค่า IncludeAnnotations บน TextOptions แล้วเรียก GetText แทน

Whole‑Document Extraction

โค้ดสแนปต่อไปนี้ดึง annotation ทั้งหมดจากไฟล์ด้วยการเรียกครั้งเดียว ซึ่งเป็นวิธีที่เร็วที่สุดในการตรวจสอบว่าเอกสารมีคอมเมนต์เปิดอยู่หรือไม่

// Extract every annotation from the whole document
var result = new List<string>();
using (var parser = new Parser(path))
{
    IEnumerable<AnnotationItem> annotations = parser.GetAnnotations();
    if (annotations == null)
    {
        return result; // format doesn't support annotations
    }

    foreach (var item in annotations)
    {
        result.Add(item.Value); // annotation text
    }
}
return result;

Key points:

  • GetAnnotations() จะคืนค่า null เมื่อฟอร์แมตไม่รองรับการดึง annotation, และคืนคอลเลกชันว่างเมื่อเอกสารไม่มีคอมเมนต์เลย
  • แต่ละ AnnotationItem เปิดเผยข้อความผ่านคุณสมบัติ Value – นี่คือข้อมูลจุดเดียวที่ SDK รายงานในขณะนี้
  • ไม่มีการระบุหน้าที่นี่; ใช้ overload ตามหน้าที่แสดงด้านล่างหากต้องการ

Per‑Page Extraction

เมื่อตำแหน่งของคอมเมนต์สำคัญ ให้วนลูปผ่านหน้าของเอกสารและเรียก GetAnnotations(pageIndex) สำหรับแต่ละหน้า

// Tag each annotation with its zero-based page index
var result = new List<AnnotationRecord>();
using (var parser = new Parser(path))
{
    if (!parser.Features.Annotations)
    {
        return result;
    }

    var info = parser.GetDocumentInfo();
    if (info == null || info.PageCount == 0)
    {
        return result;
    }

    for (int pageIndex = 0; pageIndex < info.PageCount; pageIndex++)
    {
        IEnumerable<AnnotationItem> pageAnnotations = parser.GetAnnotations(pageIndex);
        if (pageAnnotations == null)
        {
            continue;
        }

        foreach (var item in pageAnnotations)
        {
            result.Add(new AnnotationRecord { PageIndex = pageIndex, Value = item.Value });
        }
    }
}
return result;

Key points:

  • GetDocumentInfo().PageCount เป็นตัวขับลูป; ไม่มี “จำนวนหน้าที่มี annotation” แยกต่างหาก
  • GetAnnotations(pageIndex) ใช้ดัชนีเริ่มจากศูนย์ เหมือนเมธอดระดับหน้าทั้งหมดใน API
  • รายการ AnnotationRecord ที่ได้มีรูปแบบที่เหมาะกับการส่งออกเป็น CSV หรือ JSON

Extracting Text Together with Annotations

แทนที่จะทำสองรอบบนเอกสาร คุณสามารถผสานข้อความ annotation เข้าไปในผลลัพธ์การดึงข้อความปกติได้โดยตรง

// Read document text with annotation text included
using (var parser = new Parser(path))
{
    var options = new TextOptions
    {
        IncludeAnnotations = true
    };

    using (TextReader reader = parser.GetText(options))
    {
        return reader?.ReadToEnd() ?? string.Empty;
    }
}

Key points:

  • IncludeAnnotations เป็นคุณสมบัติของ TextOptions ดังนั้นจึงทำงานร่วมกับการเรียก GetText ที่คุณใช้สำหรับการดึงข้อความธรรมดา
  • มีประโยชน์เมื่อต้องการผลลัพธ์แบบทรานสคริปต์เดียวแทนรายการคอมเมนต์แยกต่างหาก
  • สามารถผสานกับ GetText(pageIndex, options) หากต้องการเฉพาะหน้าหนึ่ง

Checking Annotation Support First

ไม่ใช่ทุกฟอร์แมตที่รองรับ annotation ดังนั้นควรตรวจสอบก่อนที่จะเขียนตรรกะรอบ GetAnnotations

// Returns true if the loaded document format supports annotation extraction
using (var parser = new Parser(path))
{
    return parser.Features.Annotations;
}

Key points:

  • Features.Annotations เป็นแฟล็กบูลีนง่าย ๆ บนอินสแตนซ์ Parser
  • การตรวจสอบล่วงหน้าช่วยทำให้เจตนาโค้ดชัดเจน แม้ว่า GetAnnotations จะคืนค่า null อย่างสุภาพอยู่แล้ว

Exporting the Annotations to CSV

การส่งออกเป็น CSV ทำให้ผู้ตรวจสอบสามารถเปิดรายการคอมเมนต์โดยตรงใน Excel วิธีด้านล่างเขียนไฟล์สองคอลัมน์ (page,value) จากรายการที่มีการแท็กหน้าที่สร้างไว้ก่อนหน้า

var sb = new StringBuilder();
sb.AppendLine("page,value");

foreach (var record in records)
{
    sb.AppendLine($"{record.PageIndex},{CsvEscape(record.Value)}");
}

File.WriteAllText(outputPath, sb.ToString());

Key points:

  • CsvEscape ทำการใส่เครื่องหมายคำพูดอย่างปลอดภัยสำหรับฟิลด์ที่มีคอมม่า, เครื่องหมายคำพูด, หรือการขึ้นบรรทัดใหม่
  • ไฟล์ที่ได้เปิดโดยตรงใน Excel หรือสามารถส่งต่อไปยังเครื่องมือจัดการตั๋วได้

Helper: CsvEscape

if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
    return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;

Exporting the Annotations to JSON

สำหรับ pipeline ที่ต้องการคอมเมนต์แบบโปรแกรมเมติก JSON array มักจะเหมาะกว่า CSV มาก

var sb = new StringBuilder();
sb.AppendLine("[");

for (int i = 0; i < records.Count; i++)
{
    var comma = i < records.Count - 1 ? "," : string.Empty;
    sb.AppendLine($"  {{ \"page\": {records[i].PageIndex}, \"value\": \"{Escape(records[i].Value)}\" }}{comma}");
}

sb.AppendLine("]");
File.WriteAllText(outputPath, sb.ToString());

Key points:

  • ผลลัพธ์เป็นอาร์เรย์แบนของอ็อบเจ็กต์ { page, value } – ง่ายต่อการ deserialize โดยบริการ downstream ใด ๆ
  • Escape ทำให้ payload เป็น JSON ที่ถูกต้องโดยไม่ต้องนำเข้าไลบรารีการแปลง

Helper: Escape

return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;

Comparing Methods: When to Use Each

Method Best For Key Advantages Limitations
Whole‑Document Extraction ตรวจสอบอย่างรวดเร็วว่า “มีคอมเมนต์หรือไม่?” เรียกครั้งเดียว, โค้ดง่ายที่สุด ไม่มีการระบุหน้า
Per‑Page Extraction ส่งต่อฟีดแบ็กไปยังส่วนที่ถูกต้อง ผลลัพธ์มีการแท็กหน้า, พร้อมส่งออก ต้องเรียกเพิ่มหนึ่งครั้งต่อหน้า
Combined Text + Annotations สร้างทรานสคริปต์เดียวที่อ่านได้ ไม่ต้องทำรอบที่สองบนเอกสาร คอมเมนต์ไม่แยกจากข้อความหลัก
CSV Export การติดตามการตรวจสอบแบบสเปรดชีต เปิดง่ายใน Excel, อ่านได้โดยมนุษย์ โครงสร้างแบน จำกัด
JSON Export Pipeline อัตโนมัติ, ระบบตั๋ว โครงสร้าง, อ่านโดยเครื่อง ขนาด payload มากกว่าบางกรณี

เริ่มด้วยการดึงแบบ Whole‑Document เพื่อตรวจสอบว่าไฟล์มีคอมเมนต์ที่ต้องดำเนินการหรือไม่, จากนั้นสลับไปใช้ Per‑Page เมื่อจำเป็นต้องระบุตำแหน่งฟีดแบ็ก

Best Practices and Tips

  • Dispose Parser ทันที: ใช้ using block เพื่อปล่อยทรัพยากรเนทีฟ
  • แยกแยะ null กับค่าว่าง: GetAnnotations คืน null หมายถึงฟอร์แมตไม่รองรับ; คอลเลกชันว่างหมายถึงไม่มีคอมเมนต์
  • ตรวจสอบ Features.Annotations ในงานแบช: ข้ามไฟล์ที่ไม่รองรับตั้งแต่ต้น แทนการตรวจ null ภายในลูป
  • ใช้รายการที่แท็กหน้าครั้งเดียว: สร้างรายการด้วย ExtractAnnotationsByPage แล้วส่งต่อให้ทั้งตัวส่งออก CSV และ JSON เพื่อให้ผลลัพธ์สองแบบสอดคล้องกัน
  • ความปลอดภัย: ข้อความ annotation เป็นข้อมูลที่ผู้ตรวจสอบใส่เอง – ควรจัดการเหมือนสตริงที่ไม่เชื่อถือได้ก่อนแสดงใน UI หรือรายงาน

Conclusion

GroupDocs.Parser ให้วิธีโดยตรงและโปรแกรมเมติกในการดึงคอมเมนต์ของผู้ตรวจสอบออกจาก PDF แทนการค้นหาด้วยตนเอง โดยการดึง annotation ทั้งไฟล์, แท็กตามหน้า, หรือผสานเข้ากับข้อความปกติ คุณสามารถสร้างกระบวนการตรวจสอบที่แสดงฟีดแบ็กทันทีเมื่อเอกสารเข้าสู่ pipeline ของคุณ ส่งออกผลลัพธ์เป็น CSV หรือ JSON แล้วเชื่อมต่อกับเครื่องมือที่ทีมของคุณใช้อยู่แล้ว

Next steps:

  • สำรวจ GetAnnotations API reference เพื่อดูลายเซ็นเมธอดเต็มและ overloads
  • เรียนรู้วิธี extract text from PDF documents ควบคู่กับ annotation เพื่อสร้าง pipeline เนื้อหาที่ครบถ้วน
  • ดูตัวอย่างโปรเจกต์เพิ่มเติมบน GitHub สำหรับสถานการณ์การประมวลผลแบบแบช (Examples Repo)

Additional Resources