PDFsharp позволяет из кода создавать PDF-документы, добавлять и переставлять страницы, рисовать текст и векторную графику, помещать изображения, объединять и разделять файлы, настраивать закладки, аннотации, шифрование и цифровые подписи. Основная работа строится вокруг объектов PdfDocument, PdfPage и XGraphics: приложение формирует структуру документа, получает графический контекст страницы, выводит содержимое и сохраняет результат в файл или поток.
Рабочее окно в привычном смысле здесь заменяет проект в среде разработки: команды задаются на C#, а результат проверяется в любом PDF-просмотрщике. Такой подход удобен для счетов, актов, сертификатов, этикеток, пакетных отчётов и серверной сборки документов, когда размеры, координаты, шрифты и правила обработки страниц должны воспроизводиться одинаково при каждом запуске.
Библиотека особенно полезна там, где требуется низкоуровневый контроль: можно точно указать геометрию страницы, порядок операторов рисования, способ встраивания шрифта, прозрачность изображения и режим открытия исходного файла. За автоматическую верстку длинных абзацев, перенос таблиц и расчёт разрывов отвечает связанный с проектом слой MigraDoc; в самом PDFsharp эти задачи приходится рассчитывать программно.
Скачать PDFsharp
- Редактирование PDF
- Русский интерфейс
- Просто новичкам
- Нет визуального редактора
- Не преобразует HTML
- Не рендерит страницы
Как устроен рабочий процесс PDFsharp
Типичный сценарий начинается с создания нового PdfDocument либо открытия существующего файла через PdfReader. Для нового документа добавляют PdfPage, задают размер и ориентацию, затем получают XGraphics. Именно XGraphics принимает команды рисования: линии, прямоугольники, пути, изображения и строки текста. Когда все страницы готовы, документ сохраняют в путь или поток. После сохранения объект следует считать завершённым: дополнительные изменения выполняют до вызова Save либо в новом проходе с повторным открытием файла.
При обработке существующего PDF режим открытия выбирают по цели. Modify нужен, когда страницы и словари будут изменяться; Import применяется, когда исходный документ служит донором страниц для другого файла; ReadOnly подходит для чтения структуры без записи. Такой выбор важен не только логически: он определяет, какие операции разрешены и как библиотека обращается с объектами исходного файла. Для объединения десятков документов обычно создают один целевой PdfDocument, открывают каждый входной файл в режиме импорта и добавляют нужные страницы по порядку.
В коде полезно разделять три уровня ответственности. Сервис данных готовит числа, даты, подписи и пути к изображениям. Модуль компоновки переводит их в координаты, размеры и набор страниц. Модуль вывода вызывает PDFsharp и не содержит бизнес-правил. Тогда изменение шаблона счёта не затрагивает расчёт налога, а смена набора данных не ломает геометрию документа. Это особенно заметно при тестировании: можно отдельно проверить вычисление координат и отдельно сравнить свойства готового PDF.

Выбор пакета для проекта
Для кроссплатформенной серверной логики используют пакет PDFsharp без привязки к Windows. Он подходит для приложений .NET, запускаемых в Linux, macOS, контейнере или облачной функции, но требует осознанной настройки шрифтов. Варианты PDFsharp-GDI и PDFsharp-WPF опираются соответственно на Windows Forms и WPF. Они уместны, когда код уже работает внутри Windows-приложения и должен совместно использовать типы или механизмы выбранного графического стека.
Пакеты нельзя выбирать только по совпадению пространства имён. Один и тот же вызов рисования может компилироваться в разных вариантах, однако загрузка шрифтов и растровых форматов зависит от сборки. Для библиотеки классов, которую затем подключают к нескольким приложениям, безопаснее заранее определить один графический вариант и закрепить его во всех проектах решения. Смешивание Core, GDI и WPF через транзитивные зависимости приводит к конфликтам сборок и неочевидным различиям при публикации.
Установка через менеджер пакетов должна быть частью воспроизводимой сборки: номер пакета фиксируют в файле проекта, а восстановление зависимостей проверяют на чистом агенте CI. Сам пакет не добавляет конструктор документов или панель инструментов. После подключения разработчик создаёт классы шаблона, определяет ресурсы шрифтов и изображений и пишет команды формирования PDF. Поэтому успешное восстановление NuGet-пакета — лишь начало настройки, а не готовый пользовательский процесс.
dotnet add package PDFsharp
// Пространства имён для базового сценария
using PdfSharp.Pdf;
using PdfSharp.Drawing;
Первый документ: от PdfDocument до Save
Минимальная программа создаёт PdfDocument, добавляет страницу и передаёт её в XGraphics.FromPdfPage. Шрифт задаётся объектом XFont, цвет — кистью XBrush, позиция — прямоугольником или парой координат. DrawString умеет привязать текст к точке и выбранному выравниванию. Для одиночного заголовка этого достаточно; для нескольких строк потребуется измерение текста, XTextFormatter или собственный алгоритм переноса.
Свойства document.Info заполняют заголовок, автора, тему и ключевые слова. Эти значения не отображаются на странице, но видны в свойствах PDF и полезны для архивирования и поиска. Их лучше задавать из данных документа, а не из имени приложения: например, номер счёта хранить в заголовке или теме, а название организации — в авторе. Время создания и изменения следует формировать единообразно, особенно если файлы собираются на серверах в разных часовых поясах.
Сохранение в MemoryStream удобно для веб-ответа, вложения в письмо или записи в объектное хранилище. Поток должен оставаться доступным столько, сколько его читает следующий компонент. Не следует без необходимости преобразовывать большой PDF в массив байтов несколько раз: это удваивает пиковое потребление памяти. Для файлового сценария проще сохранить непосредственно в FileStream, для HTTP-ответа — использовать потоковый результат или один готовый массив, если этого требует фреймворк.
var document = new PdfDocument();
document.Info.Title = "Счёт 1842";
var page = document.AddPage();
using var gfx = XGraphics.FromPdfPage(page);
var font = new XFont("Arial", 18, XFontStyleEx.Bold);
gfx.DrawString("Счёт № 1842", font, XBrushes.Black,
new XRect(40, 40, page.Width.Point - 80, 40),
XStringFormats.TopLeft);
document.Save("invoice.pdf");

