💡 مثال كامل يعمل متاح على GitHub:
extract-annotations-from-pdf-using-groupdocs-parser-dotnet
المقدمة
عادةً ما يحمل ملف PDF خضع للمراجعة أكثر من النص الظاهر – ملاحظات لاصقة، ملاحظات مميزة، وتعليقات مدمجة تركها المراجعون. التمرير عبر كل صفحة للعثور عليها لا يتناسب عندما يمر المستند بعدة جولات من التعليقات. GroupDocs.Parser هي مكتبة .NET تقرأ التعليقات المدمجة في المستند برمجياً، محوّلةً تعليقات المراجعين المتفرقة إلى بيانات منظمة يمكن لكودك التعامل معها. يوضح هذا الدرس كيفية استخراج التعليقات من ملف PDF كامل، تقسيمها صفحةً بصفحة، سحبها جنبًا إلى جنب مع نص المستند، وتصدير النتائج إلى CSV أو JSON.
صادفت هذه المشكلة أثناء بناء أداة تتبع مراجعات لفريق توثيق: ملاحظة إصدار مكوّنة من 40 صفحة مرت بثلاث مراجعين، وكان فتح الملف يدويًا للعثور على كل تعليق يستغرق وقتًا أطول من إصلاح المشكلات التي أشاروا إليها. استخراج التعليقات ببضع أسطر من الكود حول ذلك إلى مهمة تستغرق دقيقتين فقط.
في الأقسام التالية ستتعلم كيف:
- تستخرج كل تعليق من PDF في عملية واحدة.
- تُوسم كل تعليق بالصفحة التي ينتمي إليها.
- تجمع نص المستند ونص التعليق معًا في قراءة واحدة.
- تسلسل النتائج إلى CSV أو JSON لأدوات المعالجة اللاحقة.
لماذا استخراج تعليقات PDF مهم
قراءة تعليقات PDF برمجياً مفيدة لـ:
- سير عمل المراجعة: جمع كل تعليق مراجع دون فتح الملف في عارض PDF.
- التعاون: إظهار الأقسام المميزة أو الملاحظة مباشرة داخل أدواتك.
- التدقيق: الحفاظ على سجل للعلامات المضافة إلى المستند بمرور الوقت، حتى بعد تسطيحه أو إكماله.
أضافت GroupDocs.Parser استخراج التعليقات الأصلي لملفات PDF في الإصدار 26.7 عبر طريقة GetAnnotations ، إلى جانب خيار جديد IncludeAnnotations في TextOptions لسحب نص التعليق إلى قراءة النص العادية.
المتطلبات المسبقة
- .NET 6.0 أو أحدث
- GroupDocs.Parser for .NET 26.7+ (رخصة مؤقتة)
- ملف PDF يحتوي على تعليقات موجودة (مثال:
document-with-annotations.pdf)
التثبيت عبر NuGet:
dotnet add package GroupDocs.Parser
كيف أستخرج التعليقات من مستند PDF؟
الإجابة: حمّل الملف باستخدام Parser، ثم استدعِ GetAnnotations() للمستند بالكامل أو GetAnnotations(pageIndex) لصفحة واحدة. كل نتيجة هي مجموعة من كائنات AnnotationItem التي يحتوي خاصية Value على نص التعليق. إذا كنت تفضّل رؤية التعليقات مدمجة مع محتوى المستند العادي، عيّن IncludeAnnotations على TextOptions واستدعِ GetText بدلاً من ذلك.
استخراج المستند بالكامل
المقتطف التالي يخرج كل تعليق من الملف في نداء واحد، وهو أسرع طريقة للتحقق مما إذا كان المستند يحتوي على أي تعليقات مفتوحة على الإطلاق.
// 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عندما لا يدعم المستند استخراج التعليقات، وتُعيد مجموعة فارغة عندما لا يحتوي المستند على أي تعليقات.- كل
AnnotationItemيُظهر نصه عبر خاصيةValue– هذه هي النقطة الوحيدة التي يُبلغ عنها SDK حاليًا. - لا يتم تضمين إشارة إلى الصفحة هنا؛ استخدم التحميل حسب الصفحة أدناه إذا كنت بحاجة إليها.
استخراج حسب الصفحة
عندما يهم موقع التعليق، قم بالتكرار عبر صفحات المستند واستدعِ 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يحدد عدد التكرارات؛ لا يوجد عدد صفحات تعليقات منفصل.GetAnnotations(pageIndex)يستخدم فهرسًا يبدأ من الصفر، متطابقًا مع كل طريقة أخرى على مستوى الصفحة في الـ API.- قائمة
AnnotationRecordالناتجة هي بالضبط الشكل الذي يحتاجه تصدير CSV أو JSON.
استخراج النص مع التعليقات
بدلاً من تمريرين على المستند، يمكنك دمج نص التعليق مباشرةً في ناتج استخراج النص العادي.
// 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)إذا كنت تحتاج ذلك لصفحة واحدة فقط.
التحقق من دعم التعليقات أولاً
ليس كل تنسيق يدعم التعليقات، لذا من الجيد التحقق قبل بناء المنطق حول 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.
تصدير التعليقات إلى 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;
تصدير التعليقات إلى JSON
لأنابيب المعالجة التي تستهلك التعليقات برمجياً، عادةً ما يكون مصفوفة 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 }– سهل لأي خدمة لاحقة أن تقوم بفك تسلسله. Escapeيحافظ على صلاحية JSON دون الحاجة إلى مكتبة تسلسل.
Helper: Escape
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;
مقارنة الطرق: متى تستخدم كل منها
| الطريقة | الأفضل لـ | المزايا الرئيسية | القيود |
|---|---|---|---|
| استخراج المستند بالكامل | تحقق سريع من وجود أي تعليقات | نداء واحد، أبسط كود | لا توجد إشارة إلى الصفحة |
| استخراج حسب الصفحة | توجيه الملاحظات إلى القسم الصحيح | نتائج موسومة بالصفحة، جاهزة للتصدير | نداء إضافي لكل صفحة |
| نص مدمج + تعليقات | نص واحد قابل للقراءة | لا تحتاج إلى تمرير ثاني للمستند | التعليقات غير منفصلة عن نص الجسم |
| تصدير CSV | تتبع المراجعة باستخدام جداول البيانات | سهل الفتح في Excel، قابل للقراءة البشرية | بنية مسطحة فقط |
| تصدير JSON | أنابيب آلية، أنظمة التذاكر | هيكلية، قابلة للقراءة آليًا | حجم حمولة أكبر قليلًا |
ابدأ باستخراج المستند بالكامل لتأكيد أن الملف يحتوي على تعليقات تستحق المعالجة، ثم انتقل إلى استخراج حسب الصفحة عندما تحتاج إلى توجيه الملاحظات إلى قسم محدد.
أفضل الممارسات والنصائح
- تخلص من
Parserبسرعة: ضعها داخل كتلةusingلتحرير الموارد الأصلية. - تمييز
nullعن الفارغ: إرجاعnullمنGetAnnotationsيعني أن التنسيق غير مدعوم؛ مجموعة فارغة تعني عدم وجود تعليقات. - تحقق من
Features.Annotationsفي وظائف الدُفعات: تخطى الملفات غير المدعومة مبكرًا بدلاً من الاعتماد على فحصnullداخل الحلقة. - أعد استخدام القائمة الموسومة بالصفحة: أنشئها مرة واحدة بـ
ExtractAnnotationsByPageومرّرها لكل من مُصدري CSV و JSON لتجنب تباعد المخرجات. - الأمان: نص التعليق هو مدخل حر من المراجع – عالجه كأي سلسلة غير موثوقة قبل عرضها في واجهة أو تقرير.
الخلاصة
توفر GroupDocs.Parser طريقة مباشرة وبرمجية لسحب تعليقات المراجعين من PDF بدلاً من البحث عنها يدويًا. من خلال استخراج التعليقات للمستند بالكامل، أو وسمها بالصفحة، أو دمجها مع تدفق النص العادي، يمكنك بناء سير عمل مراجعة يُظهر الملاحظات فور وصول المستند إلى خط أنابيبك. صدّر النتائج إلى CSV أو JSON وربطها مباشرةً بالأدوات التي يستخدمها فريقك بالفعل.
الخطوات التالية:
- استكشف GetAnnotations API reference للحصول على توقيع الطريقة الكامل والتحميلات.
- تعلّم كيفية extract text from PDF documents جنبًا إلى جنب مع التعليقات لإنشاء خط محتوى كامل.
- اطلع على مشاريع عينات إضافية على GitHub لسيناريوهات المعالجة الدُفعية (Examples Repo).