Examples / merge_pdf / activex_cpp

Merge Pdf

A complete, runnable activex_cpp program — 116 lines, shipped in your download.

Demonstrates Import & Output APIs

Same example, other languages: c cpp cpp_linux csharp php python vbnet

examples/activex_cpp/merge_pdf/merge_pdf.cpp 116 lines
// ============================================================================
//  merge_pdf -- C++ ActiveX (COM) port, following
//  examples/activex/merge_pdf/merge_pdf.vbs
//
//  Merges arbitrary PDF files. The interesting part is not ImportPDFFile --
//  it is the cases that must NOT be merged blindly:
//
//    * a PDF COLLECTION (portfolio) can only absorb other collections' catalog
//      objects; a plain PDF is ATTACHED to it instead of imported
//    * two documents that both carry form fields (or an XFA form) would collide
//      on field names, so the merge stops rather than corrupt the form
//
//  LumasPDF rewrites link-annotation and bookmark destinations to the new page
//  numbers as it imports, which is what the cover page explains.
// ============================================================================
#import "..\\..\\wrappers\\activex\\LumasPdfAX.tlb" no_namespace named_guids

#include <windows.h>

#include "axcommon.h"

#include <cstdio>
#include <string>

static const char* FILES[] = {
    "E:\\LUMASPDFSDK\\license.pdf",
    "E:\\LUMASPDFSDK\\sample_multipage.pdf",
};
static const int nFiles = (int)(sizeof(FILES) / sizeof(FILES[0]));

// fso.GetFileName equivalent -- the attachment's display name.
static std::string FileName(const char* path) {
    std::string s(path);
    size_t p = s.find_last_of("\\/");
    return p == std::string::npos ? s : s.substr(p + 1);
}

int main() {
    ChdirToExe();
    ComInit com;
    if (!com.ok()) return 1;
    int rc = 0;
    try {
        ILumasPDFPtr pdf;
        if (FAILED(pdf.CreateInstance(L"LumasPdf.PDF"))) _com_issue_error(E_FAIL);
        pdf->RaiseExceptions = VARIANT_TRUE;

        std::string outFile = OutFile();

        pdf->CreateNewPDFA(B(""));
        pdf->SetPageCoords(pcTopDown);

        pdf->Append();
        pdf->SetFontA(B("Helvetica"), fsRegular, 14, VARIANT_FALSE, cp1252);
        std::string cover =
            "The following pages were imported from different PDF files. LumasPDF adjusts the "
            "destinations of link annotations and bookmarks so that all destinations refer to "
            "the new page numbers after import.";
        cover += '\r'; cover += '\r';
        cover +=
            "Entire PDF files can be easily merged with ImportPDFFile() but it is also possible "
            "to import only specific pages.";
        pdf->WriteFTextExA(50, 50, pdf->GetPageWidth() - 100, -1, taJustify, B(cover));
        pdf->EndPage();

        bool first = true;
        long destPage = 1;
        bool haveXFA = false;
        bool isCollection = false;

        for (int i = 0; i < nFiles; ++i) {
            if (pdf->OpenImportFileA(B(FILES[i]), ptOpen, B("")) < 0) {
                std::printf("Cannot open %s\n", FILES[i]);
                return 1;
            }
            if (first) {
                first = false;
                haveXFA = pdf->GetInIsXFAForm() != 0;
                isCollection = pdf->GetInIsCollection() != 0;
                destPage = pdf->ImportPDFFile(destPage + 1, 1, 1);
                if (destPage < 0) break;
            } else {
                if (isCollection) {
                    if (pdf->GetInIsCollection() != 0) {
                        pdf->SetImportFlags(ifEmbeddedFiles);
                        if (pdf->ImportCatalogObjects() == 0) break;
                    } else {
                        pdf->CloseImportFile();
                        pdf->AttachFileA(B(FILES[i]), B(FileName(FILES[i])), VARIANT_TRUE);
                    }
                } else {
                    if ((pdf->GetInIsCollection() != 0) ||
                        (((pdf->GetInIsXFAForm() != 0) || (pdf->GetInFieldCount() > 0)) &&
                         ((pdf->GetFieldCount() > 0) || haveXFA)))
                        break;
                    pdf->SetImportFlags(ifImportAll | ifImportAsPage);
                    pdf->SetImportFlags2(if2UseProxy);
                    destPage = pdf->ImportPDFFile(destPage + 1, 1, 1);
                    if (destPage < 0) break;
                }
            }
            pdf->CloseImportFile();
        }

        if (pdf->HaveOpenDoc()) {
            if (!pdf->OpenOutputFileA(B(outFile))) return 1;
            if (pdf->CloseFile()) std::printf("OK: %s\n", outFile.c_str());
        }
    } catch (const _com_error& e) {
        const _bstr_t d = e.Description();
        std::fprintf(stderr, "%ws\n", d.length() ? (const wchar_t*)d : L"COM error");
        rc = 1;
    }
    return rc;
}

This file is in the SDK at examples/activex_cpp/merge_pdf/merge_pdf.cpp. The build fails if this page and that file ever differ.