Examples / reporting / activex_cpp

Reporting

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

Demonstrates Reporting

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

examples/activex_cpp/reporting/18_invoice_pro.cpp 212 lines
// ===========================================================================
//  LumasReport example 18 -- Professional FRAMED invoice
//  C++ ActiveX (COM) port of examples/delphi/reporting/18_invoice_pro.dpr
//
//  A polished, print-ready invoice built entirely from the report engine's
//  banded model + the SECTION-BOUNDED line feature (scope="section"):
//
//    * reportheader .... company / INVOICE title / bill-to, closed by section
//                        H rules (top hairline + bottom heavy navy rule)
//    * pageheader ...... navy caption bar; the table's top border + the FIVE
//                        vertical column dividers start here (scope="section")
//    * detail .......... one row per line-item; the same 5 vertical dividers
//                        span each row, so stacked they form ONE continuous
//                        column grid, closed per row by a hairline bottom rule
//    * summary ......... totals panel framed by a section box (H top/bottom +
//                        V left/right), grand total emphasised
//    * pagefooter ...... section H top rule + thank-you + page x of y
//
//  Every frame line is a scope="section" line: horizontal rules auto-span the
//  band WIDTH, vertical rules auto-span the band HEIGHT -- no coordinates to
//  chase. Money columns are right-aligned; totals use inline SUM(Qty*Price).
//  Exports to PDF / HTML / SVG / TEXT + native CSV / XLSX / XLS.
//
//  The address separators are the two literal bytes "\xC2\xB7", not a 'ยท'
//  character. The .dpr has no BOM, so Delphi reads its UTF-8 source bytes as
//  ANSI and passes C2 B7 straight through -- the reference output holds exactly
//  those two bytes. Writing the character instead would go through _bstr_t's
//  CP_ACP conversion and land somewhere else entirely (on this machine, where
//  the ANSI codepage IS UTF-8, a lone B7 is invalid and became U+FFFD).
// ===========================================================================
#import "..\\..\\wrappers\\activex\\LumasPdfAX.tlb" no_namespace named_guids

#include <windows.h>

#include "rptcommon.h"

#include <cstdio>
#include <string>

// Colours are COLORREF $00BBGGRR (low byte = red).
#define NAVY  "005F3A1F"   /* #1F3A5F  brand / rules / totals */
#define INK   "00222222"   /* near-black body text */
#define GREY  "00808080"   /* muted labels */
#define GRID  "00B9B9B9"   /* table grid lines */
#define HAIR  "00D8D8D8"   /* hairline row rules */
#define SHADE "00F4F1EC"   /* zebra row tint */
#define WHITE "00FFFFFF"

// Line items: Item, Qty, Price
static const char* DATA_CSV =
    "Item,Qty,Price\n"
    "Precision Widget Assembly,4,42.50\n"
    "Gadget Control Module,2,149.50\n"
    "Shielded Signal Cable (3m),10,4.75\n"
    "Universal Power Adapter,3,28.00\n"
    "Steel Mounting Bracket,12,3.25\n"
    "Thermal Interface Kit,5,11.20\n";

// The five vertical column dividers, section-scoped so each spans the band it
// sits in. Used in BOTH the caption band and the detail band -> continuous grid.
static std::string ColGrid(const std::string& tag) {
    return
        "   <line name=\"" + tag + "a\" orient=\"v\" scope=\"section\" x=\"0\"   width=\"0.35\" color=\"" GRID "\"/>\n"
        "   <line name=\"" + tag + "b\" orient=\"v\" scope=\"section\" x=\"95\"  width=\"0.35\" color=\"" GRID "\"/>\n"
        "   <line name=\"" + tag + "c\" orient=\"v\" scope=\"section\" x=\"117\" width=\"0.35\" color=\"" GRID "\"/>\n"
        "   <line name=\"" + tag + "d\" orient=\"v\" scope=\"section\" x=\"149\" width=\"0.35\" color=\"" GRID "\"/>\n"
        "   <line name=\"" + tag + "e\" orient=\"v\" scope=\"section\" x=\"182\" width=\"0.35\" color=\"" GRID "\"/>\n";
}

