Rix is the official package layer for Vix.cpp Browse packages
Skip to content

Table

This example shows how to create a PDF table with rix/pdf.

The example uses the public Rix facade:

cpp
#include <rix.hpp>

and accesses PDF through:

cpp
rix.pdf

Use this example when you want to create a PDF report with rows, columns, and a header.

Create the file

bash
mkdir -p ~/rix-pdf-table-example
cd ~/rix-pdf-table-example
touch table.cpp

Add:

cpp
#include <rix.hpp>

int main()
{
  auto doc = rix.pdf.document();

  doc.set_title("Rix PDF Table Example");

  auto &page = doc.add_page();

  auto y = page.heading(
      page.x_left(),
      page.y_top(),
      "Project table",
      1);

  y -= 20.0F;

  rixlib::pdf::Table table;

  table.set_column_widths({
      160.0F,
      160.0F,
      160.0F});

  table.add_header({
      "Name",
      "Language",
      "Project"});

  table.add_row({
      "Ada",
      "C++",
      "Rix"});

  table.add_row({
      "Gaspard",
      "C++",
      "Vix.cpp"});

  table.add_row({
      "Grace",
      "Systems",
      "PDF"});

  page.table(
      page.x_left(),
      y,
      table);

  auto saved = rix.pdf.save(doc, "rix_pdf_table.pdf");

  if (saved.failed())
  {
    rix.debug.eprint(
        "pdf error:",
        rix.pdf.error.to_string(saved.error()),
        saved.error().message());

    return 1;
  }

  rix.debug.print("created:", "rix_pdf_table.pdf");
  return 0;
}

Run it:

bash
vix run table.cpp

If Rix is not available yet for single-file usage:

bash
vix install -g rix/rix
vix run table.cpp

This creates:

txt
rix_pdf_table.pdf

What this example does

The example creates a document:

cpp
auto doc = rix.pdf.document();

It adds a page:

cpp
auto &page = doc.add_page();

It creates a table:

cpp
rixlib::pdf::Table table;

It sets column widths:

cpp
table.set_column_widths({
    160.0F,
    160.0F,
    160.0F});

It adds a header row:

cpp
table.add_header({
    "Name",
    "Language",
    "Project"});

It adds data rows:

cpp
table.add_row({
    "Ada",
    "C++",
    "Rix"});

Then it draws the table on the page:

cpp
page.table(
    page.x_left(),
    y,
    table);

Create a table

Use:

cpp
rixlib::pdf::Table table;

The table model stores:

txt
column widths
rows
cells
style

A table is drawn when you pass it to:

cpp
page.table(...)

Set column widths

Use:

cpp
table.set_column_widths({
    160.0F,
    160.0F,
    160.0F});

Column widths are expressed in PDF points.

One inch is 72 points.

The total table width should fit inside the page content width.

You can check the available width with:

cpp
page.content_width()

Add a header

Use:

cpp
table.add_header({
    "Name",
    "Language",
    "Project"});

A header is a table row marked as a header row.

It is rendered with the table header style.

Add rows

Use:

cpp
table.add_row({
    "Ada",
    "C++",
    "Rix"});

Each value becomes a cell.

Rows should normally have the same number of values as the number of columns.

Draw the table

Use:

cpp
auto y_after_table = page.table(
    page.x_left(),
    y,
    table);

The arguments are:

txt
x position
starting y position
table

page.table returns the Y position below the table.

This lets you continue drawing after the table.

Continue after a table

cpp
auto y_after_table = page.table(
    page.x_left(),
    y,
    table);

y_after_table -= 20.0F;

page.text(
    page.x_left(),
    y_after_table,
    "End of report");

Use this pattern when a report has text before and after a table.

Complete table report

cpp
#include <rix.hpp>

int main()
{
  auto doc = rix.pdf.document();

  doc.set_title("Table Report")
      .set_author("Rix");

  auto &page = doc.add_page();

  auto y = page.heading(
      page.x_left(),
      page.y_top(),
      "Table Report",
      1);

  y -= 20.0F;

  rixlib::pdf::Table table;

  table.set_column_widths({
      160.0F,
      160.0F,
      160.0F});

  table.add_header({
      "Name",
      "Language",
      "Project"});

  table.add_row({
      "Ada",
      "C++",
      "Rix"});

  table.add_row({
      "Gaspard",
      "C++",
      "Vix.cpp"});

  table.add_row({
      "Grace",
      "Systems",
      "PDF"});

  auto y_after_table = page.table(
      page.x_left(),
      y,
      table);

  y_after_table -= 20.0F;

  page.text(
      page.x_left(),
      y_after_table,
      "Generated through the public Rix facade.");

  auto saved = rix.pdf.save(doc, "table-report.pdf");

  if (saved.failed())
  {
    rix.debug.eprint(
        "pdf error:",
        rix.pdf.error.to_string(saved.error()),
        saved.error().message());

    return 1;
  }

  rix.debug.print("created:", "table-report.pdf");
  return 0;
}

