Examples / signed_pdfa / activex_cpp

Signed Pdfa

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

Demonstrates Digital Signatures

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

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.