Размер страницы, поля и система координат
PDF использует пункты: 72 пункта соответствуют одному дюйму. При подготовке шаблона удобнее хранить бизнес-размеры в миллиметрах и один раз переводить их в пункты. Координата X растёт вправо, Y — вниз для привычного режима XGraphics. Начало находится у верхнего левого края области страницы, поэтому верхнее поле добавляют к Y, а правую границу получают вычитанием правого поля из ширины. Ошибка в единицах часто проявляется как слишком крупный логотип или текст, ушедший за лист.
Размер можно взять из стандартного формата страницы или задать явно. Для этикеток, чеков и нестандартных бланков явные размеры надёжнее: они не зависят от локали и настроек принтера. Ориентацию задают до рисования. Если страницу поворачивают после расчёта макета, ширина и высота меняются местами, а координаты перестают соответствовать шаблону. В печатных формах полезно создать отдельный объект LayoutMetrics с шириной, высотой, полями и рабочей областью.
Границы рабочей области следует контролировать в коде. Перед выводом блока вычисляют его высоту и сравнивают нижнюю координату с page.Height минус нижнее поле. Если места нет, закрывают текущий XGraphics, добавляют следующую страницу, повторяют постоянные элементы и продолжают вывод. PDFsharp не вставляет разрыв автоматически, поэтому этот контроль должен выполняться для каждого растущего списка: строк таблицы, позиций заказа, комментариев и приложений.
Линии, фигуры и графические пути
XGraphics предоставляет операции для линий, ломаных, дуг, кривых Безье, прямоугольников, эллипсов, многоугольников и сложных путей. Контур задаётся XPen, заливка — XBrush. Толщину линий лучше определять в одном наборе стилей: тонкая сетка таблицы, обычная рамка и акцентный контур должны сохранять одинаковую визуальную иерархию во всех шаблонах. При масштабировании страницы толщина пера также требует проверки, иначе уменьшенный блок выглядит тяжелее исходного.
XGraphicsPath объединяет сегменты в одну фигуру и позволяет применять общую заливку или отсечение. Этот механизм подходит для нестандартных плашек, вырубных контуров этикеток, пиктограмм и масок. При построении пути важно явно закрывать контуры, которые должны заливаться. Незамкнутый путь может дать неожиданный край или зависеть от правила заполнения. Для повторяющихся символов путь создают один раз и рисуют с преобразованием координат.
Прозрачность удобна для водяных знаков и мягких подложек, но её нужно проверять в целевом просмотрщике и при печати. Полупрозрачная заливка поверх мелкого текста снижает читаемость, а несколько прозрачных слоёв увеличивают сложность содержимого страницы. Для обычной формы предпочтительны непрозрачные цвета и минимальное число графических состояний. Прозрачность оставляют там, где она действительно несёт смысл: отметка статуса, фон диаграммы или ненавязчивый штамп.

Преобразования, поворот и отсечение
Графический контекст умеет перемещать, масштабировать и поворачивать систему координат. Это позволяет описать значок или блок один раз, а затем разместить его в разных точках. Перед локальным преобразованием сохраняют состояние XGraphics, после вывода восстанавливают его. Иначе поворот заголовка влияет на все последующие команды, и ошибка обнаруживается далеко от места, где была допущена.
Поворот вокруг центра требует последовательности: перенести начало координат в центр объекта, выполнить RotateTransform, нарисовать объект относительно нового начала и восстановить состояние. Поворот вокруг начала страницы технически проще, но заставляет вручную вычислять смещение. Для вертикальных подписей в узких ячейках полезно вынести этот расчёт в функцию, принимающую прямоугольник и угол.
Область отсечения ограничивает вывод заданным прямоугольником или путём. Она нужна для миниатюр, диаграмм и изображений, которые не должны выходить за рамку. Отсечение не уменьшает исходный объект и не меняет его пропорции; оно лишь скрывает часть результата. Поэтому для фотографии сначала рассчитывают масштаб и положение, затем задают clip и рисуют изображение. После операции графическое состояние обязательно восстанавливают.
Цвета, кисти и прозрачность
Цвет можно задать готовой константой либо компонентами RGB, CMYK или оттенком серого. Выбор цветовой модели должен соответствовать назначению файла. Для экранных отчётов обычно достаточно RGB, для печатного потока типография может потребовать CMYK и дополнительную проверку профилей. PDFsharp записывает цветовые значения, но не заменяет профессиональную допечатную подготовку: преобразование профилей, контроль суммарной краски и создание PDF/X выполняют специализированными средствами.
Сплошные кисти подходят для большинства документов. Градиенты используются в декоративных блоках и диаграммах, однако усложняют визуальное сравнение и могут отвлекать от данных. В деловой форме лучше ограничиться несколькими цветами: основным текстом, вторичным текстом, линиями и одним акцентом. Цветовые значения выносят в тему шаблона, чтобы не искать десятки литералов при изменении фирменного стиля.
Альфа-канал применяется как к кистям, так и к некоторым изображениям. При наложении логотипа с прозрачным фоном нужно убедиться, что исходный PNG действительно содержит корректную маску, а не белый прямоугольник. Если после вставки появляется тёмный ореол, изображение часто было экспортировано с полупрозрачными пикселями под другой цвет фона. Исправлять это лучше в исходном изображении, а не пытаться скрыть дополнительной плашкой в PDF.
Вывод текста и измерение строк
DrawString выводит одну строку в заданной позиции или прямоугольнике. Он не превращает произвольный абзац в готовую колонку с переносами и разрывами страниц. Перед выводом динамического текста вызывают MeasureString, сравнивают ширину с доступной областью и выбирают шрифт, сокращение либо перенос. Для чисел и дат важно измерять реальные значения, а не пример из макета: длинный номер договора или отрицательная сумма могут выйти за пределы колонки.
XStringFormat управляет горизонтальным и вертикальным выравниванием. В таблицах текстовые поля обычно выравнивают влево, денежные значения — вправо, короткие коды — по центру. Вертикальное выравнивание по базовой линии и по центру даёт разный результат; при смешении шрифтов это особенно заметно. Для повторяемых ячеек стоит иметь одну функцию DrawCellText, которая учитывает внутренние отступы, обрезку и выбранное выравнивание.
XTextFormatter упрощает перенос текста внутри прямоугольника, но остаётся базовым инструментом. Он не создаёт полноценную типографскую верстку с автоматическим продолжением на следующей странице, вдовами, сиротами и сложным обтеканием. Для коротких примечаний и адресов его возможностей достаточно. Для многостраничного договора лучше использовать MigraDoc либо собственный компоновщик, который возвращает высоту использованного фрагмента и остаток текста.