Run:

bash
vix run table.cpp

Table rows

You can create a row manually:

cpp
rixlib::pdf::TableRow row;

row.add_cell("Ada");
row.add_cell("C++");
row.add_cell("Rix");

table.add_row(std::move(row));

Use manual rows when you need more control over row-level settings.

Header rows

A row can be marked as a header:

cpp
rixlib::pdf::TableRow row;

row.set_header(true);
row.add_cell("Name");
row.add_cell("Language");
row.add_cell("Project");

table.add_row(std::move(row));

The helper:

cpp
table.add_header(...)

is shorter for normal header rows.

Table cells

You can create cells manually:

cpp
rixlib::pdf::TableCell cell{"Ready"};

cell.set_align(rixlib::pdf::Align::Center);
cell.set_text_color(rixlib::pdf::Color::green_color());

Then add it to a row:

cpp
rixlib::pdf::TableRow row;

row.add_cell(std::move(cell));

Use manual cells when you need alignment, color, background, or colspan control.

Align a cell

cpp
rixlib::pdf::TableCell cell{"C++"};

cell.set_align(rixlib::pdf::Align::Center);

Available alignments are:

txt
Left
Center
Right
Justify

In code:

cpp
rixlib::pdf::Align::Left
rixlib::pdf::Align::Center
rixlib::pdf::Align::Right
rixlib::pdf::Align::Justify

Set cell text color

cpp
rixlib::pdf::TableCell cell{"Ready"};

cell.set_text_color(
    rixlib::pdf::Color::green_color());

Use this when some values need a visual status.

Set cell background

cpp
rixlib::pdf::TableCell cell{"Header"};

cell.set_background_color(
    rixlib::pdf::Color::light_gray());

Clear it with:

cpp
cell.clear_background_color();

Use colspan

cpp
rixlib::pdf::TableCell cell{"Full row"};

cell.set_colspan(3);

Use colspan when one cell should span several columns.

Style the table

Access the table style with:

cpp
table.style()

Example:

cpp
table.style()
    .set_font(rixlib::pdf::Font::Helvetica)
    .set_font_size(10.0F)
    .set_header_font(rixlib::pdf::Font::HelveticaBold)
    .set_header_size(10.0F)
    .set_row_height(24.0F)
    .set_cell_padding(6.0F)
    .set_stripe_rows(true);

Use table styles when building reports with a repeated look.

Border style

Use BorderStyle to control table borders:

cpp
rixlib::pdf::BorderStyle border{
    0.75F,
    rixlib::pdf::Color::gray(),
    rixlib::pdf::LineStyle::Solid};

table.style().set_border(border);

Disable borders:

cpp
table.style().set_border(
    rixlib::pdf::BorderStyle::none());

Use a thin default border:

cpp
table.style().set_border(
    rixlib::pdf::BorderStyle::thin());

Stripe rows

Enable alternating row backgrounds:

cpp
table.style().set_stripe_rows(true);

Set the stripe color:

cpp
table.style().set_stripe_color(
    rixlib::pdf::Color::light_gray());

Styled table example

cpp
#include <rix.hpp>

int main()
{
  auto doc = rix.pdf.document();

  auto &page = doc.add_page();

  auto y = page.heading(
      page.x_left(),
      page.y_top(),
      "Styled table",
      1);

  y -= 20.0F;

  rixlib::pdf::Table table;

  table.set_column_widths({
      180.0F,
      160.0F,
      140.0F});

  table.style()
      .set_font(rixlib::pdf::Font::Helvetica)
      .set_font_size(10.0F)
      .set_header_font(rixlib::pdf::Font::HelveticaBold)
      .set_header_size(10.0F)
      .set_row_height(24.0F)
      .set_cell_padding(6.0F)
      .set_stripe_rows(true);

  table.add_header({
      "Name",
      "Language",
      "Status"});

  {
    rixlib::pdf::TableRow row;

    row.add_cell("Ada");
    row.add_cell("C++");

    rixlib::pdf::TableCell status{"Ready"};
    status.set_align(rixlib::pdf::Align::Center);
    status.set_text_color(rixlib::pdf::Color::green_color());

    row.add_cell(std::move(status));

    table.add_row(std::move(row));
  }

  table.add_row({
      "Gaspard",
      "C++",
      "Building"});

  page.table(
      page.x_left(),
      y,
      table);

  auto saved = rix.pdf.save(doc, "styled-table.pdf");

  if (saved.failed())
  {
    rix.debug.eprint(
        "pdf error:",
        rix.pdf.error.to_string(saved.error()),
        saved.error().message());

    return 1;
  }

  rix.debug.print("created:", "styled-table.pdf");
  return 0;
}

