Эта страница может содержать автоматически переведенный текст.

Объединить PDF документы в C# и VB.NET

Бизнесу часто нужно объединять PDF файлы для архивирования документов. Хотя объединение PDF файлов кажется простой задачей, здесь есть много подводных камней. Вам следует правильно комбинировать поля формы, закладки, слои и другие объекты PDF. Вам также следует избегать дублирования объектов, чтобы получить компактный выходной файл.

Библиотека Docotic.Pdf обрабатывает все нюансы слияния. Она позволяет объединять PDF документы всего в несколько строк кода C# или VB.NET.

Объединить PDF документы

Docotic.Pdf поставляется с бесплатными и платными лицензиями. Вы можете загрузить библиотеку и получить пробный лицензионный ключ на странице загрузки Docotic.Pdf.

Библиотека Docotic.Pdf 9.4.17467-dev Регрессионные тесты 14,760 прошло Всего загрузок NuGet 4,415,970

Основы объединения 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.ReplaceDuplateObjects() для оптимизации результата слияния:

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 файл в другой в качестве вложения. Это тоже возможно. Вы также можете добавить ссылки на встроенный файл на страницах 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 файлы в параллельных потоках

При объединении большого количества 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.