Шрифты и FontResolver
При создании XFont библиотека должна сопоставить семейство и начертание с реальным файлом шрифта. В Core-варианте нет универсального способа найти системные шрифты на всех платформах, поэтому производственное приложение обычно реализует IFontResolver. Метод ResolveTypeface выбирает лицо для сочетания семейства, жирности и курсива, а GetFont возвращает байты файла. Такой подход делает результат независимым от набора шрифтов на конкретном сервере.
Resolver настраивают при запуске процесса до создания первого XFont. PDFsharp кэширует сопоставления и байты, поэтому поздняя замена запрещена. В тестах существует сброс управления шрифтами, но после него ранее созданные XFont использовать нельзя. Практическое правило простое: зарегистрировать основной и резервный resolver в точке старта, затем не менять глобальную настройку в обработчиках запросов.
В контейнере шрифты лучше поставлять вместе с приложением либо отдельным управляемым ресурсом. При этом необходимо проверить лицензию каждого файла: право использовать шрифт на рабочей станции не всегда означает право встраивать его в распространяемый PDF. Внутренние имена семейства и лица могут отличаться от имени файла, поэтому resolver должен возвращать стабильные собственные идентификаторы, а не строить путь из пользовательской строки.
Если кириллица заменяется квадратами, причина обычно не в кодировке исходной строки, а в выбранном лице без нужных глифов или в неправильном сопоставлении. Проверяют наличие символов в шрифте, фактические байты, стиль и резервное семейство. Для смешанного текста с редкими символами может потребоваться разбиение строки на участки с разными шрифтами; автоматического универсального fallback для всех письменностей ожидать не следует.
Встраивание и подмножества шрифтов
Встроенный шрифт обеспечивает одинаковый вид документа на компьютере без установленного семейства. Подмножество содержит только использованные глифы и уменьшает размер файла, что особенно выгодно для отчётов с крупными гарнитурами. Полное встраивание может понадобиться, когда документ позднее редактируется другим инструментом или политика архива требует полного ресурса. Решение принимают вместе с лицензией шрифта и требованиями к файлу.
При формировании многих документов один и тот же шрифт не стоит повторно читать с диска на каждый текстовый блок. Resolver кэширует байты, но приложение также должно избегать создания тысяч эквивалентных XFont в тесном цикле. Удобно хранить набор шрифтов шаблона — заголовок, основной текст, подпись, моноширинный код — и использовать их повторно в рамках одного задания.
Размер PDF зависит не только от количества шрифтов, но и от числа начертаний. Regular, Bold, Italic и BoldItalic обычно являются отдельными файлами. Искусственное наклонение поддерживается ограниченно и не заменяет настоящее лицо во всех случаях. Для официального документа лучше поставлять нужные начертания явно, проверять ширины строк и сравнивать результат после обновления шрифтовых ресурсов.
Изображения из файла и потока
XImage загружает изображение из файла или потока, после чего DrawImage размещает его на странице. Путь к файлу удобен в пакетном задании, поток — когда изображение пришло из базы, HTTP-запроса или встроенного ресурса. Поток должен поддерживать ожидаемый библиотекой режим чтения и оставаться действительным до завершения использования XImage. Для MemoryStream с доступным буфером можно применять соответствующий конструктор, сокращая лишнее копирование.
Core-вариант непосредственно обрабатывает основные форматы JPEG, PNG и BMP. Windows-варианты могут опираться на системные графические механизмы и принимать дополнительные форматы. Чтобы поведение не менялось между разработкой и сервером, входные изображения разумно заранее нормализовать в JPEG или PNG. TIFF с несколькими страницами, сложный GIF или файл с необычной палитрой лучше преобразовать контролируемым этапом до передачи в PDFsharp.
JPEG подходит для фотографий, PNG — для схем, логотипов и прозрачности. Повторное сохранение JPEG ухудшает качество, поэтому исходный файл не следует декодировать и кодировать без необходимости. Огромная фотография, уменьшенная до нескольких сантиметров, всё равно может существенно увеличить PDF. Перед вставкой её масштабируют до разумного разрешения, сохраняя запас для печати, и удаляют метаданные, которые не нужны документу.

Масштабирование, кадрирование и качество изображений
DrawImage принимает прямоугольник назначения. Если его пропорции отличаются от пропорций исходника, изображение растягивается. Для режима contain масштаб выбирают по меньшему коэффициенту и добавляют свободные поля; для cover — по большему коэффициенту и отсекают края. Эти два расчёта стоит оформить отдельными функциями, потому что ручное повторение формул быстро приводит к разным результатам в карточках и таблицах.
PDF хранит размер на странице в пунктах, а растровый файл — в пикселях. Эффективное разрешение получают делением пикселей на физический размер. Для экрана достаточно меньшего значения, для качественной печати требуется больше. PDFsharp не оценивает, будет ли изображение размытым, поэтому предупреждение о низком разрешении реализует само приложение. В пакетной обработке полезно записывать в журнал размеры изображения и рассчитанное эффективное DPI.
Кадрирование выполняют через область отсечения и смещение изображения либо предварительно обрабатывают bitmap. Первый вариант сохраняет один исходный ресурс и удобен для разных рамок, но скрытые части остаются в содержимом. Второй уменьшает объём и исключает лишние области, однако создаёт новый растровый файл. Для персональных фотографий и сканов с чувствительными краями безопаснее физически обрезать данные до добавления в PDF.

Изображения в памяти и повторное использование
Когда один логотип встречается на каждой странице, его загружают один раз на документ и повторно рисуют. Постоянное создание XImage из одинакового массива увеличивает нагрузку и затрудняет освобождение ресурсов. Объекты изображений следует корректно освобождать после завершения задания, особенно в сервисе, который обрабатывает много документов подряд.
Массив байтов удобно получать из хранилища один раз и передавать через MemoryStream. Если поток создаётся внутри фабрики XImage и немедленно закрывается, необходимо убедиться, что библиотека уже прочитала нужные данные; безопаснее сохранять поток вместе с изображением до конца вывода. Ошибки вида parameter is not valid часто связаны не с PDF, а с пустым, обрезанным или уже закрытым потоком.
Входной файл проверяют до декодирования: ограничивают размер, убеждаются в ожидаемой сигнатуре и не доверяют расширению. Это защищает сервер от случайной загрузки огромного файла и от содержимого, которое декодер не способен обработать. PDFsharp не заменяет слой валидации загрузок. Для недоверенных изображений полезно выполнить предварительное декодирование в изолированном компоненте и передать библиотеке уже нормализованный PNG или JPEG.
Встраивание изображений в строку
Высокоуровневый MigraDoc умеет помещать изображение внутрь абзаца, использовать его как маркер и выравнивать относительно текста. На уровне PDFsharp аналогичный результат строят вручную: измеряют участок текста, рассчитывают прямоугольник картинки и продолжают строку после него. Для короткой подписи это несложно, но при переносах и разных высотах шрифтов требуется полноценная модель строки.
При смешивании текста и пиктограмм базовая линия важнее геометрического центра. Значок, выровненный по центру прямоугольника шрифта, часто выглядит ниже букв. Практический метод — хранить для каждого значка визуальную поправку по Y и проверять её рядом с реальными символами. Для доступности значимую пиктограмму нельзя оставлять единственным носителем смысла: рядом выводят текстовое обозначение.
Если документ содержит десятки мелких иконок, выгодно использовать векторные пути либо один ресурс, а не множество почти одинаковых PNG. Вектор сохраняет резкость при увеличении и обычно занимает меньше места. Однако преобразование сложного SVG напрямую не является базовой задачей PDFsharp; его следует заранее перевести в поддерживаемые примитивы, растр или совместимый формат другим компонентом.

