Эта страница может содержать автоматически переведенный текст.
Объединение PDF-документов в C# и VB.NET
Бизнес часто объединяет PDF-файлы для архивирования документов. Хотя объединение PDF может показаться простой задачей, здесь есть много подводных камней. Нужно правильно объединять поля форм, закладки, слои и другие объекты PDF. Также следует избегать дублирования объектов, чтобы получить компактный выходной файл.
Библиотека Docotic.Pdf обрабатывает все нюансы объединения. Она позволяет объединять PDF-документы всего в несколько строк кода C# или VB.NET.

Docotic.Pdf поставляется с бесплатными и платными лицензиями. Получите библиотеку и бесплатный временный ключ лицензии на странице Скачать C# .NET библиотеку PDF.
Основы объединения PDF
Методы PdfDocument.Append позволяют добавлять PDF-документы из файлов, потоков или массивов байтов. Также есть параметры для добавления защищенных файлов и объединения полей форм.
Объединение двух PDF-файлов
Этот пример кода показывает, как объединить PDF-файлы в C#:
using var pdf = new PdfDocument("first.pdf");
pdf.Append("second.pdf");
pdf.Save("merged.pdf");
Попробуйте пример кода Объединить два PDF документа на GitHub.
Объединение потоков PDF
Предыдущий пример легко адаптировать для работы с потоками вместо путей к файлам. Вот вспомогательный метод для объединения потоков:
void Merge(Stream first, Stream second, Stream result)
{
using var pdf = new PdfDocument(first);
pdf.Append(second);
pdf.Save(result);
}
Объединение нескольких PDF-файлов
Можно неоднократно вызывать метод Append, чтобы добавлять несколько PDF-файлов:
string[] filesToMerge = ..;
using var pdf = new PdfDocument();
foreach (string file in filesToMerge)
pdf.Append(file);
// Удалить пустую страницу, добавленную вызовом PdfDocument()
pdf.RemovePage(0);
pdf.Save(pathToFile);
Объединение зашифрованных PDF-файлов
Для объединения зашифрованных документов есть перегрузки Append:
using var pdf = new PdfDocument();
pdf.Append("encrypted.pdf", new PdfStandardDecryptionHandler("password"));
pdf.Save("merged.pdf");
Дополнительную информацию можно найти в статье Расшифровка PDF-документов в C# и VB.NET.
Объединение PDF-форм
Каждое поле формы в PDF-документе должно иметь уникальное имя. Это может привести к проблеме, если документы для объединения содержат поля с одинаковыми именами. Docotic.Pdf предоставляет следующие стратегии объединения для конфликтующих элементов управления форм:
- Переименовывать добавляемые элементы управления при конфликте с существующими
- Объединять добавляемые элементы управления с существующими элементами управления
- Делать добавляемые элементы управления плоскими
- Не добавлять никаких элементов управления
- Добавлять элементы управления как есть
По умолчанию библиотека переименовывает добавляемые элементы управления при конфликте. Можно выбрать альтернативную стратегию с помощью класса PdfMergingOptions:
using var pdf = new PdfDocument("form.pdf");
var decryptionHandler = new PdfStandardDecryptionHandler(string.Empty);
var mergingOptions = new PdfMergingOptions()
{
ControlMergingMode = PdfControlMergingMode.CopyAsKids
};
pdf.Append("form.pdf", decryptionHandler, mergingOptions);
pdf.Save("merged.pdf");
В режиме CopyAsKids библиотека объединяет и синхронизирует конфликтующие элементы управления. Т.е. при изменении одного элемента управления второй будет иметь то же значение.
Уменьшение размера объединенного PDF-файла
PDF-документы могут содержать одинаковые объекты, например шрифты или изображения. При объединении таких документов результирующий документ будет содержать копии одних и тех же объектов. Используйте метод PdfDocument.ReplaceDuplicateObjects() для оптимизации результата объединения:
using var pdf = new PdfDocument("2024-05-28.pdf");
pdf.Append("2024-05-29.pdf");
pdf.ReplaceDuplicateObjects();
pdf.Save("merged.pdf");
Можно еще сильнее уменьшить размер выходного файла. Например, можно удалить неиспользуемые глифы шрифтов или сжать изображения. О поддерживаемых параметрах сжатия читайте в статье Сжатие PDF-документов в C# и VB.NET.
Настройка объединения PDF
Docotic.Pdf предоставляет методы для извлечения, изменения порядка или удаления страниц PDF. Их можно использовать вместе с методом Append для реализации пользовательских задач объединения PDF.
Добавление отдельных страниц PDF
Docotic.Pdf также позволяет объединять часть PDF-документа. Это можно сделать разными способами. Например, можно разделить добавленный PDF-документ и добавить извлеченные страницы. Следующий вспомогательный метод C# добавляет выбранные страницы в PdfDocument:
private static void AppendPart(PdfDocument pdf, string filePath, params int[] pagesToAppend)
{
using var streamToAppend = new MemoryStream();
using var other = new PdfDocument(filePath);
using var extracted = other.CopyPages(pagesToAppend);
var options = new PdfSaveOptions
{
UseObjectStreams = false
};
extracted.Save(streamToAppend, options);
pdf.Append(streamToAppend);
}
Или можно добавить весь PDF-документ и удалить лишние страницы. Следующий пример кода добавляет только первые две страницы файла second.pdf:
using var pdf = new PdfDocument(@"first.pdf");
int pageCountBefore = pdf.PageCount;
pdf.Append(@"second.pdf");
pdf.RemovePages(pageCountBefore + 2);
pdf.Save(pathToFile);
Еще одно решение связано с импозицией PDF. Об этом можно прочитать в соответствующем разделе.
Добавление PDF в начало
Методы Append всегда добавляют страницы в конец текущего документа. Как объединить PDF-файлы в другом порядке? Иногда можно изменить порядок вызовов Append. Т.е. используйте
pdf.Append("first.pdf");
pdf.Append("second.pdf");
вместо
pdf.Append("second.pdf");
pdf.Append("first.pdf");
Или можно изменить порядок страниц после объединения. Этот код C# перемещает добавленный PDF-документ в начало:
using var pdf = new PdfDocument(@"second.pdf");
int pageCountBefore = pdf.PageCount;
pdf.Append(@"first.pdf");
pdf.MovePages(pageCountBefore, pdf.PageCount - pageCountBefore, 0);
pdf.Save(pathToFile);
Дополнительную информацию об изменении порядка страниц PDF читайте здесь:
Импозиция PDF
Docotic.Pdf позволяет объединять несколько страниц PDF на одной странице. Используйте метод PdfDocument.CreateXObject(PdfPage) для создания объекта PdfXObject на основе существующей страницы. Затем нарисуйте этот объект с нужным масштабированием. Пример кода:
using var src = new PdfDocument(@"src.pdf");
using var dest = new PdfDocument();
PdfXObject firstXObject = dest.CreateXObject(src.Pages[0]);
PdfXObject secondXObject = dest.CreateXObject(src.Pages[1]);
PdfPage page = dest.Pages[0];
page.Orientation = PdfPaperOrientation.Landscape;
double halfOfPage = page.Width / 2;
page.Canvas.DrawXObject(firstXObject, 0, 0, halfOfPage, page.Height, 0);
page.Canvas.DrawXObject(secondXObject, halfOfPage, 0, halfOfPage, page.Height, 0);
dest.Save("result.pdf");
Проверьте связанный пример проекта Создать XObject со страницы на GitHub.
Объединение в качестве вложения
Иногда может понадобиться встроить один PDF-файл в другой в виде вложения. Это тоже возможно. Можно также добавлять ссылки на встроенный файл на страницах PDF:
using var pdf = new PdfDocument();
PdfFileSpecification first = pdf.CreateFileAttachment("first.pdf");
pdf.SharedAttachments.Add(first);
var bounds = new PdfRectangle(20, 70, 100, 100);
PdfFileSpecification fs = pdf.CreateFileAttachment("second.pdf");
pdf.Pages[0].AddFileAnnotation(bounds, fs);
pdf.Save("attachments.pdf");
Связанные примеры кода можно найти в группе Вложения PDF на GitHub.
Объединение в параллельных потоках
При объединении многих PDF-файлов код можно распараллелить. Класс PdfDocument не является потокобезопасным. Поэтому нужно использовать отдельные объекты PdfDocument в разных потоках. Подробнее см. в примере кода Объединение PDF документов в параллельных потоках.
Этот код показывает, как можно объединять потоки PDF параллельно:
Stream[] documentsToMerge = ..;
int rangeSize = 50;
while (documentsToMerge.Length > rangeSize)
{
int partitionCount = (int)Math.Ceiling(documentsToMerge.Length / (double)rangeSize);
var result = new Stream[partitionCount];
var partitioner = Partitioner.Create(0, documentsToMerge.Length, rangeSize);
Parallel.ForEach(partitioner, range =>
{
int startIndex = range.Item1;
int count = range.Item2 - range.Item1;
result[startIndex / rangeSize] = MergeToStream(documentsToMerge, startIndex, count);
});
documentsToMerge = result;
}
using PdfDocument final = GetMergedDocument(documentsToMerge, 0, documentsToMerge.Length);
final.Save("merged.pdf");
private static Stream MergeToStream(Stream[] streams, int startIndex, int count)
{
using PdfDocument pdf = GetMergedDocument(streams, startIndex, count);
var result = new MemoryStream();
var options = new PdfSaveOptions
{
UseObjectStreams = false // ускорить запись промежуточных документов
};
pdf.Save(result, options);
return result;
}
private static PdfDocument GetMergedDocument(Stream[] streams, int startIndex, int count)
{
var pdf = new PdfDocument();
try
{
for (int i = 0; i < count; ++i)
{
var s = streams[startIndex + i];
pdf.Append(s);
s.Dispose();
}
pdf.RemovePage(0);
pdf.ReplaceDuplicateObjects();
return pdf;
}
catch
{
pdf.Dispose();
throw;
}
}
Приведенный выше код разделяет входные документы на группы размером rangeSize. Затем код параллельно объединяет каждую группу в промежуточные документы. Процесс продолжается, пока число входных документов не станет достаточно малым для простого объединения.
Параллельное решение не обязательно быстрее однопоточной версии. Результаты могут различаться в зависимости от числа входных документов и их размеров. В примере кода оптимальное значение параметра rangeSize может быть больше или меньше. Следует выполнить бенчмаркинг приложения, чтобы найти наиболее эффективную реализацию.
Заключение
Можно использовать библиотеку Docotic.Pdf для объединения PDF-документов в C# и VB.NET. Она позволяет объединять файлы, потоки или массивы байтов. Можно объединять зашифрованные файлы, PDF-формы и отдельные страницы PDF. Docotic.Pdf также помогает сжимать итоговые файлы и экономить место на диске.
Попробуйте примеры кода из репозитория примеров Docotic.Pdf на GitHub. Можно получить ключ оценочной лицензии и скачать библиотеку на странице загрузки Docotic.Pdf.