Знаете, что объединяет разработчиков и алхимиков? И те, и другие пытаются превратить один материал в другой, рискуя взорвать лабораторию. Конвертация PDF в DOC — это та самая задача, где вместо золота часто получается что-то, напоминающее философский камень с ошибками форматирования. Но мы-то с вами не ищем лёгких путей! 😈
Сегодня я расскажу, как на C# можно скрестить ужа с ежом и получить работающий конвертер, который спасёт ваши нервы и, возможно, даже сделает пару рабочих задач. Будет код, немного магии и пара забавных граблей, на которые я сам наступил.
Почему вообще конвертировать PDF в DOC — это боль?
PDF создавался как «неизменяемый формат». Его задача — везде выглядеть одинаково, как фото отпечатка пальца. DOC же — это суп из стилей, секций, колонтитулов и прочих радостей. Попробуйте объяснить программе, что надпись «Важно!» должна быть не просто текстом, а красным жирным шрифтом с отступом слева.
В идеальном мире мы бы просто взяли PDF, нажали кнопку «магия» — и получили идеальный DOC. В реальном мире приходится либо платить деньги за супер-библиотеки, либо смириться с тем, что итоговый документ будет выглядеть так, будто его верстали в состоянии аффекта.
Но мы — программисты. Нам не привыкать.
Что нам понадобится для подвига
Я выбрал подход «бесплатно и сердито» (почти). Будем использовать:
- iText7 (Community версия) — читаем PDF. Осторожно: у iText есть свои лицензионные тонкости, но для личных и open‑source проектов AGPL подходит.
- DocumentFormat.OpenXml — творим DOCX на низком уровне. Microsoft, спасибо за этот подарок.
- .NET 8 (или 6+, не принципиально) — наш боевой станок.
Если вы хотите платное решение с красивой картинкой — берите Aspose или Spire, они делают всю работу за минуту. Но где тогда драйв? 😉
Этап 1: Читаем PDF и плачем
Первая проблема: PDF может содержать текст в виде набора команд «нарисовать букву здесь». Иногда текст есть, иногда — только картинки. Мы будем работать с текстовыми PDF, потому что OCR — это уже совсем другая история (и другая статья с большим количеством мата).
Устанавливаем NuGet пакеты:
dotnet add package iText7
dotnet add package DocumentFormat.OpenXml
Теперь напишем метод, который вытаскивает текст из PDF страницу за страницей:
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas.Parser;
public static string ExtractTextFromPdf(string pdfPath)
{
using var pdfReader = new PdfReader(pdfPath);
using var pdfDoc = new PdfDocument(pdfReader);
var text = new StringBuilder();
for (int page = 1; page <= pdfDoc.GetNumberOfPages(); page++)
{
var strategy = new SimpleTextExtractionStrategy();
var pageText = PdfTextExtractor.GetTextFromPage(pdfDoc.GetPage(page), strategy);
text.AppendLine(pageText);
text.AppendLine("--- PAGE BREAK ---");
}
return text.ToString();
}
Ура, мы получили сырой текст, но без форматирования. Шрифты, размеры, жирность — всё выброшено. Для простого конвертера этого хватит, но если хочется сохранить структуру — придётся лезть в LocationTextExtractionStrategy и выуживать координаты. Это уровень «джедай-отшельник», оставим на другой раз.
Создаём DOCX из текста
Здесь начинается магия Open XML. Мы создадим новый документ, добавим в него параграфы, разбитые по строкам из PDF.
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
public static void CreateDocxFromText(string text, string outputPath)
{
using var wordDoc = WordprocessingDocument.Create(outputPath, WordprocessingDocumentType.Document);
var mainPart = wordDoc.AddMainDocumentPart();
mainPart.Document = new Document();
var body = new Body();
var paragraphs = text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
foreach (var line in paragraphs)
{
if (line.StartsWith("--- PAGE BREAK ---"))
{
// Добавим разрыв страницы
var para = new Paragraph();
var run = new Run();
var br = new Break() { Type = BreakValues.Page };
run.Append(br);
para.Append(run);
body.Append(para);
}
else
{
var paragraph = new Paragraph();
var run = new Run();
run.Append(new Text(line));
paragraph.Append(run);
body.Append(paragraph);
}
}
mainPart.Document.Append(body);
mainPart.Document.Save();
}
Вуаля! Документ готов. Но если вы думаете, что он будет выглядеть как оригинал — увы, это просто текст, выложенный абзацами. Без колонтитулов, картинок, таблиц. Зато быстро и работает на любом сервере без установленного Word.
А как же таблицы, изображения и прочие радости?
Вот тут начинается квест на выживание. Чтобы конвертировать PDF с полным форматированием, нужно:
- Распарсить координаты каждого блока текста, его стиль (шрифт, размер, цвет).
- Определить границы таблиц, объединённых линиями или отступами.
- Извлечь изображения из PDF (iText это умеет, но нужно вытаскивать их и вставлять в DOCX как картинки).
- Сопоставить всё это с возможностями Open XML, что сравнимо с расшифровкой древнего манускрипта.
В коммерческих библиотеках эти муки уже переложены на плечи разработчиков, и вы просто пишете document.LoadPdf → document.SaveDocx. Но если вы хотите сделать свой велосипед — готовьтесь к ночам с отладчиком и множеству «а что, если…».
Готовый «минимально жизнеспособный» конвертер
Соберём всё вместе:
public static void ConvertPdfToDoc(string pdfPath, string docxPath)
{
var text = ExtractTextFromPdf(pdfPath);
CreateDocxFromText(text, docxPath);
Console.WriteLine("Конвертация завершена. Ждите аплодисментов (или багов).");
}
Вызовем:
csharp
ConvertPdfToDoc(@"C:\temp\document.pdf", @"C:\temp\result.docx");
Получим DOCX с текстом и разрывами страниц. Можно улучшать: добавить определение заголовков, попытаться сохранить жирность (если найдёте шрифт в PDF), поддержать списки.
Подводные камни, о которых я бы хотел знать раньше
- Кодировки. Некоторые PDF используют нестандартные кодировки, и текст превращается в кракозябры. iText7 умеет с этим бороться, но иногда приходится подбирать параметры.
- Сложные макеты. Если PDF сделан из скана или содержит слои — наш простой способ вытянет только текст, который лежит сверху.
- Производительность. Для больших PDF (сотни страниц) лучше использовать асинхронные методы и обрабатывать страницы по одной, иначе память может не выдержать.
- Лицензии. iText7 Community (AGPL) требует открытия кода, если вы распространяете приложение. Для коммерческого использования придётся купить лицензию или выбрать другую библиотеку (например, PdfPig — полностью Apache 2.0).
Альтернативы для тех, кто не хочет страдать
Если ваша задача — не гордость за самодельный велосипед, а результат, вот проверенные способы:
- Microsoft Office Interop — запускаем Word, он открывает PDF и сохраняет как DOC. Требует установленного MS Word и не подходит для серверных решений. И да, это медленно и ненадёжно.
- Aspose.Words — дорого, но божественно красиво. Поддерживает даже самые кривые PDF.
- Spire.Doc — аналог, есть бесплатная версия с ограничением на количество страниц.
- Онлайн-сервисы — для разовых конвертаций можно отправить файл на какой-нибудь API (но тогда данные улетают в облака).
Заключение: стоило ли оно того?
Создание конвертера PDF в DOC на C# — это отличный способ погрузиться в два сложных формата одновременно и получить много новых нервных клеток. Если у вас разовая задача и качество не критично — приведённый код спасёт ситуацию. Если вы пишете коммерческий продукт — лучше возьмите готовую библиотеку и сэкономьте время.
Но экспериментировать всё равно интересно. В процессе я, например, узнал, что PDF хранит текст в виде кучи команд «поставь букву 'A' по координатам (10,20)», а Open XML позволяет создавать документы без единой кнопки мыши.
Попробуйте сами, а потом расскажите в комментариях: что у вас получилось? Какие подводные камни встретили? Может, вы нашли способ конвертировать таблицы без потери форматирования? Делитесь — вместе веселее!
А если хотите больше магии C# и разбора реальных задач — подписывайтесь, впереди ещё много интересного. 🚀