Посібник з PDFsharp: Створення PDF-файлів за допомогою MigraDoc та PdfSharpCore
PDFsharp — це бібліотека .NET з відкритим кодом для створення та редагування PDF-документів з коду C#. Ви малюєте текст, лінії та зображення на сторінці, викликаєте функцію «Зберегти» — і у вас є PDF-файл. Жодного браузерного движка, жодного власного бінарного файлу. Проєкт існував ще у 2005 році, і багато рахунків-фактур, звітів та сертифікатів, що виходять із серверних частин .NET, досі проходять через нього.
Я написав це для розробників, яким потрібні PDF-файли на сервері та які хочуть знати, на що вони йдуть. Ми розглянемо, як PDFsharp та MigraDoc поєднуються, яку збірку встановлювати, перший робочий файл, проблеми зі шрифтами в Linux, нещодавню зміну ліцензії та момент, коли варто обрати щось інше.
Що таке PDFsharp і що він може робити?
За бібліотекою PDFsharp стоїть empira Software GmbH, невелика німецька майстерня, яка підтримує цю справу вже майже двадцять років. Більшість вашого часу витрачається на один клас – XGraphics. Якщо ви коли-небудь малювали у WinForms за допомогою System.Drawing.Graphics, ви почуватиметеся як вдома вже за десять хвилин: шрифт, пензель, x та y, починайте.
З коробки ви отримуєте:
- Створення PDF-документів з нуля з текстом, векторними фігурами, лініями та зображеннями
- Відкривайте існуючі PDF-файли та додавайте до їхніх сторінок вміст, такий як штампи, водяні знаки або номери сторінок
- Об'єднання та розділення документів шляхом імпорту сторінок з одного файлу в інший
- Вбудовувати та створювати підмножини шрифтів , щоб файл виглядав однаково на кожному комп'ютері
- Додайте закладки, посилання та метадані документа, такі як назва, автор і ключові слова
- Захист файлів за допомогою паролів та прапорців дозволів
- Підписуйте документи цифровим способом (починаючи з версії 6.2)
Розгортання нудне, я маю на увазі як похвалу. Все робиться на керованому коді, тому вашому операційному менеджеру ніколи не доведеться ставити Chrome без графічного інтерфейсу на систему чи шукати DLL-бібліотеку Ghostscript о 2-й годині ночі. Ви посилаєтеся на один пакет NuGet, надсилаєте — і все.
А тепер у чому загвоздка. Ви отримуєте полотно і нічого більше. Довгий абзац торкається нижнього поля? Ваша проблема. Таблиця виходить за межі першої сторінки? Теж ваша, якщо ви не застосуєте MigraDoc.
PDFsharp та MigraDoc: який шар вам потрібен?
Той самий постачальник, той самий графік випусків, зовсім різні завдання. MigraDoc Foundation знаходиться на один поверх вище від PDFsharp. Замість того, щоб вказувати йому, куди йде рядок, ви передаєте йому розділи, абзаци, таблиці, заголовки, нижні колонтитули, стилі та дивні діаграми, і він розбирається, куди все потрапляє. Виходить PDF. Або RTF, для колеги, який наполягає на відкритті всього у Word.
Моє скорочення: PDFsharp — це «розмістити цей текст за координатами x=40, y=120». MigraDoc — це «ось тридцять рядків і заголовок, який повторюється, ви сортуєте сторінки».
| Завдання | Найкраще підходить | Чому |
|---|---|---|
| Односторінковий сертифікат або квиток із фіксованими позиціями | PDFsharp | Повний контроль над кожною координатою |
| Багатосторінковий звіт з таблицями та поточними заголовками | МіграДок | Автоматичне нумерування сторінок та стилі |
| Рахунок-фактура зі змінною кількістю позицій | МіграДок | Таблиці розширюються та розбиваються на сторінках |
| Штамп, водяний знак або номери сторінок на існуючому PDF-файлі | PDFsharp | MigraDoc створює лише нові документи |
| Об'єднання або розділення існуючих файлів | PDFsharp | Імпорт сторінок – це функція PDFsharp |
| Бізнес-діаграма у звіті | МіграДок | Вбудована підтримка діаграм |
| Змішаний: згенерований звіт плюс намальована на замовлення обкладинка | Обидва | MigraDoc може відображати на сторінці PDFsharp |
Реальні проекти, як правило, мають і те, й інше. Основний текст через MigraDoc, намальована від руки обкладинка або штамп на PDF-файлі якогось постачальника через нижній шар. У будь-якому разі неможливо уникнути встановлення PDFsharp, оскільки пакет MigraDoc перетягує його як залежність.
Пояснення збірок PDFsharp: збірка Core, GDI+ та WPF
Новачки зазвичай опиняються наступними у списку пакетів NuGet. PDFsharp 6 постачається у трьох збірках, кожна з яких має власний двійник MigraDoc.
| Пакет NuGet | Збірка | Платформи | Шрифти та зображення |
|---|---|---|---|
| PDFsharp / PDFsharp-MigraDoc | Ядро | Windows, Linux, macOS | Потрібен засіб розпізнавання шрифтів; зображення BMP, PNG, JPEG |
| PDFsharp-GDI / PDFsharp-MigraDoc-GDI | GDI+ | Тільки для Windows | Використовує встановлені шрифти через System.Drawing |
| PDFsharp-WPF / PDFsharp-MigraDoc-WPF | WPF | Тільки для Windows | Використовує встановлені шрифти через WPF |
Поточні пакети орієнтовані на .NET 8, 9 та 10, а також .NET Standard 2.0 та .NET Framework 4.6.2. Тож охоплено практично все, над чим ще ведеться робота, від старого інструменту WinForms до абсолютно нового API ASP.NET Core.
Консольний додаток, веб-API, Docker, Azure Functions, щось на Linux? Збірка Core. GDI+ або WPF мають сенс лише для настільних додатків Windows, де потрібні системні шрифти або попередній перегляд у нативному елементі керування. Хтось на Stack Overflow якось запитав, чи потрібна їхньому консольному додатку версія WPF. Ні.