Объединение и разделение PDF
Для слияния создают целевой PdfDocument, открывают каждый входной файл в режиме Import и последовательно добавляют страницы. Порядок входных файлов и диапазоны лучше задавать отдельной моделью, чтобы один алгоритм поддерживал полное объединение, выбор страниц и чередование приложений. Перед добавлением проверяют число страниц и корректность диапазона; отрицательный индекс или страница за пределами файла должны давать понятную ошибку до начала записи результата.
Разделение выполняется тем же механизмом в обратную сторону: для каждого диапазона создают новый документ и импортируют выбранные страницы. Имена файлов формируют безопасно, не используя напрямую заголовки из PDF. Если нужно получить отдельный файл на каждую страницу, заранее оценивают количество результатов и свободное место. Сотни маленьких документов могут создать больше операционных накладных расходов, чем один исходник.
При импорте страница переносится как PDF-объект, поэтому качество исходной графики не ухудшается. Это отличается от растрирования: текст остаётся текстом, вектор — вектором. Однако логическая структура, формы, закладки и связи между страницами требуют отдельной проверки. Простое добавление страниц не гарантирует автоматического переноса оглавления целевого документа или исправления ссылок, указывающих на старые номера.
using var output = new PdfDocument();
foreach (var path in inputFiles)
{
using var input = PdfReader.Open(path, PdfDocumentOpenMode.Import);
for (var i = 0; i < input.PageCount; i++)
output.AddPage(input.Pages[i]);
}
output.Save("combined.pdf");
Импорт страницы как формы и монтаж нескольких страниц
XPdfForm позволяет использовать страницу существующего PDF как графический объект и рисовать её внутри прямоугольника. Этот приём нужен для размещения двух или четырёх исходных страниц на одном листе, создания превью, подложки бланка и повторного использования шаблона. Размер формы берут из исходной страницы, после чего рассчитывают масштаб с сохранением пропорций.
При монтаже n-up учитывают не только размер бумаги, но и поворот исходных страниц. Альбомная страница может лучше поместиться после поворота на 90 градусов. Алгоритм должен сравнить два варианта и выбрать больший полезный масштаб, если порядок чтения это допускает. Между ячейками оставляют зазор, а внешние поля не делают меньше допустимой области печати.
Использование страницы как формы не превращает её содержимое в редактируемые элементы шаблона. PDFsharp рисует готовый ресурс, поверх которого можно добавить новую графику. Это удобно для штампа или номера, но не для замены отдельных слов в исходном абзаце. Для такой замены нужен доступ к операторам содержимого и шрифтам, а практичнее часто заново сформировать страницу из структурированных данных.
Водяные знаки, штампы и слои вывода
Водяной знак добавляют через XGraphics для существующей страницы. Режим Prepend помещает новый контент под исходным, Append — поверх. Подложка не перекрывает текст, но может быть незаметна под непрозрачными объектами; верхний слой гарантированно виден, зато способен ухудшить читаемость. Выбор должен быть частью настройки штампа, а не жёстко заданным решением.
Для диагональной надписи сохраняют состояние графики, переносят начало в центр страницы, поворачивают контекст и выводят строку с полупрозрачной кистью. Размер шрифта рассчитывают по ширине страницы, чтобы длинный статус не обрезался. Затем состояние восстанавливают. Если документ содержит страницы разных размеров, расчёт выполняют для каждой страницы отдельно.
Штамп с номером, датой и оператором лучше строить как компактный блок с непрозрачным фоном и тонкой рамкой. Прозрачная надпись поверх сложного скана плохо читается. Координаты выбирают с учётом CropBox и Rotation исходной страницы, иначе штамп оказывается в неожиданном углу. После обработки проверяют несколько страниц разных типов, а не только первую.
Нумерация страниц и повторяющиеся элементы
Когда документ создаётся с нуля и число страниц известно только после компоновки, нумерацию удобно выполнять вторым проходом. Сначала формируют все страницы, затем обходят коллекцию и добавляют страница X из Y. Так общее количество доступно без предварительной оценки. Важно выбрать один режим наложения и одинаковые поля для страниц разных размеров.
Колонтитул, логотип и реквизиты можно рисовать общей функцией при создании каждой страницы. Функция принимает PdfPage и данные документа, создаёт XGraphics, выводит постоянные элементы и освобождает контекст. Для второго прохода по существующим страницам она должна открывать графику в режиме добавления, чтобы не заменить исходное содержимое.
Если номера входят в подписываемую область, их добавляют до цифровой подписи. Любое изменение байтов после подписи может сделать её недействительной или показать документ изменённым. Аналогично шифрование и финальные метаданные настраивают до сохранения окончательного экземпляра. Последовательность операций фиксируют в одном конвейере, а не распределяют между независимыми обработчиками.
Закладки, внутренние переходы и ссылки
Коллекция Outlines формирует дерево закладок, которое PDF-просмотрщик показывает в боковой панели. Каждая запись указывает страницу и может иметь дочерние элементы. Для отчёта закладки создают из той же структуры разделов, что и заголовки, иначе навигация расходится с содержимым. Названия делают короткими и уникальными, а уровни не углубляют без необходимости.
Ссылки внутри документа полезны для оглавления, возврата к приложению и перехода от сводной строки к подробной странице. Координаты активной области должны совпадать с визуальным текстом; слишком маленькая область неудобна, слишком большая перекрывает соседние элементы. После генерации ссылки проверяют в нескольких просмотрщиках, потому что подсветка и поведение открытия могут различаться.
Внешние и файловые ссылки являются частью аннотаций PDF. Их следует добавлять только из доверенных данных и явно различать типы назначения. Путь к локальному файлу, работающий на компьютере автора, почти наверняка не существует у получателя. Для архивных и защищённых документов внешние действия могут быть запрещены политикой, поэтому навигацию лучше строить на внутренних закладках.

