examples/activex_cpp/signed_pdfa/signed_pdfa.cpp
142 lines
// ============================================================================
// signed_pdfa -- C++ ActiveX (COM) port, following
// examples/activex/signed_pdfa/signed_pdfa.vbs
//
// Creates a PDF/A-1b file with a signed signature field: the same custom
// appearance as signature_ap, then CheckConformance, the matching output
// intent, and finally a self-signed signature.
//
// The two substitution hooks the conformance pass needs are connection-point
// EVENTS here, not callback pointers:
// OnFontNotFound -> ReplaceFont back into the engine. PDF/A requires
// every font embedded, so a missing one must be
// substituted rather than dropped.
// OnReplaceICCProfile -> ReplaceICCProfile with the profile for the colour
// space the engine is asking about.
// Both return their result through ResultCode, which is what the Delphi
// callbacks return.
// ============================================================================
#import "..\\..\\wrappers\\activex\\LumasPdfAX.tlb" no_namespace named_guids
#include <windows.h>
#include "axcommon.h"
#include <cstdio>
#include <string>
// NO_COLOR is a type-library enumerator; local re-declaration is C2365.
static const char* TF = "E:\\LUMASPDFSDK\\examples\\test_files\\";
static ILumasPDFPtr pdf;
// The engine's colour order is 0x00BBGGRR, which is what r + g*256 + b*65536 is.
static long RGBv(long r, long g, long b) { return r + g * 256 + b * 65536; }
class ConfSink : public AxSink {
public:
long OnError(long, const wchar_t*, long) override { return 0; }
// Substitute a missing font with Arial.
long OnFontNotFound(__int64 pdfFont, const wchar_t*, long style, long, bool) override {
return pdf->ReplaceFontW(pdfFont, B("Arial"), style, VARIANT_TRUE);
}
// Supply the requested ICC profile from the shared test_files folder.
long OnReplaceICCProfile(long profileType, long colorSpace) override {
if (profileType == ictRGB)
return pdf->ReplaceICCProfileW(colorSpace, B(std::string(TF) + "sRGB.icc"));
if (profileType == ictCMYK)
return pdf->ReplaceICCProfileW(colorSpace, B(std::string(TF) + "ISOcoated_v2_bas.ICC"));
return pdf->ReplaceICCProfileW(colorSpace, B(std::string(TF) + "gray.icc"));
}
};
int main() {
ChdirToExe();
ComInit com;
if (!com.ok()) return 1;
if (FAILED(pdf.CreateInstance(L"LumasPdf.PDF"))) return 1;
ConfSink sink;
sink.Advise(pdf);
pdf->RaiseExceptions = VARIANT_FALSE;
pdf->CreateNewPDFW(B(""));
const char cr = '\r';
pdf->Append();
pdf->SetFontW(B("Arial"), fsNone, 10, VARIANT_TRUE, cp1252);
std::string body =
"This is a PDF/A 1b compatible PDF file that was digitally signed with "
"a self sign certificate. Because PDF/A requires that all fonts are embedded it is "
"important to avoid the usage of the 14 Standard fonts.";
body += cr; body += cr;
body +=
"Signature fields must be visible and the print flag must be set (default). "
"CheckConformance() adjusts these flags if necessary and produces a warning if changes "
"were applied.";
body += cr; body += cr;
body += "\\FC[255]Notice:\\FC[0]";
body += cr;
body += "CheckConformance() should be used to find the right settings to create PDF/A "
"compatible files.";
pdf->WriteFTextW(taLeft, B(body));
long sigField = pdf->CreateSigField(B("Signature"), -1, 200, 400, 200, 80);
pdf->SetFieldColor(sigField, fcBorderColor, csDeviceRGB, NO_COLOR);
pdf->PlaceSigFieldValidateIcon(sigField, 0, 15, 50, 50);
pdf->CreateSigFieldAP(sigField);
pdf->SaveGraphicState();
pdf->Rectangle(0, 0, 200, 80, fmNoFill);
pdf->ClipPath(cmWinding, fmNoFill);
long sh = pdf->CreateAxialShading(0, 0, 200, 0, 0.5, RGBv(120, 120, 220),
RGBv(255, 255, 255), 1, 1);
pdf->ApplyShading(sh);
pdf->RestoreGraphicState();
pdf->SaveGraphicState();
pdf->Ellipse(50.5, 1, 148.5, 78, fmNoFill);
pdf->ClipPath(cmWinding, fmNoFill);
sh = pdf->CreateAxialShading(0, 0, 0, 78, 2, RGBv(255, 255, 255),
RGBv(120, 120, 220), 1, 1);
pdf->ApplyShading(sh);
pdf->RestoreGraphicState();
pdf->SetFontW(B("Arial"), fsBold | fsUnderlined, 11, VARIANT_TRUE, cp1252);
pdf->SetFillColor(RGBv(120, 120, 220));
pdf->WriteFTextExW(50, 60, 150, -1, taCenter, B("Digitally signed by:"));
pdf->SetFontW(B("Arial"), fsBold | fsItalic, 18, VARIANT_TRUE, cp1252);
pdf->SetFillColor(RGBv(100, 100, 200));
pdf->WriteFTextExW(50, 45, 150, -1, taCenter, B("LumasPdf"));
pdf->EndTemplate();
pdf->EndPage();
long rc = pdf->CheckConformance(ctPDFA_1b_2005, 0);
if (rc == 1 || rc == 3)
pdf->AddOutputIntentW(B(std::string(TF) + "sRGB.icc"));
else if (rc == 2)
pdf->AddOutputIntentW(B(std::string(TF) + "ISOcoated_v2_bas.ICC"));
std::string outFile = OutFile();
if (pdf->HaveOpenDoc())
if (!pdf->OpenOutputFileW(B(outFile))) {
std::printf("OpenOutputFile failed\n");
return 1;
}
std::string cert = OutFile("test_cert.pfx");
if (pdf->CloseAndSignFile(B(cert), B("123456"), B("Test"), B("")))
std::printf("PDF file \"%s\" successfully created and signed! (CheckConformance=%ld)\n",
outFile.c_str(), rc);
else
std::printf("CloseAndSignFile failed: %ws\n",
(const wchar_t*)pdf->GetLastErrorMessage());
sink.Unadvise();
pdf = nullptr;
return 0;
}
This file is in the SDK at examples/activex_cpp/signed_pdfa/signed_pdfa.cpp.
The build fails if this page and that file ever differ.