Як встановити PDFsharp з NuGet у Visual Studio
Ви все ще знайдете повідомлення на форумах часів WinForms, де вам радять взяти ZIP-архів, знайти DLL-файл і самостійно додати посилання. Ігноруйте їх. Встановіть PDFsharp через NuGet; менеджер пакетів відстежує версії, тому вам не доведеться цього робити.
- Відкрийте своє рішення у Visual Studio та клацніть правою кнопкою миші на проекті в Solution Explorer.
- Виберіть «Керування пакетами NuGet» , перейдіть на вкладку «Огляд» і знайдіть «PDFsharp».
- Виберіть PDFsharp (базова збірка) або PDFsharp-MigraDoc, якщо вам також потрібна верстка документа. Перевірте, чи видавець — empira Software GmbH.
- Натисніть кнопку «Встановити» та прийміть умови ліцензії.
- Зберіть проєкт один раз, щоб підтвердити правильність відновлення пакета.
Користувачі терміналів можуть пропустити клацання:
bash dotnet додати пакет PDFsharp dotnet додати пакет PDFsharp-MigraDoc
У консолі менеджера пакетів те саме — Install-Package PDFsharp. І, будь ласка, не змішуйте збірки. Я бачив, як команда втратила цілий день через конфлікти типів, тому що один проект посилався на PDFsharp, а його сусід запитував PDFsharp-GDI.
Ваш перший PDF-документ з PDFsharp на C
Нижче наведено інформацію про найменшу програму, яка створює односторінковий PDF-файл. Вона працює на збірці Core на будь-якій ОС, якщо налаштовано засіб розпізнавання шрифтів (наступний розділ).
```csharp
використання PdfSharp.Drawing;
використання PdfSharp.Pdf;
// PDFsharp 6.5+ вимагає вибору ліцензії перед першим використанням
PdfSharpLicense.SetAgplLicense();
var документ = новий PdfДокумент();
document.Info.Title = "Привіт, PDFsharp";
Сторінка PdfPage = документ.ДодатиСторінку();
XGraphics gfx = XGraphics.FromPdfPage(сторінка);
var шрифт = новий XFont("Arial", 20, XFontStyleEx.Bold);
gfx.DrawString("Привіт, PDFsharp!", шрифт, XBrushes.Black,
новий XRect(0, 0, сторінка.Ширина.Точка, сторінка.Висота.Точка),
XStringFormats.Center);
документ.Зберегти("привіт.pdf");
```
Кожна програма PDFsharp має таку форму: document, page, XGraphics, draw, save. Одиницями вимірювання є пункти (1/72 дюйма), а початок координат знаходиться у верхньому лівому куті. У версії 6.2.x видаліть лінію ліцензії. Цей API з'явився лише у версії 6.5.0.
А також версія MigraDoc:
```csharp
використання MigraDoc.DocumentObjectModel;
використання MigraDoc.Rendering;
var doc = новий Документ();
Розділ section = doc.ДодатиРозділ();
section.AddParagraph("Привіт, MigraDoc!");
var renderer = new PdfDocumentRenderer { Document = doc };
рендерер.RenderDocument();
renderer.PdfDocument.Save("hello-migradoc.pdf");
```
Жодних координат ніде. Додайте 500 абзаців і отримаєте скільки завгодно сторінок, без жодного коду для нульової сторінкової нумерації.
Шрифти в основній збірці PDFsharp: проблема з Resolver
Розпитайте навколо, і найчастіше ви почуєте скарги на шрифти. Збірки GDI+ та WPF просто зчитують встановлені шрифти TrueType з Windows. Збірка Core не може нічого припустити, оскільки тонкий контейнер Linux часто взагалі не має жодних корисних шрифтів. Тому перше розгортання Docker зазвичай завершується винятком про те, що не знайдено відповідного шрифту.
Виправлення полягає в розпізнаванні шрифтів, класі, який реалізує IFontResolver. Він має два методи:
- ResolveTypeface зіставляє запитувану родину та стиль (наприклад, "Arial", жирний шрифт) з назвою обличчя
- GetFont повертає необроблені байти TTF-файлу для цього імені обличчя
Зареєструйте його один раз під час запуску через GlobalFontSettings.FontResolver, перед створенням першого XFont.
```csharp
публічний клас AppFontResolver : IFontResolver
{
public FontResolverInfo? ResolveTypeface(string family, bool bold, bool italic)
=> new FontResolverInfo(bold ? "Inter-Bold" : "Inter-Regular");
public byte[]? GetFont(string faceName) => File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory, "Fonts", faceName + ".ttf"));
}
GlobalFontSettings.FontResolver = новий AppFontResolver();
```
Речі, які варто знати, перш ніж вдарити їх самостійно:
- Зберігайте файли TTF у папці Fonts у проєкті та налаштуйте їх копіювання до вихідного каталогу або вбудовуйте їх як ресурси, щоб образ контейнера завжди містив їх.
- У випуску 6.2.0 було видалено старий CoreBuildFontResolver. У Windows або WSL2 ви можете відновити подібну поведінку за допомогою GlobalFontSettings.UseWindowsFontsUnderWindows, але це нічого не дає в Linux або macOS.
- Офіційний репозиторій зразків містить SubstitutingFontResolver, який ви можете скопіювати як відправну точку.
- Перевірте ліцензію шрифту, перш ніж вбудовувати комерційний шрифт у згенеровані файли. Відкрийте шрифти, такі як Inter, Roboto або Noto, щоб повністю уникнути цього питання.
З налаштованим резольвером вивід виглядає однаково у Windows, Linux та macOS.
Об'єднання, підписання та захист файлів за допомогою PDFsharp 6
Багато команд ніколи не створюють документ з нуля. Вони використовують бібліотеку для склеювання, штампування або блокування файлів, які вже існують. Об'єднання виконується в кілька рядків: відкрийте кожне джерело в режимі імпорту та скопіюйте сторінки.
csharp var output = new PdfDocument(); foreach (var path in new[] { "part1.pdf", "part2.pdf" }) { using var input = PdfReader.Open(path, PdfDocumentOpenMode.Import); foreach (PdfPage p in input.Pages) output.AddPage(p); } output.Save("merged.pdf");
Розділення — це той самий цикл у зворотному напрямку, один вихідний файл на сторінку або діапазон. Для захисту document.SecuritySettings приймає пароль користувача, пароль власника та прапорці дозволів (друк, копіювання тексту тощо).
Дві давно очікувані функції нарешті з'явилися у версії 6.2:
- Цифрові підписи на основі CMS. Підписувач за замовчуванням постачається в окремій збірці PDFsharp.Cryptography, і ви можете підключити свій власний, реалізувавши IDigitalSigner. Підтримка сервера позначок часу працює на сучасній .NET, але не на збірці .NET Framework.
- Вивід PDF/A для архівування. В офіційній документації його описують як робочий «з деякими обмеженнями» та все ще в розробці, тому перевірте результат за допомогою програми перевірки PDF/A, перш ніж покладатися на нього на відповідність.
У цьому випуску також з'явилися кольорові гліфи емодзі та керована реалізація MD5, що має значення, незалежно від того, чи працюєте ви за правилами FIPS, чи в Blazor.
Репозиторій PDFsharp, форки PdfSharpCore та версії 1.x
Пошукайте PDFsharp на GitHub, і ви знайдете кілька проектів майже з однаковою назвою. Звинувачуйте історію.
Стара лінійка 1.x (1.50, 1.51) працювала лише на .NET Framework. Коли вийшов .NET Core, людям не хотілося чекати, тому вони портували його самі. PdfSharpCore, започаткований Стефаном Штайгером і підхоплений пізніше іншими розробниками, — це той, який знає більшість людей. Він вирвав GDI+ і запустив код на Linux за роки до офіційного релізу.
Потім з'явився PDFsharp 6: належним чином переписаний на сучасному .NET з офіційною кросплатформною збіркою Core. Форки втратили більшу частину свого сенсу існування.
| Демонструвати | Супровідник | Платформи | Статус |
|---|---|---|---|
| PDFsharp 6.x (репозиторій empira/PDFsharp) | empira Software GmbH | .NET 8–10, .NET Standard 2.0, .NET Framework 4.6.2 | Активно підтримується |
| PDFsharp 1.5x | empira Software GmbH | Тільки .NET Framework | Вихідний код зберігається на GitHub, більше не підтримується |
| PdfSharpCore та його форки | Громада | .NET Core / .NET Standard 2.0 | Залежить від форку, оновлення повільніші |
| PDFsharp-MigraDoc 6.x | empira Software GmbH | Те саме, що й PDFsharp 6 | Активно підтримується |
Отже, тема "PdfSharp проти PdfSharpCore", яка постійно з'являється на Reddit? Новий проект, офіційні пакети. Залишайте форк лише тоді, коли старий код вже спирається на нього, і міграція зараз не варта зусиль. А коли документації здається обмаль, перейдіть до окремого репозиторію зразків empira. Його папки та файли є запускаємими прикладами, що виключає будь-які здогадки.
Чи є PDFsharp безкоштовним? Зміни ліцензування в PDFsharp 6.5
Протягом більшої частини свого існування – так. Він мав ліцензію MIT, що означає комерційне використання майже без жодних зобов'язань. Кожен реліз до версії 6.2.4 включно досі має таку ліцензію.
Версія 6.5.0 це змінила. Бібліотека тепер має подвійну ліцензію:
- AGPL (безкоштовно). Ви вмикаєте його, викликавши PdfSharpLicense.SetAgplLicense() перед використанням бібліотеки. AGPL вимагає, щоб ви зробили свій вихідний код доступним для користувачів, включаючи тих, хто взаємодіє з вашим програмним забезпеченням через мережу, тому він погано підходить для SaaS із закритим вихідним кодом.
- Комерційна ліцензія. Ви купуєте ліцензійний ключ у постачальника та завантажуєте його за допомогою PdfSharpLicense.LoadLicense(...). Це знімає зобов'язання AGPL.
Починаючи з версії 6.5.0, ваш код має вибирати один варіант явно. Попередні версії 7.0 працюють так само.
На практиці:
- Проєкти з відкритим кодом, сумісні з AGPL, можуть продовжувати використовувати найновішу версію безкоштовно.
- Продукти із закритим кодом можна або придбати комерційну ліцензію, або залишитися на версії 6.2.x, яка залишається під егідою MIT. Залишитися на ній означає пропустити майбутні виправлення, тому чесно зважте цю вартість.
- Що б ви не обрали, прочитайте фактичний текст ліцензії та попросіть свою команду юристів підписати його. Цей посібник не є юридичною консультацією.