Метаданные, параметры просмотра и структура документа
document.Info хранит заголовок, автора, тему, ключевые слова и связанные даты. Эти поля заполняют до сохранения и проверяют отдельно от видимого текста. Значения не должны содержать секреты, служебные пути или внутренние идентификаторы, которые нельзя передавать получателю. При создании файла из шаблона метаданные шаблона не следует бездумно сохранять в результате.
Параметры просмотра могут подсказать, показывать ли закладки, миниатюры или страницу целиком при открытии. Это рекомендация, а не гарантия: просмотрщик вправе применить настройки пользователя. Поэтому критически важную инструкцию нельзя прятать только в режиме открытия. Документ должен оставаться понятным при обычном просмотре с первой страницы.
Каталог страниц, ресурсы, шрифты и изображения образуют связанную объектную структуру. При ручной работе с низкоуровневыми словарями легко создать ссылку на объект другого документа или забыть зарегистрировать ресурс. Пока задача решается публичным API страниц, XGraphics и аннотаций, лучше не спускаться к словарям. Низкий уровень используют после изучения спецификации PDF и обязательно проверяют результат валидатором.
Поля форм и аннотации
PDFsharp позволяет обращаться к объектам PDF и отдельным видам аннотаций, но не заменяет визуальный конструктор форм. Существующие AcroForm-поля могут требовать низкоуровневой обработки, а внешний вид значения зависит от appearance stream. Простая запись свойства не всегда приводит к одинаковому отображению во всех просмотрщиках. После заполнения документ открывают в целевых программах и проверяют печать.
Если получателю не нужна интерактивность, форму часто безопаснее превратить в обычное содержимое: нарисовать значения поверх бланка и удалить либо отключить поля специализированным процессом. PDFsharp не следует считать универсальным движком flatten для любых сложных форм. Особенно осторожно работают с вычисляемыми полями, JavaScript, подписями и XFA.
Аннотации включают ссылки, заметки и другие интерактивные элементы. Координаты аннотации задаются в системе страницы и должны учитывать её поворот. При копировании страниц между документами проверяют, сохранились ли действия и корректны ли назначения. Недоверенные действия, вложения и скрипты разумно анализировать до передачи файла дальше.
Пароли, шифрование и разрешения
Документ можно защищать пользовательским и владельческим паролем. Пользовательский пароль требуется для открытия, владельческий даёт полный доступ к настройкам и изменениям. Если задан только набор запретов без надёжного владельческого пароля, защита не обеспечивает серьёзного контроля. Пароли не следует хранить в исходном коде или журнале; их получают из защищённой конфигурации либо вводят в момент задания.
PDFsharp поддерживает современные варианты AES, включая 128-битный режим и AES-256 для PDF 2.0. Более старые RC4-режимы сохраняются ради совместимости, но для новых файлов их выбирать не следует. Конкретный алгоритм согласуют с программами получателей: очень старый просмотрщик может не открыть современно зашифрованный файл. Совместимость проверяют на реальном наборе клиентов, а не только в одном браузере.
Разрешения печати, копирования и изменения являются частью шифрования и политики просмотра. Они не равны DRM и не могут предотвратить фотографирование экрана или обработку программой, игнорирующей ограничения. Эти флаги подходят для обозначения намерения и обычного пользовательского контроля, но не заменяют управление доступом к самому файлу. Конфиденциальные документы передают по защищённому каналу и ограничивают срок хранения.
Открытие зашифрованного файла требует корректного пароля. Для полного изменения может понадобиться владельческий пароль. Ошибку пароля нужно отличать от повреждённого файла: первое позволяет запросить другой секрет, второе требует диагностики структуры. В пакетном задании не следует многократно угадывать пароль; число попыток ограничивают, а секреты очищают из памяти настолько, насколько позволяет окружение.
Цифровая подпись
Цифровая подпись связывает содержимое PDF с сертификатом и позволяет обнаружить изменения после подписания. PDFsharp формирует контейнер CMS и поддерживает подключение реализации подписывающего компонента через интерфейс. Сертификат может находиться в файле, системном хранилище, HSM или удалённой службе; конкретная интеграция отвечает за закрытый ключ и политику доступа.
Для подписи в PDF заранее резервируется место, затем вычисляется хэш и записывается результат. Размер подписи зависит от цепочки сертификатов и отметки времени, поэтому слишком маленький резерв приводит к ошибке финализации. Внешний подписывающий сервис должен возвращать данные в ожидаемом формате и не изменять документ параллельно.
Видимая подпись и криптографическая подпись — разные сущности. Картинка, имя и дата на странице помогают человеку, но сами по себе ничего не доказывают. Криптографическая подпись может быть невидимой. Если нужен видимый блок, его координаты, текст и изображение добавляют согласованно с полем подписи и проверяют, что они не закрывают содержание.
Отметка времени подтверждает существование подписи в определённый момент и полезна для долгосрочной проверки. Её получение зависит от внешнего сервиса и сети, поэтому конвейер должен обрабатывать тайм-аут, недоступность и сертификат ответа. Подписание выполняют последним значимым этапом: последующее наложение номера, исправление метаданных или повторное сохранение может изменить статус подписи.
PDF/A для архивного хранения
Режим PDF/A вводит ограничения, необходимые для долгосрочного воспроизведения. Шрифты должны быть встроены, шифрование запрещено, цвета и метаданные требуют согласованной настройки. PDFsharp умеет пометить создаваемый документ выбранным уровнем соответствия, но разработчик отвечает за содержимое и ресурсы. Один вызов настройки не превращает произвольный импортированный PDF в гарантированно соответствующий архивный файл.
Если в архивный документ импортируются страницы или XObject из внешнего PDF, их шрифты, цветовые пространства и метаданные могут нарушить требования. Для надёжного процесса входные файлы предварительно проверяют, а итог прогоняют через независимый валидатор. Ошибка должна останавливать публикацию, а не только попадать в журнал.
PDF/A несовместим с шифрованием, поэтому нельзя одновременно требовать архивную конформность и пароль на открытие. Защиту архива организуют на уровне хранилища, прав доступа и канала передачи. Также следует определить точный профиль PDF/A, который принимает архив или регулятор; более новый профиль не автоматически подходит системе, настроенной на другой уровень.
PDF/UA и доступность
PDF/UA требует логической структуры, порядка чтения, обозначения заголовков, альтернативного текста и отделения значимого содержимого от декоративного. PDFsharp предоставляет средства для добавления необходимых структурных указаний, но доступность нельзя получить только визуально правильным рисунком страницы. Каждый фрагмент должен иметь роль и место в дереве структуры.
Пробелы, переносы и порядок текстовых объектов важны для экранного диктора. Если слова нарисованы отдельными командами без корректных разделителей, визуально строка выглядит нормально, а извлечённый текст склеивается. Поэтому доступный шаблон проектируют с учётом логического текста, а не добавляют теги после завершения графики.
Изображениям назначают альтернативное описание, декоративные элементы исключают из чтения, таблицам задают заголовочные ячейки и связи. Затем файл проверяют валидатором и реальным экранным диктором. Автоматическая проверка обнаруживает структуру, но не оценивает осмысленность текста или удобство навигации.
Таблицы: ручная сетка и MigraDoc
В самом PDFsharp таблица строится как набор прямоугольников, линий и текстовых блоков. Разработчик рассчитывает ширины колонок, высоту каждой строки, перенос текста и переход на новую страницу. Такой подход оправдан для строгой формы с фиксированными колонками и ограниченным содержимым. Для переменных описаний и многостраничных реестров объём расчётов быстро растёт.
Алгоритм ручной таблицы сначала измеряет все ячейки строки, выбирает максимальную требуемую высоту, проверяет остаток страницы и только затем рисует фон, границы и текст. Нельзя вывести половину строки, обнаружить нехватку места и просто продолжить на следующем листе: потребуется очистка или повторная сборка. Заголовок таблицы повторяют после каждого разрыва, а номер страницы добавляют отдельным проходом.
MigraDoc предлагает объектную модель колонок, строк и ячеек, умеет переносить таблицу, повторять строки заголовка и применять стили. Он использует PDFsharp для окончательного PDF-вывода. Выбор между ними определяется не сложностью установки, а моделью макета: абсолютные координаты и штампы удобнее рисовать напрямую, потоковый отчёт с абзацами и таблицами — описывать через MigraDoc.

Повтор заголовка таблицы и длинные реестры
Для ручного вывода повторяемый заголовок оформляют отдельной функцией, возвращающей новую координату Y. При создании страницы сначала рисуют постоянный колонтитул, затем заголовок таблицы, после чего строки. Если высота одной строки превышает всю рабочую область, алгоритм должен либо разбить содержимое по строкам текста, либо сообщить о невозможности размещения. Бесконечное добавление страниц является типичной ошибкой такого компоновщика.
В MigraDoc строку помечают как заголовочную, и при разрыве она повторяется автоматически. Но высота строк и переносы всё равно зависят от шрифтов, поэтому тесты должны использовать те же файлы шрифтов, что и рабочая среда. Замена гарнитуры способна сдвинуть разрыв на страницу и изменить итоговое число листов.
Большие реестры формируют потоково на уровне данных, но DOM документа всё равно может занимать память до рендеринга. Если объём измеряется десятками тысяч строк, полезно разбивать результат на логические тома, избегать тяжёлых изображений и измерять память на реальном наборе. Один огромный PDF не всегда удобнее нескольких подписанных частей с общим индексом.

Поля, даты и номера страниц в MigraDoc
Поля MigraDoc вставляют номер страницы, число страниц раздела или документа, дату и отдельные свойства документа. Они вычисляются при рендеринге, поэтому подходят для колонтитулов и автоматического оглавления. Формат даты задаётся строкой формата .NET, а не текстом, вычисленным заранее. Это сохраняет единообразие локали, но локаль рендера должна быть явно определена для воспроизводимого результата.
Поля не следует путать с интерактивными AcroForm-полями. В MigraDoc это элементы содержимого, которые превращаются в обычный текст PDF. Пользователь не редактирует их в просмотрщике. Для номера страницы это преимущество: значение всегда печатается и не зависит от поддержки форм.
Если нужен формат страница X из Y, оба поля помещают в один абзац колонтитула и задают выравнивание стилем. В документе с несколькими разделами заранее решают, должна ли нумерация продолжаться или начинаться заново. Смена раздела влияет на заголовки, поля и формат страницы, поэтому её нельзя использовать лишь как случайный контейнер для очередного блока.