static std::string REPORT_XML(const std::string& csv) {
    return
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        "<report name=\"InvoicePro\" tagLangVersion=\"1\">\n"
        " <page width=\"210\" height=\"297\" marginLeft=\"14\" marginTop=\"14\" marginRight=\"14\" marginBottom=\"16\"/>\n"
        " <datasources><datasource alias=\"d\" provider=\"csv\" conn=\"" + csv + "\"/></datasources>\n"
        " <variables><variable name=\"PageNo\" init=\"1\"/></variables>\n"
        " <styles>\n"
        "  <style name=\"brand\"  fontName=\"Helvetica\" fontSize=\"20\" bold=\"1\" textColor=\"" NAVY "\"/>\n"
        "  <style name=\"addr\"   fontName=\"Helvetica\" fontSize=\"8\"  textColor=\"" GREY "\"/>\n"
        "  <style name=\"title\"  fontName=\"Helvetica\" fontSize=\"30\" bold=\"1\" textColor=\"" NAVY "\" hAlign=\"right\"/>\n"
        "  <style name=\"mlbl\"   fontName=\"Helvetica\" fontSize=\"8.5\" bold=\"1\" textColor=\"" GREY "\" hAlign=\"right\"/>\n"
        "  <style name=\"mval\"   fontName=\"Helvetica\" fontSize=\"8.5\" textColor=\"" INK "\" hAlign=\"right\"/>\n"
        "  <style name=\"billto\" fontName=\"Helvetica\" fontSize=\"8\" bold=\"1\" textColor=\"" NAVY "\"/>\n"
        "  <style name=\"cust\"   fontName=\"Helvetica\" fontSize=\"9.5\" textColor=\"" INK "\"/>\n"
        "  <style name=\"colh\"   fontName=\"Helvetica\" fontSize=\"8.5\" bold=\"1\" textColor=\"" WHITE "\"/>\n"
        "  <style name=\"colhr\"  fontName=\"Helvetica\" fontSize=\"8.5\" bold=\"1\" textColor=\"" WHITE "\" hAlign=\"right\"/>\n"
        "  <style name=\"cell\"   fontName=\"Helvetica\" fontSize=\"9.5\" textColor=\"" INK "\"/>\n"
        "  <style name=\"cellr\"  fontName=\"Helvetica\" fontSize=\"9.5\" textColor=\"" INK "\" hAlign=\"right\"/>\n"
        "  <style name=\"tlbl\"   fontName=\"Helvetica\" fontSize=\"9.5\" bold=\"1\" textColor=\"" INK "\" hAlign=\"right\"/>\n"
        "  <style name=\"tval\"   fontName=\"Helvetica\" fontSize=\"9.5\" textColor=\"" INK "\" hAlign=\"right\"/>\n"
        "  <style name=\"glbl\"   fontName=\"Helvetica\" fontSize=\"13\" bold=\"1\" textColor=\"" WHITE "\"/>\n"
        "  <style name=\"gval\"   fontName=\"Helvetica\" fontSize=\"13\" bold=\"1\" textColor=\"" WHITE "\" hAlign=\"right\"/>\n"
        "  <style name=\"note\"   fontName=\"Helvetica\" fontSize=\"8.5\" textColor=\"" GREY "\"/>\n"
        "  <style name=\"foot\"   fontName=\"Helvetica\" fontSize=\"8\" textColor=\"" GREY "\"/>\n"
        "  <style name=\"footr\"  fontName=\"Helvetica\" fontSize=\"8\" textColor=\"" GREY "\" hAlign=\"right\"/>\n"
        " </styles>\n"
        " <bands>\n"

        // ============ REPORT HEADER =================================================
        "  <band kind=\"reportheader\" name=\"rh\" height=\"42\">\n"
        "   <text name=\"co\"   x=\"0\"  y=\"0\"  w=\"110\" h=\"9\" style=\"brand\" wordWrap=\"0\">ACME Corporation</text>\n"
        "   <text name=\"a1\"   x=\"0\"  y=\"10\" w=\"120\" h=\"4\" style=\"addr\" wordWrap=\"0\">123 Industrial Way  \xC2\xB7  Springfield, IL 62704</text>\n"
        "   <text name=\"a2\"   x=\"0\"  y=\"14\" w=\"120\" h=\"4\" style=\"addr\" wordWrap=\"0\">+1 (555) 018-2245  \xC2\xB7  billing@acme.example</text>\n"
        "   <text name=\"ti\"   x=\"92\" y=\"0\"  w=\"90\"  h=\"13\" style=\"title\" wordWrap=\"0\">INVOICE</text>\n"
        "   <text name=\"ml1\"  x=\"108\" y=\"15\" w=\"40\" h=\"4\" style=\"mlbl\" wordWrap=\"0\">INVOICE #</text>\n"
        "   <text name=\"mv1\"  x=\"150\" y=\"15\" w=\"32\" h=\"4\" style=\"mval\" wordWrap=\"0\">INV-1042</text>\n"
        "   <text name=\"ml2\"  x=\"108\" y=\"20\" w=\"40\" h=\"4\" style=\"mlbl\" wordWrap=\"0\">ISSUE DATE</text>\n"
        "   <text name=\"mv2\"  x=\"150\" y=\"20\" w=\"32\" h=\"4\" style=\"mval\" wordWrap=\"0\">2026-07-19</text>\n"
        "   <text name=\"ml3\"  x=\"108\" y=\"25\" w=\"40\" h=\"4\" style=\"mlbl\" wordWrap=\"0\">DUE DATE</text>\n"
        "   <text name=\"mv3\"  x=\"150\" y=\"25\" w=\"32\" h=\"4\" style=\"mval\" wordWrap=\"0\">2026-08-18</text>\n"
        "   <text name=\"bt\"   x=\"0\"  y=\"25\" w=\"60\" h=\"4\" style=\"billto\" wordWrap=\"0\">BILL TO</text>\n"
        "   <text name=\"c1\"   x=\"0\"  y=\"29.5\" w=\"95\" h=\"4.5\" style=\"cust\" wordWrap=\"0\">Globex Manufacturing Co.</text>\n"
        "   <text name=\"c2\"   x=\"0\"  y=\"33.5\" w=\"95\" h=\"4\" style=\"addr\" wordWrap=\"0\">500 Commerce Blvd, Metropolis, NY 10001</text>\n"
        // section H rules close the header: hairline on top, heavy navy at the base
        "   <line name=\"rht\" orient=\"h\" scope=\"section\" vAlign=\"top\"    width=\"0.3\" color=\"" HAIR "\"/>\n"
        "   <line name=\"rhb\" orient=\"h\" scope=\"section\" vAlign=\"bottom\" width=\"1.1\" color=\"" NAVY "\"/>\n"
        "  </band>\n"

        // ============ COLUMN CAPTIONS (navy bar) ====================================
        "  <band kind=\"pageheader\" name=\"ph\" height=\"8\">\n"
        // filled navy caption bar (drawn first = behind the labels)
        "   <shape name=\"bar\" x=\"0\" y=\"0\" w=\"182\" h=\"8\" shape=\"0\" backColor=\"" NAVY "\"/>\n"
        "   <text name=\"hI\" x=\"3\"   y=\"2\" w=\"88\" h=\"5\" style=\"colh\"  wordWrap=\"0\">DESCRIPTION</text>\n"
        "   <text name=\"hQ\" x=\"97\"  y=\"2\" w=\"16\" h=\"5\" style=\"colhr\" wordWrap=\"0\">QTY</text>\n"
        "   <text name=\"hP\" x=\"119\" y=\"2\" w=\"26\" h=\"5\" style=\"colhr\" wordWrap=\"0\">UNIT PRICE</text>\n"
        "   <text name=\"hA\" x=\"151\" y=\"2\" w=\"29\" h=\"5\" style=\"colhr\" wordWrap=\"0\">AMOUNT</text>\n"
        + ColGrid("phg") +
        " </band>\n"

        // ============ DETAIL ROWS (continuous grid) =================================
        "  <band kind=\"detail\" name=\"det\" height=\"7\" data=\"d\">\n"
        // zebra tint on alternate rows (drawn first, behind grid + text)
        "   <shape name=\"zebra\" x=\"0\" y=\"0\" w=\"182\" h=\"7\" shape=\"0\" backColor=\"" SHADE "\" visible=\"RowNum % 2 = 0\"/>\n"
        "   <text name=\"dI\" x=\"3\"   y=\"1.6\" w=\"90\" h=\"4\" style=\"cell\"  wordWrap=\"0\">{{Item}}</text>\n"
        "   <text name=\"dQ\" x=\"97\"  y=\"1.6\" w=\"16\" h=\"4\" style=\"cellr\" wordWrap=\"0\">{{Qty}}</text>\n"
        "   <text name=\"dP\" x=\"119\" y=\"1.6\" w=\"26\" h=\"4\" style=\"cellr\" wordWrap=\"0\">{{expr: FORMATNUM('#,##0.00', Price)}}</text>\n"
        "   <text name=\"dA\" x=\"151\" y=\"1.6\" w=\"29\" h=\"4\" style=\"cellr\" wordWrap=\"0\">{{expr: FORMATNUM('#,##0.00', Qty*Price)}}</text>\n"
        + ColGrid("dg") +
        "   <line name=\"drb\" orient=\"h\" scope=\"section\" vAlign=\"bottom\" width=\"0.2\" color=\"" HAIR "\"/>\n"
        "  </band>\n"

        // ============ SUMMARY (totals panel + notes) ================================
        "  <band kind=\"summary\" name=\"sm\" height=\"46\">\n"
        // heavier navy rule closes the table above the summary
        "   <line name=\"stop\" orient=\"h\" scope=\"section\" vAlign=\"top\" width=\"0.6\" color=\"" NAVY "\"/>\n"
        // left: notes / payment instructions
        "   <text name=\"nh\" x=\"0\" y=\"4\"  w=\"95\" h=\"4\" style=\"billto\" wordWrap=\"0\">NOTES</text>\n"
        "   <text name=\"n1\" x=\"0\" y=\"8.5\" w=\"100\" h=\"4\" style=\"note\" wordWrap=\"0\">Payment due within 30 days. Bank transfer to</text>\n"
        "   <text name=\"n2\" x=\"0\" y=\"12\"  w=\"100\" h=\"4\" style=\"note\" wordWrap=\"0\">ACME Corp \xC2\xB7 IBAN GB00 ACME 0000 1042 \xC2\xB7 Ref INV-1042.</text>\n"
        // right: totals column, boxed values
        "   <text name=\"s1l\" x=\"100\" y=\"4\"  w=\"45\" h=\"4.5\" style=\"tlbl\" wordWrap=\"0\">Subtotal</text>\n"
        "   <text name=\"s1v\" x=\"149\" y=\"4\"  w=\"31\" h=\"4.5\" style=\"tval\" wordWrap=\"0\">{{expr: FORMATNUM('#,##0.00', SUM(Qty*Price))}}</text>\n"
        "   <text name=\"s2l\" x=\"100\" y=\"9.5\" w=\"45\" h=\"4.5\" style=\"tlbl\" wordWrap=\"0\">Tax (8.5%)</text>\n"
        "   <text name=\"s2v\" x=\"149\" y=\"9.5\" w=\"31\" h=\"4.5\" style=\"tval\" wordWrap=\"0\">{{expr: FORMATNUM('#,##0.00', SUM(Qty*Price)*0.085)}}</text>\n"
        // grand-total strip: filled navy panel + section-style box via short rules
        "   <shape name=\"gbar\" x=\"100\" y=\"16\" w=\"82\" h=\"10\" shape=\"0\" backColor=\"" NAVY "\"/>\n"
        "   <text name=\"gl\" x=\"104\" y=\"18.5\" w=\"40\" h=\"6\" style=\"glbl\" wordWrap=\"0\">TOTAL</text>\n"
        "   <text name=\"gv\" x=\"149\" y=\"18.5\" w=\"29\" h=\"6\" style=\"gval\" wordWrap=\"0\">{{expr: FORMATNUM('#,##0.00', SUM(Qty*Price)*1.085)}}</text>\n"
        "   <text name=\"gc\" x=\"100\" y=\"28\" w=\"82\" h=\"4\" style=\"footr\" wordWrap=\"0\">USD \xC2\xB7 Total items {{expr: COUNT()}}</text>\n"
        "  </band>\n"

        // ============ PAGE FOOTER ===================================================
        "  <band kind=\"pagefooter\" name=\"pf\" height=\"12\">\n"
        "   <line name=\"pft\" orient=\"h\" scope=\"section\" vAlign=\"top\" width=\"0.3\" color=\"" GRID "\"/>\n"
        "   <text name=\"ty\" x=\"0\"   y=\"3\" w=\"120\" h=\"4\" style=\"foot\"  wordWrap=\"0\">Thank you for your business.  Questions? billing@acme.example</text>\n"
        "   <text name=\"pg\" x=\"120\" y=\"3\" w=\"62\"  h=\"4\" style=\"footr\" wordWrap=\"0\">Page {{var:PageNo}} of {{var:TotalPages}}</text>\n"
        "  </band>\n"

        " </bands>\n"
        "</report>\n";
}