Де PDFsharp не дотягує і з чим його поєднувати
Бібліотека добре виконує свою роботу. Просто завдання вузьке, і ті самі прогалини виникають у кожній темі форуму.
- Немає можливості конвертувати HTML у PDF. PDFsharp не рендерить HTML або CSS. Існує доповнення спільноти HtmlRenderer.PdfSharp, але воно підтримує лише HTML 4.01 та CSS рівня 2, тому сучасні макети з flexbox або сіткою не виживуть. Команди, яким потрібні шаблони HTML, зазвичай використовують рендерер без headless-браузера.
- Без JavaScript. Діаграми або контент, створений на стороні клієнта, не можуть бути захоплені.
- Без рендерингу зображень. PDFsharp не може перетворити сторінку PDF на PNG для мініатюр або попереднього перегляду. Для цього потрібен окремий растеризатор.
- Обмежене видобування тексту. Ви можете пройтися потоком контенту, але немає високорівневого API, який би повертав чистий текст з позиціями. PdfPig, окрема бібліотека з відкритим кодом, створена спеціально для читання та видобування тексту.
- Часткова підтримка форм. Просте заповнення AcroForm працює; складні форми зі скриптами – це вже інша історія.
- Для таблиць потрібен MigraDoc. Звичайний PDFsharp дозволяє самостійно розраховувати висоту кожного рядка.
Це не робить його поганим вибором. Документи на основі даних, такі як рахунки-фактури, виписки, етикетки та сертифікати, – це саме те, що йому потрібно, а розмір – мінімальний. Ідеальні до пікселів копії веб-сторінок – ні.
PDFsharp для криптовалютних рахунків-фактур та квитанцій про оплату
Квитанції, мабуть, найчастіше створюються за допомогою PDFsharp. З криптовалютними квитанціями трохи складніше, ніж з картковими. Гарна новина: щойно шлюз потрапляє на ваш вебхук (HTTP-зворотний виклик, що надсилається, коли статус платежу змінюється), ви отримуєте всі дані. Нічого зайвого для пошуку.
Що б я на один поставив:
- Ідентифікатор замовлення або рахунку-фактури та реквізити продавця
- Сума в криптовалюті та її фіатний еквівалент на момент оплати
- Монета та мережа (наприклад, USDT на TRON проти USDT на Ethereum)
- Хеш транзакції, щоб клієнт міг перевірити її в оглядачі блоків
- Скорочена адреса гаманця-отримувача та час підтвердження
- QR-код, що посилається на сторінку транзакції або рахунку-фактури
Моноширинний шрифт для хешів та адрес. Клієнти мружаться, дивлячись на них по одному символу за раз, а в Arial нуль і велика літера О виглядають як близнюки. Якщо хеш занадто широкий, перенесіть його; текст розміром 6 пунктів нікому не допоможе. Вбудуйте шрифти, збережіть файл із замовленням і підпишіть його, якщо ваш бухгалтер коли-небудь попросить квитанції.
Нічого з цього не стосується фактичного отримання грошей. Для цього вам потрібно підключити шлюз, такий як Plisio . Він приймає BTC, USDT та багато інших монет, створює рахунок-фактуру та викликає вашу кінцеву точку щоразу, коли статус змінюється. Ваш обробник передає корисне навантаження до MigraDoc, і покупець отримує квитанцію з вашим логотипом, поки він ще на сторінці подяки.
Висновок
Я постійно повертаюся до PDFsharp з однієї причини: він маленький і не заважає. Збірка Core працює будь-де, де працює .NET, а MigraDoc бере на себе роботу з макетуванням, коли одного полотна недостатньо. Три речі, які потрібно вирішити заздалегідь: яка збірка, як завантажуються шрифти та яка у вас ліцензія тепер, коли версія 6.5 перейшла на AGPL. Друкуйте дані, а не веб-сторінки, і вони прослужать роками.