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

PDF Quick Start

This page shows the fastest way to create a PDF file with rix/pdf.

The examples use the public Rix facade:

cpp
#include <rix.hpp>

and access PDF through:

cpp
rix.pdf

rix/pdf is useful when a Vix.cpp or Rix project needs to generate simple PDF documents, reports, tables, or text files from C++.

What you will build

You will create a small C++ file that:

txt
creates a PDF document
adds one page
writes text
saves the document to disk
checks save errors
runs with vix run

The output file will be:

txt
hello.pdf

Create a working folder

Create a small folder in your home directory:

bash
mkdir -p ~/rix-pdf-quick-start
cd ~/rix-pdf-quick-start
touch pdf.cpp

Add the example

Open:

txt
pdf.cpp

Add:

cpp
#include <rix.hpp>

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

  auto &page = doc.add_page();

  page.text(
      page.x_left(),
      page.y_top(),
      "Hello from rix.pdf");

  page.text(
      page.x_left(),
      page.y_top() - 30.0F,
      "This PDF was generated through the unified Rix facade.");

  auto saved = rix.pdf.save(doc, "hello.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:", "hello.pdf");
  return 0;
}

Run the file

Run:

bash
vix run pdf.cpp

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

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

Expected output

The terminal output should look like this:

txt
created: hello.pdf

The folder should now contain:

txt
pdf.cpp
hello.pdf

Open hello.pdf with your normal PDF viewer.

Create a document

The first step is to create a document:

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

A document owns pages and metadata.

It does not write itself to disk automatically.

You save it later with:

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

Add a page

Add a page with:

cpp
auto &page = doc.add_page();

The returned page is the drawing surface.

You use it to add text, paragraphs, headings, tables, drawings, and images.

Write text

Use:

cpp
page.text(x, y, value);

Example:

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Hello from rix.pdf");

page.x_left() gives the left content position inside margins.

page.y_top() gives the top content position inside margins.

Add another line

To draw another line lower on the page, subtract from the Y position:

cpp
page.text(
    page.x_left(),
    page.y_top() - 30.0F,
    "Second line");

PDF coordinates use points.

One point is 1/72 inch.

Save the document

Save with:

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

Always check the result:

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

  return 1;
}

save returns a status object.

It does not throw for normal PDF errors.

Fastest helper: make_text

For a very small text PDF, use:

cpp
auto saved = rix.pdf.make_text(
    "hello.pdf",
    "Hello from rix.pdf",
    "Rix PDF");

Complete example:

cpp
#include <rix.hpp>

int main()
{
  auto saved = rix.pdf.make_text(
      "hello.pdf",
      "Hello from rix.pdf",
      "Rix 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:", "hello.pdf");
  return 0;
}

Run:

bash
vix run pdf.cpp

Use make_text when you only need a simple text document.

Use document() when you need pages, headings, tables, drawings, images, or metadata.

Add a title and paragraph

For a more document-like PDF:

cpp
#include <rix.hpp>

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

  doc.set_title("Rix PDF Quick Start");
  doc.set_author("Rix");

  auto &page = doc.add_page();

  auto y = page.heading(
      page.x_left(),
      page.y_top(),
      "Rix PDF",
      1);

  y -= 10.0F;

  page.paragraph(
      page.x_left(),
      y,
      page.content_width(),
      "Rix PDF lets C++ applications create simple generated documents "
      "through the unified Rix facade.");

  auto saved = rix.pdf.save(doc, "quick-start.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:", "quick-start.pdf");
  return 0;
}

Run:

bash
vix run pdf.cpp

Add a small table

Tables are useful for reports.

cpp
#include <rix.hpp>

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

  doc.set_title("Rix PDF Table");

  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"});

  page.table(
      page.x_left(),
      y,
      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;
}

Run:

bash
vix run pdf.cpp

Add metadata

Metadata is written into the PDF info dictionary.

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

doc.set_title("Rix PDF Metadata")
    .set_author("Rix")
    .set_subject("PDF generation")
    .set_keywords("rix,pdf,vix,cpp");

Use metadata for generated reports and files that need a readable title or author.

Write bytes instead of saving

Use write when you want the PDF bytes in memory:

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:

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

when you want a file.

Use:

cpp
rix.pdf.write(doc);

when another part of your application will handle the bytes.

Use in a Vix project

Create a Vix application:

bash
vix new pdf-quick-start --app
cd pdf-quick-start

Add Rix:

bash
vix add rix/rix
vix install

In vix.app, make sure Rix is listed under deps:

txt
deps = [
  "rix/rix",
]

A small vix.app can look like this:

txt
name = "pdf-quick-start"
type = "executable"
standard = "c++20"
output_dir = "bin"

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

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

deps = [
  "rix/rix",
]

packages = [
  "vix",
]

links = [
  "vix::vix",
]

Then use PDF in src/main.cpp:

cpp
#include <rix.hpp>

int main()
{
  auto saved = rix.pdf.make_text(
      "hello.pdf",
      "Hello from rix.pdf",
      "Rix 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:", "hello.pdf");
  return 0;
}

Build and run:

bash
vix build
vix run

Single-file usage

For small scripts, examples, and experiments:

bash
vix run pdf.cpp

If Rix is installed globally for single-file usage:

bash
vix install -g rix/rix
vix run pdf.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 saved = rix.pdf.make_text(
      "hello.pdf",
      "Hello from rix.pdf",
      "Rix PDF");

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

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

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>

Use this style when a project only needs PDF and does not need the full unified Rix facade.

For most application documentation, prefer:

cpp
#include <rix.hpp>

Common mistakes

Forgetting to save the document

Wrong:

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

Correct:

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

Not checking errors

Wrong:

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

Better:

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

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

  return 1;
}

Saving to an empty path

This fails:

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

Use a real output path:

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

Drawing outside the visible page

This can make content invisible:

cpp
page.text(0.0F, -50.0F, "Hidden text");

Use page helpers:

cpp
page.x_left()
page.y_top()
page.content_width()

Forgetting deps

For a Vix project, do not put Rix packages in packages.

Use:

txt
deps = [
  "rix/rix",
]

packages is for CMake package discovery.

deps is for Vix Registry packages.

What you should remember

Create a document:

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

Add a page:

cpp
auto &page = doc.add_page();

Write text:

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Hello from rix.pdf");

Save:

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

Check errors:

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

Use make_text for the fastest simple PDF:

cpp
rix.pdf.make_text(
    "hello.pdf",
    "Hello from rix.pdf",
    "Rix PDF");

For a Vix project, install Rix:

bash
vix add rix/rix
vix install

and use:

txt
deps = [
  "rix/rix",
]

Next step

Learn how to create a PDF document.

Next: Create Document

Released under the MIT License.