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

Basic PDF

This example shows how to create a simple PDF file 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 the smallest complete PDF generation flow.

Create the file

bash
mkdir -p ~/rix-pdf-basic-example
cd ~/rix-pdf-basic-example
touch basic.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, "rix_pdf_basic.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_basic.pdf");
  return 0;
}

Run it:

bash
vix run basic.cpp

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

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

This creates:

txt
rix_pdf_basic.pdf

What this example does

The example creates a PDF document:

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

It adds a page:

cpp
auto &page = doc.add_page();

It writes two text lines:

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

Then it saves the file:

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

Create a document

Use:

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

A document owns pages and metadata.

It does not create a file until you call:

cpp
rix.pdf.save(...)

or generate bytes with:

cpp
rix.pdf.write(...)

Add a page

Use:

cpp
auto &page = doc.add_page();

A page is the drawing surface.

You can add text, paragraphs, headings, lines, rectangles, tables, and images.

Use margin helpers

The example uses:

cpp
page.x_left()
page.y_top()

These helpers place content inside the page margins.

Common page helpers are:

cpp
page.x_left()
page.x_right()
page.y_top()
page.y_bottom()
page.content_width()
page.content_height()

Use them instead of hardcoding positions when possible.

Draw text

Use:

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

The first argument is the X position.

The second argument is the Y position.

The third argument is the text.

Draw another line

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

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

PDF coordinates use points.

One inch is 72 points.

Save the PDF

Use:

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

save returns a status.

Always check it:

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

  return 1;
}

After a successful save:

cpp
rix.debug.print("created:", "rix_pdf_basic.pdf");

This is useful for examples and small tools.

For real Vix application logging, prefer the Vix logging system.

Complete flow

The basic PDF flow is:

txt
create document
add page
draw content
save file
check errors

In code:

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

auto &page = doc.add_page();

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

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

Add a title

You can add document metadata:

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

doc.set_title("Basic PDF")
    .set_author("Rix");

Then add visible content:

cpp
auto &page = doc.add_page();

page.text(
    page.x_left(),
    page.y_top(),
    "Basic PDF");

Metadata is not visible page content.

If you want a title visible on the page, draw it as text or heading.

Use a heading

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

heading returns the next Y position after the heading.

You can continue below it:

cpp
y -= 10.0F;

page.text(
    page.x_left(),
    y,
    "Generated with rix.pdf");

Basic PDF with heading

cpp
#include <rix.hpp>

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

  doc.set_title("Basic PDF")
      .set_author("Rix");

  auto &page = doc.add_page();

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

  y -= 10.0F;

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

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

Run:

bash
vix run basic.cpp

Write bytes instead of saving

If you want PDF bytes in memory, 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, storage adapters, or custom output handling.

Use save for writing directly to disk.

Use make_text for the shortest text PDF

For a very simple text PDF:

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

Use make_text when you only need a simple text document.

Use document() when you need more control.

Save to another folder

Create the output folder first:

bash
mkdir -p output

Then save:

cpp
auto saved = rix.pdf.save(doc, "output/basic.pdf");

If the folder does not exist, saving can fail.

Use in a Vix project

Create a project:

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

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-basic"
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 basic.cpp

If needed:

bash
vix install -g rix/rix
vix run basic.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();

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

  auto saved = rix.pdf.save(doc, "basic.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();

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

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

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

  auto saved = pdf.save(doc, "basic.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.

Forgetting to add a page

Wrong:

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

doc.set_title("Basic PDF");

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

This can still produce a blank document, but most examples should add visible content:

cpp
auto &page = doc.add_page();

Expecting metadata to be visible

This sets metadata:

cpp
doc.set_title("Basic PDF");

This draws visible content:

cpp
page.heading(
    page.x_left(),
    page.y_top(),
    "Basic PDF",
    1);

Use both when you want a document title and a visible title.

Not checking save errors

Wrong:

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

Correct:

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

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

  return 1;
}

Saving to a missing folder

This can fail:

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

Create the folder first:

bash
mkdir -p output

What you should remember

Create a document:

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

Add a page:

cpp
auto &page = doc.add_page();

Draw text:

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

Save:

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

Check errors:

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

For project usage:

bash
vix add rix/rix
vix install

and keep:

txt
deps = [
  "rix/rix",
]

Next step

Continue with PDF text.

Next: Text

Released under the MIT License.