Run:

bash
vix run table.cpp

Table from CSV data

You can parse CSV data and render it as a PDF table.

cpp
#include <rix.hpp>

int main()
{
  const auto csv = rix.csv.parse(
      "Name,Language,Project\n"
      "Ada,C++,Rix\n"
      "Gaspard,C++,Vix.cpp\n"
      "Grace,Systems,PDF\n");

  auto doc = rix.pdf.document();

  auto &page = doc.add_page();

  auto y = page.heading(
      page.x_left(),
      page.y_top(),
      "CSV table",
      1);

  y -= 20.0F;

  rixlib::pdf::Table table;

  table.set_column_widths({
      160.0F,
      160.0F,
      160.0F});

  if (!csv.empty())
  {
    table.add_header({
        csv[0][0],
        csv[0][1],
        csv[0][2]});
  }

  for (std::size_t i = 1; i < csv.size(); ++i)
  {
    const auto &row = csv[i];

    if (row.size() >= 3)
    {
      table.add_row({
          row[0],
          row[1],
          row[2]});
    }
  }

  page.table(
      page.x_left(),
      y,
      table);

  auto saved = rix.pdf.save(doc, "csv-table.pdf");

  if (saved.failed())
  {
    rix.debug.eprint(
        "pdf error:",
        rix.pdf.error.to_string(saved.error()),
        saved.error().message());

    return 1;
  }

  rix.debug.print("created:", "csv-table.pdf");
  return 0;
}

Run:

bash
vix run table.cpp

Table size helpers

You can inspect a table:

cpp
rix.debug.print("rows:", table.row_count());
rix.debug.print("columns:", table.column_count());

Check whether the table is empty:

cpp
if (table.empty())
{
  rix.debug.print("table is empty");
}

Save the document

Use:

cpp
auto saved = rix.pdf.save(doc, "table.pdf");

Check errors:

cpp
if (saved.failed())
{
  rix.debug.eprint(
      "pdf error:",
      rix.pdf.error.to_string(saved.error()),
      saved.error().message());

  return 1;
}

Write bytes instead of saving

Use:

cpp
auto bytes = rix.pdf.write(doc);

Example:

cpp
auto bytes = rix.pdf.write(doc);

if (bytes.failed())
{
  rix.debug.eprint(
      "pdf error:",
      rix.pdf.error.to_string(bytes.error()),
      bytes.error().message());

  return 1;
}

rix.debug.print("pdf bytes:", bytes.value().size());

Use write for HTTP responses or custom output handling.

Use save for direct file output.

Use in a Vix project

Create a project:

bash
vix new rix-pdf-table --app
cd rix-pdf-table

Add Rix:

bash
vix add rix/rix
vix install

Make sure vix.app contains:

txt
deps = [
  "rix/rix",
]

A minimal vix.app can look like this:

txt
name = "rix-pdf-table"
type = "executable"
standard = "c++20"
output_dir = "bin"

sources = [
  "src/main.cpp",
]

include_dirs = [
  "include",
  "src",
]

deps = [
  "rix/rix",
]

packages = [
  "vix",
]

links = [
  "vix::vix",
]

Put the example code in:

txt
src/main.cpp

Build and run:

bash
vix build
vix run

Single-file usage

For examples, tests, and quick experiments:

bash
vix run table.cpp

If needed:

bash
vix install -g rix/rix
vix run table.cpp

For project usage, prefer:

bash
vix add rix/rix
vix install

and keep the dependency in vix.app:

txt
deps = [
  "rix/rix",
]

Use only PDF with the facade

If you want the rix.* facade style but only want PDF mounted, define the feature macro before including rix.hpp:

