PDF reports¶
PdfReportBuilder (insightdock/pdf/report.h) lays out multi-page PDF reports — section
headings, tables, embedded charts with pagination — on top of PoDoFo. It requires building with
-DINSIGHTDOCK_WITH_PODOFO=ON (see Getting started).
For embedding a single chart into a PDF you're already drawing with raw PoDoFo calls, use
ChartExporter::draw_pdf / draw_pdf_vector directly — see Embedding a single chart
below. Reach for PdfReportBuilder when you want automatic pagination, section titles, and tables
across a multi-page document.
Setting up the document¶
PdfReportBuilder doesn't own the PoDoFo document or painter — you create those yourself and pass
references in, so you stay in control of page size, fonts, and saving:
#include "insightdock/charts.h"
#include "insightdock/pdf/podofo.h"
#include <podofo/podofo.h>
using namespace PoDoFo;
using namespace insightdock::charts;
PdfMemDocument doc;
PdfPainter painter;
auto &page = doc.GetPages().CreatePage(PdfPageSize::A4);
painter.SetCanvas(page);
PdfFont &font = doc.GetFonts().GetStandard14Font(PdfStandard14FontType::Helvetica);
PdfFont &font_bold = doc.GetFonts().GetStandard14Font(PdfStandard14FontType::HelveticaBold);
painter.TextState.SetFont(font, 10);
pdf::PdfReportBuilder builder(doc, painter, font, font_bold);
Coordinates throughout PdfReportBuilder are millimetres from the page's top-left corner
(jsPDF-style), not PDF's native bottom-left points — this is deliberate, so report layout code
reads like a page-flow document rather than PDF geometry.
Section headings and a header bar¶
builder.draw_header_bar("Quarterly Chart Report");
builder.section_title("Revenue Overview");
section_title advances the builder's internal vertical cursor (y_mm()); everything drawn after
it — tables, chart flows — stacks below automatically.
Embedding charts with automatic pagination¶
draw_chart_flow is the usual way to place a chart: it calls ensure_space() first (starting a
new page if there isn't enough room left on the current one), optionally draws a caption and
border, draws the chart, then advances the cursor by the chart height plus a gap — so the next
element you draw lands below it without manual bookkeeping:
BarChart bar;
bar.options().width = 480;
bar.options().height = 280;
bar.options().title = "Quarterly Sales";
bar.set_categories({"Q1", "Q2", "Q3", "Q4"});
bar.add_series("2024", {30, 45, 60, 50});
bar.add_series("2025", {40, 50, 55, 65});
ExportOptions raster_opts;
raster_opts.supersample = 2; // sharper embedded raster output
builder.draw_chart_flow(pdf::PdfPageLayout::margin_l_mm, builder.content_width_mm(), 90,
bar, "Quarterly Sales by Year", raster_opts);
ExportOptions::pdf_backend still applies here — pass PdfBackend::Vector for pie, bar, line, area
and combo charts to get native vector paths and selectable text instead of a rasterized-and-embedded
PNG (see Vector export):
ExportOptions vector_opts;
vector_opts.pdf_backend = PdfBackend::Vector;
PieChart pie;
pie.options().width = 280;
pie.options().height = 280;
pie.options().background = Color::white();
pie.add_slice("A", 50);
pie.add_slice("B", 30);
pie.add_slice("C", 20);
builder.draw_chart_flow(pdf::PdfPageLayout::margin_l_mm, 90, 90, pie, "Category Split", vector_opts);
If you need to place a chart at an exact position without touching the flow cursor or triggering a
page break — e.g. a fixed logo-adjacent chart in a custom header — use draw_chart instead of
draw_chart_flow.
Tables¶
builder.draw_table(
"Top Devices",
{"MAC", "Hostname", "Bytes"},
{
{"aa:bb:cc:dd:ee:ff", "laptop-01", "1.2 GB"},
{"11:22:33:44:55:66", "phone-02", "480 MB"},
},
{60.0, 60.0, 40.0} // column widths in mm
);
Cell contents that need manual line breaks (long MAC/IP lists) can go through
pdf::multiline_cell() first to split them across lines within a cell.
Footers and finishing the document¶
builder.draw_footers("demo-user", "Generated by podofo_report_example");
doc.Save("report_builder_example.pdf");
Starting a new page manually¶
draw_chart_flow/ensure_space paginate automatically when content doesn't fit, but you can also
force a break — for a hard section boundary, for instance:
builder.new_page(); // default top margin
builder.new_page(/*top_y_mm=*/15.0);
Embedding a single chart without the report builder¶
If you're drawing a PDF page yourself with raw PoDoFo calls and only need one or two charts
embedded — no report layout, no pagination — skip PdfReportBuilder and call ChartExporter
directly. Coordinates here are PDF points, not millimetres, and (x, y) is the chart's lower-left
corner (native PDF convention, unlike the top-left convention PdfReportBuilder uses):
ExportOptions raster_opts;
raster_opts.supersample = 2;
ChartExporter::draw_pdf(doc, painter, bar, /*x=*/72, /*y=*/500, /*w=*/220, /*h=*/130, raster_opts);
draw_pdf_vector is the vector-backend equivalent (native paths for all built-in charts;
custom charts without draw() fall back to raster, same as PdfBackend::Vector above).
Full working example¶
examples/podofo_report_example.cc builds both a hand-assembled single-page PDF (raw
ChartExporter::draw_pdf calls) and a full PdfReportBuilder-driven multi-page report in one
program:
cmake -B build-podofo -DINSIGHTDOCK_WITH_PODOFO=ON -DINSIGHTDOCK_PODOFO_DIR=/path/to/podofo
cmake --build build-podofo --target podofo_report_example
export LD_LIBRARY_PATH="$(pwd)/build-podofo/podofo/target:${LD_LIBRARY_PATH}"
./build-podofo/podofo_report_example
See Examples for the full source.