이 페이지에는 자동 번역된 텍스트가 포함될 수 있습니다.

C# 및 VB.NET에서 PDF 문서 병합

비즈니스에서는 문서 보관을 위해 PDF 파일을 자주 병합합니다. PDF 병합은 단순한 작업처럼 보이지만, 여기에는 여러 함정이 있습니다. 양식 필드, 책갈피, 레이어 및 기타 PDF 개체를 올바르게 결합해야 합니다. 또한 중복 개체를 피해서 더 작은 출력 파일을 만들어야 합니다.

Docotic.Pdf 라이브러리는 모든 병합 세부 사항을 처리합니다. C# 또는 VB.NET 코드 몇 줄만으로 PDF 문서를 결합할 수 있습니다.

PDF 문서 병합

Docotic.Pdf는 무료 및 유료 라이선스와 함께 제공됩니다. C# .NET PDF 라이브러리 다운로드 페이지에서 라이브러리와 무료 기간 제한 라이선스 키를 받으세요.

PDF 병합 기본 사항

PdfDocument.Append 메서드를 사용하면 파일, 스트림 또는 바이트 배열에서 PDF 문서를 추가할 수 있습니다. 보호된 파일을 추가하고 양식 필드를 병합하는 옵션도 있습니다.

두 개의 PDF 파일 결합

다음 샘플 코드는 C#에서 PDF 파일을 병합하는 방법을 보여줍니다:

using var pdf = new PdfDocument("first.pdf");
pdf.Append("second.pdf");
pdf.Save("merged.pdf");

GitHub의 두 개의 PDF 문서 병합 코드 샘플을 사용해 보세요.

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");

자세한 내용은 C# 및 VB.NET에서 PDF 문서 해독 문서를 참조하세요.

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");

출력 파일 크기를 더 줄일 수도 있습니다. 예를 들어 사용하지 않는 글꼴 글리프를 제거하거나 이미지를 압축할 수 있습니다. 지원되는 압축 옵션은 C# 및 VB.NET에서 PDF 문서 압축 문서에서 확인하세요.

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 페이지를 한 페이지에 결합할 수 있습니다. 기존 페이지를 기반으로 PdfXObject 개체를 만들려면 PdfDocument.CreateXObject(PdfPage) 메서드를 사용하세요. 그런 다음 원하는 크기 조정으로 이 개체를 그리면 됩니다. 샘플 코드:

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");

관련 코드 샘플은 GitHub의 PDF attachments 그룹에서 확인할 수 있습니다.

병렬 스레드로 병합

많은 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 매개변수의 최적값은 더 크거나 더 작을 수 있습니다. 가장 효과적인 구현을 찾으려면 애플리케이션을 벤치마크해야 합니다.

결론

C# 및 VB.NET에서 PDF 문서 병합에 Docotic.Pdf 라이브러리를 사용할 수 있습니다. 파일, 스트림 또는 바이트 배열을 병합할 수 있습니다. 암호화된 파일, PDF 양식, 특정 PDF 페이지도 병합할 수 있습니다. 또한 Docotic.Pdf는 결과 파일을 압축하고 디스크 공간을 절약하는 데도 도움이 됩니다.

GitHub의 Docotic.Pdf 샘플 저장소에서 코드 샘플을 사용해 보세요. Docotic.Pdf 다운로드 페이지에서 평가용 라이선스 키를 받고 라이브러리를 다운로드할 수 있습니다.