💡 ตัวอย่างทำงานเต็มที่มีบน GitHub:
extract-annotations-from-pdf-using-groupdocs-parser-dotnet
บทนำ
PDF ที่ผ่านการตรวจสอบมักจะมีข้อมูลมากกว่าข้อความที่มองเห็นได้ – โน้ตสติ๊กกี้, การไฮไลท์, และคอมเมนต์แบบอินไลน์ที่ผู้ตรวจสอบทิ้งไว้ การเลื่อนดูทุกหน้าเพื่อค้นหาเหล่านี้ไม่สามารถทำได้เมื่อเอกสารผ่านหลายรอบของฟีดแบ็ก GroupDocs.Parser เป็นไลบรารี .NET ที่อ่าน Annotation ที่ฝังอยู่ในเอกสารแบบโปรแกรมเมติก, เปลี่ยนคอมเมนต์ที่กระ散ของผู้ตรวจสอบให้เป็นข้อมูลโครงสร้างที่โค้ดของคุณสามารถทำงานได้ บทเรียนนี้จะแสดงวิธีสกัด Annotation จาก PDF ทั้งไฟล์, แบ่งตามหน้า, ดึงพร้อมกับข้อความของเอกสาร, และส่งออกผลลัพธ์เป็น CSV หรือ JSON
ฉันเจอปัญหานี้ขณะสร้างตัวติดตามการตรวจสอบสำหรับทีมเอกสาร: โน้ตปล่อยเวอร์ชัน 40 หน้าได้ผ่านผู้ตรวจสอบสามคน, และการเปิดไฟล์เพื่อค้นหาคอมเมนต์ทุกอันใช้เวลานานกว่าการแก้ไขปัญหาที่พวกเขาแจ้งไว้ การสกัด Annotation ด้วยไม่กี่บรรทัดของโค้ดทำให้งานนี้เสร็จในสองนาที
ในส่วนต่อไปนี้คุณจะได้เรียนรู้วิธี:
- สกัด Annotation ทุกอันจาก PDF ในหนึ่งครั้ง
- แท็กแต่ละ Annotation ด้วยหน้าที่มันอยู่
- ดึงข้อความของเอกสารและข้อความ Annotation มาร่วมกันในหนึ่งการอ่าน
- แปลงผลลัพธ์เป็น CSV หรือ JSON เพื่อใช้ในเครื่องมือต่อไป
ทำไมการสกัด Annotation ของ PDF ถึงสำคัญ
การอ่าน Annotation ของ PDF แบบโปรแกรมเมติกมีประโยชน์สำหรับ:
- กระบวนการตรวจสอบ: รวบรวมคอมเมนต์ของผู้ตรวจสอบทุกอันโดยไม่ต้องเปิดไฟล์ในโปรแกรมดู PDF
- การทำงานร่วมกัน: แสดงส่วนที่ไฮไลท์หรือมีโน้ตโดยตรงในเครื่องมือของคุณ
- การตรวจสอบ: เก็บบันทึกของการทำเครื่องหมายบนเอกสารตามเวลา, แม้หลังจากที่เอกสารถูกแบนหรือสรุปแล้ว
GroupDocs.Parser เพิ่มการสกัด Annotation แบบเนทีฟสำหรับเอกสาร PDF ในเวอร์ชัน 26.7 ผ่านเมธอด GetAnnotations พร้อมตัวเลือกใหม่ IncludeAnnotations บน TextOptions เพื่อดึงข้อความ Annotation เข้ากับการอ่านข้อความปกติ
ข้อกำหนดเบื้องต้น
- .NET 6.0 หรือใหม่กว่า
- GroupDocs.Parser for .NET 26.7+ (temporary license)
- ไฟล์ PDF ที่มี Annotation อยู่แล้ว (เช่น
document-with-annotations.pdf)
ติดตั้งผ่าน NuGet:
dotnet add package GroupDocs.Parser
ฉันจะสกัด Annotation จากเอกสาร PDF อย่างไร?
คำตอบ: โหลดไฟล์ด้วย Parser, แล้วเรียก GetAnnotations() สำหรับทั้งเอกสารหรือ GetAnnotations(pageIndex) สำหรับหน้าเดียว แต่ละผลลัพธ์เป็นคอลเลกชันของอ็อบเจ็กต์ AnnotationItem ที่มีคุณสมบัติ Value เก็บข้อความคอมเมนต์ หากคุณต้องการเห็นคอมเมนต์แบบอินไลน์กับเนื้อหาเอกสารปกติ ให้ตั้งค่า IncludeAnnotations บน TextOptions แล้วเรียก GetText แทน
การสกัดแบบทั้งเอกสาร
โค้ดสั้น ๆ ด้านล่างจะดึง 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;
ประเด็นสำคัญ:
GetAnnotations()จะคืนค่าnullเมื่อฟอร์แมตไม่รองรับการสกัด Annotation, และคืนคอลเลกชันว่างเมื่อเอกสารไม่มีคอมเมนต์เลย- แต่ละ
AnnotationItemเปิดเผยข้อความผ่านคุณสมบัติValue– นี่คือข้อมูลจุดเดียวที่ SDK รายงานในขณะนี้ - ไม่มีการระบุหน้าที่แนบมาที่นี่; ใช้ overload per‑page ด้านล่างหากต้องการ
การสกัดแบบต่อหน้า
เมื่อตำแหน่งของคอมเมนต์สำคัญ, ให้วนลูปผ่านหน้าของเอกสารและเรียก 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;
ประเด็นสำคัญ:
GetDocumentInfo().PageCountเป็นตัวขับลูป; ไม่มี “จำนวนหน้าที่มี Annotation” แยกออกมาGetAnnotations(pageIndex)ใช้ดัชนีเริ่มจากศูนย์, ตรงกับเมธอดระดับหน้าทุกตัวใน API- รายการ
AnnotationRecordที่ได้มีรูปแบบที่เหมาะกับการส่งออกเป็น CSV หรือ JSON
การสกัดข้อความพร้อมกับ Annotation
แทนที่จะทำสองรอบบนเอกสาร, คุณสามารถผสานข้อความ 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;
}
}
ประเด็นสำคัญ:
IncludeAnnotationsเป็นพร็อพเพอร์ตี้บนTextOptions, ดังนั้นจึงทำงานร่วมกับการเรียกGetTextที่คุณใช้สำหรับการสกัดข้อความธรรมดา- มีประโยชน์เมื่อคุณต้องการผลลัพธ์สไตล์ทรานสคริปต์เดียวแทนรายการคอมเมนต์แยกต่างหาก
- สามารถผสานกับ
GetText(pageIndex, options)หากต้องการเฉพาะหน้าเดียว
ตรวจสอบการสนับสนุน Annotation ก่อน
ไม่ใช่ทุกฟอร์แมตที่รองรับ Annotation, ดังนั้นควรตรวจสอบก่อนที่จะเขียนตรรกะรอบ GetAnnotations
// Returns true if the loaded document format supports annotation extraction
using (var parser = new Parser(path))
{
return parser.Features.Annotations;
}
ประเด็นสำคัญ:
Features.Annotationsเป็นแฟล็กบูลีนง่าย ๆ บนอินสแตนซ์Parser- การตรวจสอบล่วงหน้าทำให้เจตนาเป็นที่ชัดเจน, แม้ว่า
GetAnnotationsจะล้มเหลวอย่างสุภาพโดยคืนค่าnullอยู่แล้ว
การส่งออก Annotation ไปยัง 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());
ประเด็นสำคัญ:
CsvEscapeทำการใส่เครื่องหมายคำพูดอย่างปลอดภัยสำหรับฟิลด์ที่มีคอมม่า, เครื่องหมายคำพูด, หรือการขึ้นบรรทัดใหม่- ไฟล์ที่ได้เปิดโดยตรงใน Excel หรือสามารถส่งต่อเข้าเครื่องมือจัดการตั๋วได้
Helper: CsvEscape
if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;
การส่งออก Annotation ไปยัง JSON
สำหรับ pipeline ที่ต้องการคอมเมนต์แบบโปรแกรมเมติก, อาร์เรย์ JSON มักจะเหมาะกว่า 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());
ประเด็นสำคัญ:
- ผลลัพธ์เป็นอาร์เรย์แบนของอ็อบเจ็กต์
{ page, value }– ง่ายต่อการ deserialize โดยบริการ downstream ใด ๆ Escapeทำให้ payload เป็น JSON ที่ถูกต้องโดยไม่ต้องดึงไลบรารีการแปลงข้อมูลเข้ามา
Helper: Escape
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;
การเปรียบเทียบวิธีการ: เมื่อใช้แต่ละวิธี
| วิธี | เหมาะสำหรับ | ข้อได้เปรียบหลัก | ข้อจำกัด |
|---|---|---|---|
| การสกัดแบบทั้งเอกสาร | ตรวจสอบอย่างรวดเร็วว่า “มีคอมเมนต์ใด ๆ หรือไม่?” | เรียกครั้งเดียว, โค้ดง่ายที่สุด | ไม่มีการระบุหน้า |
| การสกัดแบบต่อหน้า | ส่งฟีดแบ็กไปยังส่วนที่ถูกต้อง | ผลลัพธ์มีการแท็กหน้า, พร้อมส่งออก | ต้องเรียกเพิ่มหนึ่งครั้งต่อหน้า |
| ข้อความรวมกับ Annotation | สร้างทรานสคริปต์อ่านได้เดียว | ไม่ต้องทำรอบสองบนเอกสาร | คอมเมนต์ไม่ได้แยกจากข้อความหลัก |
| ส่งออก CSV | การติดตามการตรวจสอบแบบสเปรดชีต | เปิดง่ายใน Excel, อ่านได้โดยมนุษย์ | โครงสร้างแบนจำกัด |
| ส่งออก JSON | Pipeline อัตโนมัติ, ระบบตั๋ว | โครงสร้าง, อ่านได้โดยเครื่อง | ขนาด payload มากกว่านิดหน่อย |
เริ่มต้นด้วยการสกัดแบบทั้งเอกสารเพื่อยืนยันว่าไฟล์มีคอมเมนต์ที่ต้องดำเนินการ, จากนั้นสลับไปใช้การสกัดแบบต่อหน้าเมื่อจำเป็นต้องระบุตำแหน่งฟีดแบ็ก
แนวทางปฏิบัติที่ดีที่สุดและเคล็ดลับ
- Dispose
Parserทันที: ห่อไว้ในบล็อกusingเพื่อปล่อยทรัพยากรเนทีฟ - แยกความแตกต่างระหว่าง
nullกับค่าว่าง:GetAnnotationsคืนค่าnullหมายถึงฟอร์แมตไม่รองรับ; คอลเลกชันว่างหมายถึงเอกสารไม่มีคอมเมนต์ - ตรวจสอบ
Features.Annotationsในงานแบตช์: ข้ามไฟล์ที่ไม่รองรับตั้งแต่ต้นแทนการตรวจสอบnullภายในลูป - ใช้รายการที่มีการแท็กหน้าเดียวกัน: สร้างรายการครั้งเดียวด้วย
ExtractAnnotationsByPageแล้วส่งต่อให้ทั้งตัวส่งออก CSV และ JSON จากข้อมูลเดียวกัน เพื่อให้ผลลัพธ์สองแบบไม่แตกต่างกัน - ความปลอดภัย: ข้อความ Annotation เป็นข้อมูลที่ผู้ตรวจสอบใส่เอง – ควรปฏิบัติเช่นเดียวกับสตริงที่ไม่เชื่อถือได้ก่อนแสดงใน UI หรือรายงาน
สรุป
GroupDocs.Parser ให้วิธีโดยตรงและโปรแกรมเมติกในการดึงคอมเมนต์ของผู้ตรวจสอบออกจาก PDF แทนการค้นหาด้วยตนเอง โดยการสกัด Annotation ทั้งเอกสาร, แท็กตามหน้า, หรือผสานเข้ากับสตรีมข้อความปกติ คุณสามารถสร้างกระบวนการตรวจสอบที่แสดงฟีดแบ็กทันทีเมื่อเอกสารเข้าสู่ pipeline ของคุณ ส่งออกผลลัพธ์เป็น CSV หรือ JSON แล้วเชื่อมต่อเข้ากับเครื่องมือที่ทีมของคุณใช้แล้ว
ขั้นตอนต่อไป:
- สำรวจ GetAnnotations API reference เพื่อดูลายเซ็นเมธอดเต็มและ overloads
- เรียนรู้วิธี extract text from PDF documents ควบคู่กับ Annotation เพื่อสร้าง pipeline เนื้อหาที่ครบถ้วน
- ดูตัวอย่างโปรเจกต์เพิ่มเติมบน GitHub สำหรับสถานการณ์การประมวลผลแบบแบตช์ (Examples Repo)