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.