static void ExportOne(long job, long target, const std::string& path) {
    if (mPdf->RptExport(job, target, B(path)))
        printf("  wrote %s\n", path.c_str());
    else
        printf("  EXPORT FAILED: %s\n", path.c_str());
}

int main() {
    ChdirToExe();
    RptSession s;
    if (!s.ok) return 1;

    std::string dir = CurDir();
    std::string csv = dir + "18_items.csv";
    WriteText(csv, DATA_CSV);
    std::string lrpt = dir + "18_invoice.lrpt";
    WriteText(lrpt, REPORT_XML(csv));

    long job = mPdf->RptOpenReport(mEng, B(lrpt));
    if (job == 0) { printf("open failed\n"); DumpRptError(mEng); return 2; }

    int rc = 0;
    if (!mPdf->RptRender(job)) {
        printf("render failed\n");
        DumpRptError(mEng);
        rc = 3;
    } else {
        printf("rendered %d page(s); exporting:\n", (int)mPdf->RptGetPageCount(job));
        ExportOne(job, RPT_EXP_PDF,  dir + "18_invoice.pdf");
        ExportOne(job, RPT_EXP_HTML, dir + "18_invoice.html");
        ExportOne(job, RPT_EXP_SVG,  dir + "18_invoice.svg");
        ExportOne(job, RPT_EXP_TEXT, dir + "18_invoice.txt");
        ExportOne(job, RPT_EXP_CSV,  dir + "18_invoice.csv");
        ExportOne(job, RPT_EXP_XLSX, dir + "18_invoice.xlsx");
        ExportOne(job, RPT_EXP_XLS,  dir + "18_invoice.xls");
    }
    mPdf->RptCloseReport(job);
    return rc;
}

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