💡 Повний робочий приклад доступний на 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 для .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 або може бути переданий у систему тикетів.
Допоміжна функція: 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 }, який легко десеріалізується будь‑яким downstream‑сервісом. Escapeзберігає коректність JSON без підключення додаткових бібліотек серіалізації.
Допоміжна функція: Escape
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;
Порівняння методів: коли який використовувати
| Метод | Краще підходить для | Ключові переваги | Обмеження |
|---|---|---|---|
| Витяг всього документа | Швидка перевірка «чи є коментарі взагалі?» | Один виклик, найпростіший код | Без атрибуції до сторінки |
| Витяг за сторінками | Маршрутизація зворотного зв’язку до потрібного розділу | Результати з позначенням сторінок, готові до експорту | Додатковий виклик на кожну сторінку |
| Текст + анотації разом | Єдиний читабельний транскрипт | Не потрібен другий прохід по документу | Коментарі не розділені від основного тексту |
| CSV‑експорт | Відстеження у електронних таблицях | Легко відкрити в Excel, зрозумілий людям | Плоска структура |
| JSON‑експорт | Автоматизовані конвеєри, системи тикетів | Структурований, машинно‑читабельний | Трохи більший обсяг даних |
Почніть з витягу всього документа, щоб переконатися, що файл містить коментарі, які варто обробляти, а потім переходьте до витягу за сторінками, коли потрібно прив’язати зворотний зв’язок до конкретного розділу.
Кращі практики та поради
- Швидко звільняйте
Parser: обгорніть його уusing, щоб звільнити нативні ресурси. - Розрізняйте
nullі порожню колекцію:GetAnnotations, що повертаєnull, означає, що формат не підтримується; порожня колекція – що в документі немає коментарів. - Перевіряйте
Features.Annotationsу пакетних процесах: пропускайте непідтримувані файли одразу, а не під час глибоких перевірок у циклі. - Повторно використовуйте список з позначенням сторінок: сформуйте його один раз за допомогою
ExtractAnnotationsByPageі передайте як у CSV, так і у JSON‑експортери, щоб два виходи не розійшлися. - Безпека: текст анотації – довільний ввід рецензента; обробляйте його як будь‑який інший недовірений рядок перед відображенням у UI або звіті.
Висновок
GroupDocs.Parser надає прямий, програмний спосіб витягнути коментарі рецензентів з PDF, замість їх ручного пошуку. Витягуючи анотації для всього документа, позначаючи їх сторінками або включаючи їх у звичайний текстовий потік, ви можете будувати робочі процеси ревізії, які одразу показують зворотний зв’язок, коли документ потрапляє у ваш конвеєр. Експортуйте результати у CSV або JSON і підключайте їх безпосередньо до інструментів, якими вже користується ваша команда.
Наступні кроки:
- Ознайомтеся з API‑довідником GetAnnotations для повного опису сигнатур і перегрузок.
- Дізнайтеся, як витягнути текст з PDF‑документів разом з анотаціями для повного контент‑конвеєра.
- Перегляньте додаткові приклади проектів на GitHub для сценаріїв пакетної обробки (Examples Repo).