Диаграммы и графика данных
PDFsharp позволяет нарисовать диаграмму самостоятельно из линий, фигур и текста. Это даёт полный контроль и подходит для специализированной визуализации, но требует расчёта шкал, подписей, легенды и пересечений. MigraDoc содержит базовые объекты диаграмм, которые проще встроить в потоковый отчёт. Их возможности следует сравнить с требованиями до выбора: сложная интерактивная или научная графика обычно создаётся отдельной библиотекой и вставляется как вектор или растр.
Числовые значения проверяют до построения. Пустой набор, отрицательные значения для круговой диаграммы, бесконечность и NaN должны обрабатываться явно. Цвета выбирают не только по эстетике, но и по различимости в серой печати и для пользователей с нарушением цветового восприятия. Значимые категории подписывают текстом или узором, а не передают смысл одним оттенком.
При вставке готового графика в виде PNG контролируют разрешение, при вставке векторного ресурса — совместимость и шрифты. Слишком подробная диаграмма на маленькой области остаётся нечитаемой независимо от формата. Макет должен выделять достаточное место и, при необходимости, переносить легенду или таблицу значений на отдельную страницу.

Создание счетов и актов
Шаблон счёта удобно разделить на шапку, реквизиты сторон, таблицу позиций, итоги и подписи. Шапка и подписи имеют фиксированные координаты, таблица растёт. Перед выводом каждой позиции измеряют описание и вычисляют высоту строки. Итоговый блок нельзя отделять от последних строк так, чтобы он остался один на следующей странице; заранее резервируют его высоту либо переносят несколько строк вместе с итогом.
Денежные значения форматируют в одном месте с явно заданной культурой и количеством знаков. В PDF выводят уже готовую строку, а не полагаются на формат по умолчанию сервера. Для отрицательных сумм, скидок и нулей определяют правила заранее. Десятичные разделители и пробелы-разряды влияют на ширину, поэтому измерение выполняют после форматирования.
Логотип и печать не должны быть единственными признаками подлинности. При необходимости добавляют цифровую подпись или проверяемый идентификатор документа. QR-код генерирует отдельный компонент; PDFsharp размещает готовое изображение или набор векторных модулей. Полезную нагрузку кода ограничивают и тестируют считывание с печати, а не только с монитора.
Сертификаты, билеты и этикетки
Для сертификата важна точная привязка имени, номера и даты к фону. Бланк можно импортировать как XPdfForm, а переменные поля нарисовать поверх. Длинное имя измеряют и уменьшают шрифт в допустимых пределах либо переносят по заданному правилу. Автоматическое бесконечное уменьшение недопустимо: текст станет нечитаемым, поэтому после минимального размера задание должно перейти в ручную проверку.
Билет часто содержит штрихкод или QR-код, отрывную часть и метки реза. Размер модулей кода должен быть кратен или близок к целому числу пикселей при растрировании, а вокруг оставляют тихую зону. Для печати нескольких билетов на листе страницу билета используют как форму и размещают по сетке. Порядок нумерации определяют с учётом последующей резки.
Этикетка имеет нестандартный физический размер и малые поля. Размер страницы задают явно, не полагаясь на A4 и масштабирование драйвера. Все элементы проверяют при печати со значением 100 процентов; режим вписать меняет геометрию. Если принтер требует метки или зазоры, они относятся к настройкам носителя, а не к содержимому PDF.
Добавление служебного штампа к входящим документам
Пакетная регистрация входящих файлов обычно открывает каждый PDF в режиме изменения, добавляет на первую страницу номер, дату и подразделение и сохраняет новый экземпляр. Исходник не перезаписывают до успешной проверки результата. Имя временного файла создают в том же файловом томе, затем выполняют атомарную замену или перемещение.
Перед штампованием определяют свободную область. Универсальная фиксированная координата может закрыть подпись или штрихкод. Более надёжный процесс использует утверждённую зону для документов известного шаблона, а неизвестные файлы отправляет на контроль. Автоматический поиск пустого места по растровому изображению не входит в задачи PDFsharp.
Поворот страницы, CropBox и MediaBox проверяют до расчёта координат. Визуально вертикальная страница может иметь альбомный MediaBox и Rotation. Если игнорировать это, штамп будет повернут или окажется за видимой областью. Набор тестов должен содержать обычную страницу, повёрнутую страницу, скан и документ с нестандартным CropBox.
Сборка персональных пакетов документов
Для каждого получателя можно объединить титульный лист, персональный отчёт и общие приложения. Общие страницы открывают один раз в контролируемом процессе либо повторно в режиме импорта для каждого результата, учитывая время жизни документов. Персональные данные не должны попадать в общий кэш или имя временного файла. После создания пакет проверяют на число страниц и наличие ожидаемого идентификатора.
Закладки формируют по разделам пакета, а нумерацию добавляют после объединения. Если приложения уже имеют номера страниц, нужно решить, сохранять ли их или накладывать сквозную нумерацию в отдельной зоне. Дублирующиеся номера без пояснения вводят читателя в заблуждение.
При массовой отправке каждый файл шифруют уникальным паролем только при наличии безопасного канала передачи секрета. Один общий пароль на всю рассылку почти не защищает данные. Альтернативой служит защищённый портал с контролем доступа; PDFsharp отвечает за документ, а не за аутентификацию получателя.
Работа в ASP.NET и фоновых заданиях
В веб-приложении генерацию отделяют от HTTP-контроллера. Контроллер проверяет запрос и ставит задание, сервис формирует PDF, а хранилище возвращает поток или ссылку с ограниченным сроком. Для небольшого файла допустима синхронная выдача, но время компоновки и размер должны иметь пределы. Отмена запроса должна прекращать дорогое задание там, где это возможно.
Глобальный FontResolver настраивают один раз при старте процесса. Нельзя назначать его заново для каждого пользователя или документа. Если клиенты используют разные корпоративные шрифты, единый resolver может выбирать ресурсы по переданному имени из заранее разрешённого набора, но не должен читать произвольный путь из запроса.
PdfDocument, XGraphics и связанные изменяемые объекты не следует совместно использовать между параллельными заданиями. Каждый документ строят в собственном контексте, а общими делают только неизменяемые данные и безопасные кэши ресурсов. Ограничение параллелизма защищает сервер от пиков памяти, особенно когда документы содержат крупные изображения.
Ответу задают корректный MIME-тип и безопасное имя. Пользовательское имя очищают от управляющих символов и разделителей пути. Если файл хранится временно, срок удаления и поведение при повторной загрузке определяют явно. Журнал не должен содержать содержимое документа или пароль.
Контейнеры Linux и воспроизводимость
Основная проблема при переносе генератора в контейнер — не сама запись PDF, а ресурсы окружения. На машине разработчика присутствуют шрифты, локаль и файлы шаблона, которых нет в минимальном образе. Поэтому шрифты и изображения включают в публикацию, resolver читает их из известного каталога или ресурсов сборки, а культура форматирования задаётся в коде.
Тест контейнера выполняют из того же образа, который пойдёт в эксплуатацию. Недостаточно запустить unit-тест на Windows. Интеграционный тест генерирует несколько эталонных документов, извлекает метаданные и текст, а при необходимости сравнивает растровые изображения страниц с допуском. Так обнаруживаются замена шрифта, другая ширина строки и потерянный ресурс.
Файловая система контейнера может быть только для чтения. PDFsharp способен сохранять в поток, поэтому временный файл не обязателен. Если сторонний этап всё же требует путь, используют разрешённый временный каталог и удаляют данные после задания. Размер временного пространства и памяти ограничивают на уровне оркестратора.
Размер файла, память и производительность
На размер результата сильнее всего влияют изображения и встроенные шрифты. Сжатие потоков помогает, но не исправляет фотографию в десятки мегапикселей, выведенную как маленькая миниатюра. Оптимизацию начинают до PDFsharp: уменьшают изображение, выбирают подходящий формат и не встраивают лишние начертания. Затем измеряют размер на реальных документах.
При объединении больших PDF библиотеке приходится хранить и копировать структуру объектов. Для очень крупного задания заранее оценивают память, число страниц и общий размер входов. Обработку разбивают на части, если единый результат не является обязательным. Ошибку нехватки памяти нельзя надёжно исправить повтором того же задания без изменения условий.
Потоки следует передавать без лишних промежуточных копий. Схема MemoryStream — ToArray — новый MemoryStream — запись создаёт несколько экземпляров одного PDF. Если API следующего слоя принимает Stream, передают исходный поток после установки Position в начало. Если требуется массив, вызывают ToArray один раз после завершения сохранения.
Время генерации измеряют по этапам: загрузка данных, декодирование изображений, компоновка, сохранение и подпись. Тогда видно, где находится узкое место. Общий таймер не показывает, что сеть службы отметки времени занимает больше, чем PDFsharp. Метрики не должны включать персональные значения, но могут содержать тип шаблона, число страниц, объём и результат.
Чтение и извлечение текста
PDF хранит команды рисования, а не обязательно абзацы и строки. Текст может быть разбит на отдельные глифы, выведен в произвольном порядке или закодирован через таблицу шрифта. PDFsharp предоставляет доступ к низкоуровневым объектам, но не является готовым семантическим экстрактором. Простое чтение строк содержимого не гарантирует порядок чтения или правильные пробелы.
Для задачи найти текст и заменить его с сохранением верстки нужно разобрать операторы, матрицы, шрифты и позиции, затем решить, чем закрыть старый фрагмент и как разместить новый. Если новая строка шире, соседнее содержимое не сдвинется автоматически. В документах, которые контролирует ваша система, надёжнее хранить исходные данные и повторно генерировать страницу.
Для поиска и анализа готовых сторонних PDF лучше использовать библиотеку, ориентированную на извлечение текста и слов, а PDFsharp оставить для сборки и наложения. Если всё же выполняется низкоуровневый разбор, тестовый набор должен включать разные шрифты, повороты, колонки, лигатуры и сканы. Скан без текстового слоя потребует OCR отдельным инструментом.
Чего PDFsharp не делает автоматически
Библиотека не преобразует HTML и CSS в PDF. Она не запускает браузерный движок, не выполняет JavaScript и не рассчитывает веб-макет. Для HTML-отчёта используют отдельный renderer, а PDFsharp может затем объединить полученные страницы, добавить метаданные, штамп или шифрование. Попытка вручную повторить современный CSS через XGraphics обычно дороже выбора подходящего HTML-движка.
PDFsharp не отображает страницу PDF в bitmap и не предоставляет готовый просмотрщик. XPdfForm использует страницу как ресурс внутри другого PDF, но не создаёт PNG-превью. Для миниатюр и печати требуется внешний PDF-renderer. Это различие важно: импорт без потери качества и растровое отображение — разные операции.
Базовый API не выполняет автоматическую пагинацию длинного текста. XTextFormatter помогает внутри ограниченной области, но не строит весь документ. MigraDoc добавляет абзацы, стили, таблицы, колонтитулы и разрывы. Если проекту нужен потоковый макет, его лучше выбрать в начале, а не после того, как сотни координат уже записаны вручную.
Также не следует ожидать полноценного редактора содержимого, конвертации PDF в Word, OCR, визуального конструктора форм и универсального исправления повреждённых файлов. Эти задачи требуют специализированных компонентов. PDFsharp силён там, где приложение знает структуру результата и должно точно создать или перестроить PDF.
Обработка повреждённых и нестандартных файлов
Некоторые просмотрщики открывают PDF с ошибками, пытаясь восстановить структуру. Строгий парсер может отклонить тот же файл. Если PdfReader сообщает об ошибке, сначала проверяют, что используется свежий пакет, затем запускают независимый валидатор и пробуют пересохранить файл надёжным инструментом. Нельзя считать файл корректным только потому, что он визуально открылся.
Диагностика должна сохранять безопасные технические признаки: размер, число байтов заголовка, тип ошибки и этап обработки. Сам документ может содержать персональные данные, поэтому его копирование в общий журнал или тикет запрещают политикой. Для воспроизводимого дефекта создают обезличенный минимальный пример либо получают разрешение на передачу.
Повреждение после генерации часто возникает из-за неправильного обращения с потоком: данные читают до завершения Save, забывают вернуть Position в ноль, закрывают поток раньше ответа или дописывают посторонние байты. Проверка сигнатуры и попытка открыть результат сразу после сохранения быстро отделяют проблему генератора от ошибки передачи.
Типичные ошибки и способы исправления
Ошибка поиска шрифта в Core-сборке устраняется регистрацией FontResolver до первого XFont и поставкой реальных файлов. Установка семейства в ОС без изменения resolver не гарантирует результат в контейнере. После исправления перезапускают процесс, потому что шрифтовые решения кэшируются.
Обрезанный текст означает, что ширина или высота блока рассчитана неверно. Проверяют единицы, фактическую строку после форматирования, выбранное начертание и внутренние отступы. Уменьшать шрифт до исчезновения проблемы нельзя: нужно определить правило переноса, сокращения или расширения колонки.
Пустая или белая страница обычно появляется, когда команды рисования отправлены в другой XGraphics, контекст создан не для той страницы либо объект сохранён до вывода. Проверяют порядок операций и время жизни using-блоков. При работе с существующим PDF также убеждаются, что выбран режим добавления поверх или под содержимым.
Искажённое изображение исправляют расчётом пропорционального масштаба. Чёрный фон вокруг прозрачного PNG требует проверки альфа-канала и варианта сборки. Ошибка декодирования означает неподдерживаемый или повреждённый формат; его нормализуют заранее.
Слишком большой файл анализируют по ресурсам. Сначала ищут крупные изображения, затем дубли и полные шрифты. Простое включение максимального сжатия может почти не повлиять на уже сжатый JPEG. Оптимизация должна учитывать визуальное качество и требования архива.
Неверный пароль при открытии отличают от отсутствия прав на изменение. Для защищённого документа может требоваться владельческий пароль. Секрет не помещают в текст исключения. Если алгоритм шифрования не поддерживается получателем, выбирают совместимый режим до рассылки.
Недействительная подпись после сохранения означает, что документ был изменён после подписания либо подпись записана неверно. Все штампы, номера, метаданные и шифрование выполняют до подписи. Затем байты передают без повторного открытия и сохранения.
Разный макет на Windows и Linux почти всегда указывает на разные шрифты или ресурсы. Сравнивают фактические байты гарнитуры, культуру форматирования и пакет сборки. Эталонное тестирование только по тексту не обнаружит сдвиг; нужен визуальный или геометрический контроль.
Ссылка или закладка ведёт не туда, когда страницы импортированы после построения навигации и индексы изменились. Дерево закладок формируют после окончательного порядка страниц. При разделении файла внешние ссылки на удалённые страницы пересматривают.
Штамп повёрнут на 90 градусов из-за Rotation страницы. Координаты следует преобразовать с учётом поворота и видимой рамки. Тестируют не только A4 без поворота, но и сканы из МФУ.
Проверка результата и автоматические тесты
Минимальный тест открывает сформированный файл через PdfReader и проверяет число страниц, размеры и метаданные. Это обнаруживает нулевой поток, ошибку сохранения и неправильный диапазон. Для объединения дополнительно проверяют ожидаемую последовательность контрольных страниц. Такие тесты быстры и подходят для каждого коммита.
Текстовый тест извлекает доступные строки сторонним анализатором и ищет номер документа, сумму и имя получателя. Он полезен, но не доказывает правильную геометрию. Визуальный regression-тест рендерит страницы эталонным движком и сравнивает изображения с небольшим допуском. Эталон обновляют только после осознанного просмотра изменений.
Валидатор PDF/A или PDF/UA запускают независимо от библиотеки. Криптографическую подпись проверяют просмотрщиком или специализированной библиотекой, включая цепочку и отметку времени. Зашифрованный файл тестируют с правильным и неправильным паролем, а разрешения — в целевых клиентах.
Нагрузочный тест создаёт типичный, крупный и предельный документ, измеряет время, память и размер результата. Он должен выполняться в среде, близкой к рабочей, с теми же шрифтами. При обнаружении регрессии сначала определяют этап, затем сравнивают ресурсы и версии зависимостей, а не уменьшают общий тайм-аут без анализа.
Сравнение PDFsharp с аналогами
Инструменты ниже пересекаются по работе с PDF, но предлагают разные модели. Выбор зависит от того, нужен ли пользователю визуальный редактор, программное создание по координатам, потоковая верстка, извлечение текста или преобразование HTML.
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| PDFsharp | Создание, рисование, импорт и перестановка страниц из .NET-кода | Нет визуального редактора и автоматической верстки длинного текста |
| PDF Commander | Ручное редактирование, объединение и оформление PDF | Не предназначен для встраивания как программный .NET API |
| iText | Продвинутые программные операции с PDF, формами и подписями | Нужно учитывать условия AGPL или коммерческой лицензии |
| QuestPDF | Декларативная верстка новых отчётов и таблиц в .NET | Не является универсальным редактором существующих PDF |
| UglyToad.PdfPig | Извлечение текста, слов и геометрии из существующих PDF | Меньше возможностей для сложного создания и изменения |
| IronPDF | Преобразование HTML, CSS и веб-шаблонов в PDF | Коммерческая лицензия и более тяжёлый движок |
Как выбрать подходящий инструмент
PDFsharp выбирают для .NET-кода, который создаёт страницы, рисует точные формы, импортирует и переставляет существующие страницы, добавляет защиту или подпись. QuestPDF удобнее для новых отчётов с декларативной потоковой компоновкой. iText подходит, когда нужен широкий набор низкоуровневых PDF-функций и команда готова соблюдать условия его лицензирования. PdfPig полезен прежде всего для чтения, слов и геометрии текста. IronPDF рассматривают, когда основой документа служит HTML и приемлема коммерческая модель. PDF Commander предпочтителен человеку, который хочет править, объединять и оформлять файлы вручную без разработки.
Когда сочетать PDFsharp и MigraDoc
Один документ может использовать оба уровня. Основной отчёт описывают через MigraDoc: стили, абзацы, таблицы, изображения и колонтитулы. После рендеринга получают PdfDocument и добавляют низкоуровневые элементы PDFsharp: точный штамп, защиту, специальную аннотацию или подпись. Такой порядок сохраняет удобство потоковой верстки и доступ к структуре PDF.
Смешивать абсолютный и потоковый макет внутри одной страницы нужно осторожно. MigraDoc определяет финальные позиции только при рендеринге. Если XGraphics рисует фон заранее, координаты динамического абзаца могут измениться. Для контролируемого смешения используют события рендера или заранее зарезервированные области, размеры которых не зависят от текста.
Код следует разделять по пространствам ответственности. Модель документа MigraDoc не должна знать о паролях и подписи, а постобработка PDFsharp не должна повторно вычислять таблицы. Тогда каждый этап можно тестировать отдельно, а ошибка легко локализуется.
Практический чек-лист перед внедрением
Сначала перечисляют входные и выходные форматы, максимальное число страниц, размеры изображений и требования к шрифтам. Затем выбирают Core, GDI или WPF и проверяют пакет в целевой среде. Для Core сразу проектируют resolver, не откладывая его до публикации в Linux.
Макет описывают через измеримые правила: поля, сетку, минимальный размер шрифта, поведение длинного текста, место разрыва и повторяемые элементы. Каждый динамический блок должен иметь ответ на вопрос, что произойдёт при превышении размера. Ручная корректировка готового файла не считается частью автоматического процесса.
После реализации добавляют структурные, визуальные и нагрузочные тесты. Отдельно проверяют кириллицу, редкие символы, прозрачный PNG, повёрнутый исходный PDF, зашифрованный файл и ошибочный поток. Для архива, доступности и подписи используют независимые валидаторы.
Перед эксплуатацией фиксируют порядок этапов: получение данных, нормализация ресурсов, компоновка, объединение, нумерация, метаданные, защита, подпись, проверка и публикация. Любой этап после подписи запрещён, кроме передачи неизменённых байтов. Временные файлы и секреты удаляют по установленной политике.
Итоговый рабочий подход
PDFsharp даёт предсказуемый контроль над PDF, когда разработчик готов явно управлять страницами, графикой и ресурсами. Наиболее надёжные решения не прячут геометрию в случайных координатах: они используют модель макета, общий набор стилей, resolver шрифтов и отдельные функции для повторяемых операций. Тогда документ одинаково формируется в тесте, контейнере и рабочем сервисе.
Для коротких фиксированных форм достаточно XGraphics и нескольких функций измерения. Для длинных отчётов лучше подключить MigraDoc, оставив PDFsharp для импорта, наложения и финальных PDF-функций. HTML, OCR, растрирование страниц и семантическое извлечение текста передают специализированным компонентам, не заставляя одну библиотеку решать несвойственные задачи.
Готовый конвейер оценивают не по тому, открылся ли один файл, а по воспроизводимости: корректные шрифты, предсказуемые разрывы, ограниченная память, безопасные пароли, действительная подпись и успешная независимая проверка. Именно эти условия превращают набор команд рисования в устойчивую систему подготовки документов.