cpp
#define RIX_ENABLE_PDF
#include <rix.hpp>

int main()
{
  auto doc = rix.pdf.document();

  auto &page = doc.add_page();

  rixlib::pdf::Table table;

  table.set_column_widths({
      160.0F,
      160.0F});

  table.add_header({
      "Name",
      "Project"});

  table.add_row({
      "Ada",
      "Rix"});

  page.table(
      page.x_left(),
      page.y_top(),
      table);

  auto saved = rix.pdf.save(doc, "table.pdf");

  return saved.ok() ? 0 : 1;
}

When at least one RIX_ENABLE_* macro is defined, only selected modules are mounted.

If you also want debug output:

cpp
#define RIX_ENABLE_PDF
#define RIX_ENABLE_DEBUG
#include <rix.hpp>

int main()
{
  auto doc = rix.pdf.document();

  auto &page = doc.add_page();

  rixlib::pdf::Table table;

  table.set_column_widths({
      160.0F,
      160.0F});

  table.add_header({
      "Name",
      "Project"});

  table.add_row({
      "Ada",
      "Rix"});

  page.table(
      page.x_left(),
      page.y_top(),
      table);

  auto saved = rix.pdf.save(doc, "table.pdf");

  if (saved.failed())
  {
    rix.debug.eprint(
        "pdf error:",
        rix.pdf.error.to_string(saved.error()),
        saved.error().message());

    return 1;
  }

  rix.debug.print("created:", "table.pdf");
  return 0;
}

Use the independent package

For independent usage, install:

bash
vix add rix/pdf
vix install

In vix.app:

txt
deps = [
  "rix/pdf",
]

Then include:

cpp
#include <rix/pdf.hpp>

Example:

cpp
#include <rix/pdf.hpp>

int main()
{
  auto pdf = rixlib::pdf::module();

  auto doc = pdf.document();

  auto &page = doc.add_page();

  rixlib::pdf::Table table;

  table.set_column_widths({
      160.0F,
      160.0F});

  table.add_header({
      "Name",
      "Project"});

  table.add_row({
      "Ada",
      "Rix"});

  page.table(
      page.x_left(),
      page.y_top(),
      table);

  auto saved = pdf.save(doc, "table.pdf");

  return saved.ok() ? 0 : 1;
}

The examples in this documentation prefer the public facade:

cpp
#include <rix.hpp>

and:

cpp
rix.pdf

Common mistakes

Forgetting to install Rix

If rix.hpp is not found, install Rix first.

For a project:

bash
vix add rix/rix
vix install

For single-file usage:

bash
vix install -g rix/rix

Putting Rix in packages

Wrong:

txt
packages = [
  "rix/rix",
]

Correct:

txt
deps = [
  "rix/rix",
]

deps is for Vix Registry packages.

packages is for CMake package discovery.

Making columns wider than the page

The sum of column widths should fit inside:

cpp
page.content_width()

If the table is too wide, reduce column widths.

Forgetting to draw the table

Creating the table is not enough:

cpp
rixlib::pdf::Table table;

You must draw it:

cpp
page.table(
    page.x_left(),
    y,
    table);

Accessing CSV rows without checking size

Wrong:

cpp
table.add_row({
    row[0],
    row[1],
    row[2]});

Better:

cpp
if (row.size() >= 3)
{
  table.add_row({
      row[0],
      row[1],
      row[2]});
}

Not checking save errors

Wrong:

cpp
rix.pdf.save(doc, "table.pdf");

Correct:

cpp
auto saved = rix.pdf.save(doc, "table.pdf");

if (saved.failed())
{
  rix.debug.eprint(
      "pdf error:",
      rix.pdf.error.to_string(saved.error()),
      saved.error().message());

  return 1;
}

What you should remember

Create a table:

cpp
rixlib::pdf::Table table;

Set columns:

cpp
table.set_column_widths({
    160.0F,
    160.0F,
    160.0F});

Add a header:

cpp
table.add_header({
    "Name",
    "Language",
    "Project"});

Add rows:

cpp
table.add_row({
    "Ada",
    "C++",
    "Rix"});

Draw the table:

cpp
page.table(
    page.x_left(),
    y,
    table);

Save:

cpp
auto saved = rix.pdf.save(doc, "table.pdf");

For project usage:

bash
vix add rix/rix
vix install

and keep:

txt
deps = [
  "rix/rix",
]

Next step

Continue with PDF drawing.

Next: Drawing

Released under the MIT License.