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

Document

This page explains how to create and manage a PDF document with rix/pdf.

The examples use the public Rix facade:

cpp
#include <rix.hpp>

and access PDF through:

cpp
rix.pdf

A PDF document is the root object. It owns pages and metadata. Pages contain the visible content.

Basic document

Create a file:

bash
mkdir -p ~/rix-pdf-document
cd ~/rix-pdf-document
touch document.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 a Rix PDF document");

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

Run it:

bash
vix run document.cpp

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

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

Create a document

Use:

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

This creates a document with default settings:

txt
page size: A4
margins: default margins
pages: none
metadata: empty user metadata

A document does not automatically create a page.

You add pages explicitly:

cpp
auto &page = doc.add_page();

Create a document with page settings

You can create a document with a default page size and margins:

cpp
auto doc = rix.pdf.document(
    rixlib::pdf::PageSize::A4(),
    rixlib::pdf::Margins::one_inch());

Every page created with add_page() uses these defaults.

cpp
auto &page = doc.add_page();

Page size

Page sizes use PDF points.

One point is 1/72 inch.

Common page sizes:

cpp
rixlib::pdf::PageSize::A4()
rixlib::pdf::PageSize::A3()
rixlib::pdf::PageSize::Letter()
rixlib::pdf::PageSize::Legal()

Example:

cpp
auto doc = rix.pdf.document(
    rixlib::pdf::PageSize::Letter());

Landscape pages

Use as_landscape() when you want a landscape page size:

cpp
auto doc = rix.pdf.document(
    rixlib::pdf::PageSize::A4().as_landscape());

Use as_portrait() when you want portrait:

cpp
auto size = rixlib::pdf::PageSize::A4().as_portrait();

Custom page size

Create a custom page size with points:

cpp
auto size = rixlib::pdf::PageSize::custom(
    500.0F,
    700.0F);

Or use inch helpers:

cpp
auto size = rixlib::pdf::PageSize::from_inches(
    8.5F,
    11.0F);

Or millimeter helpers:

cpp
auto size = rixlib::pdf::PageSize::from_millimeters(
    210.0F,
    297.0F);

Margins

Use default margins:

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

Use one-inch margins:

cpp
auto doc = rix.pdf.document(
    rixlib::pdf::PageSize::A4(),
    rixlib::pdf::Margins::one_inch());

Use no margins:

cpp
auto doc = rix.pdf.document(
    rixlib::pdf::PageSize::A4(),
    rixlib::pdf::Margins::none());

Use custom margins in inches:

cpp
auto margins = rixlib::pdf::Margins::from_inches(
    1.0F,
    1.0F,
    0.75F,
    0.75F);

The order is:

txt
top
bottom
left
right

Use custom margins in millimeters:

cpp
auto margins = rixlib::pdf::Margins::from_millimeters(
    20.0F,
    20.0F,
    20.0F,
    20.0F);

Add a page

Use:

cpp
auto &page = doc.add_page();

This uses the document default page size and margins.

Example:

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

auto &page = doc.add_page();

page.text(
    page.x_left(),
    page.y_top(),
    "First page");

Add a page with custom size

Use:

cpp
auto &page = doc.add_page(
    rixlib::pdf::PageSize::Letter());

This page uses the custom size and the document default margins.

Add a page with custom size and margins

Use:

cpp
auto &page = doc.add_page(
    rixlib::pdf::PageSize::A4(),
    rixlib::pdf::Margins::from_millimeters(
        20.0F,
        20.0F,
        20.0F,
        20.0F));

This is useful when one page needs different layout rules.

Multiple pages

Add several pages:

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

auto &first = doc.add_page();

first.text(
    first.x_left(),
    first.y_top(),
    "First page");

auto &second = doc.add_page();

second.text(
    second.x_left(),
    second.y_top(),
    "Second page");

Save:

cpp
auto saved = rix.pdf.save(doc, "multi-page.pdf");

Page count

Use:

cpp
doc.page_count()

Example:

cpp
rix.debug.print("pages:", doc.page_count());

Check whether the document has no pages:

cpp
if (doc.empty())
{
  rix.debug.print("document has no pages");
}

Access pages

Access a page by index:

cpp
auto &page = doc.page(0);

Indexes are zero-based.

Use this only when you know the page exists.

Example:

cpp
if (doc.page_count() > 0)
{
  auto &page = doc.page(0);

  page.text(
      page.x_left(),
      page.y_top() - 40.0F,
      "Added later");
}

Clear pages

Remove all pages:

cpp
doc.clear_pages();

After this:

txt
doc.page_count() == 0
doc.empty() == true

Metadata

A document stores metadata.

Common metadata fields:

txt
title
author
subject
creator
keywords

Set metadata with chainable helpers:

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

You can also set creator:

cpp
doc.set_creator("my-app");

If the creator is empty, it is normalized back to the default creator.

Metadata example

cpp
#include <rix.hpp>

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

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

  auto &page = doc.add_page();

  page.heading(
      page.x_left(),
      page.y_top(),
      "Document metadata",
      1);

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

Run:

bash
vix run document.cpp

Read metadata

Access metadata:

cpp
const auto &metadata = doc.metadata();

rix.debug.print("title:", metadata.title());
rix.debug.print("author:", metadata.author());

Mutable access is also available:

cpp
doc.metadata().set_title("Updated title");

The chainable helpers are usually clearer:

cpp
doc.set_title("Updated title");

Default page settings

Read the default page size:

cpp
const auto &size = doc.default_page_size();

rix.debug.print("width:", size.width());
rix.debug.print("height:", size.height());

Read the default margins:

cpp
const auto &margins = doc.default_margins();

rix.debug.print("left:", margins.left());
rix.debug.print("right:", margins.right());

Update defaults:

cpp
doc.set_default_page_size(
    rixlib::pdf::PageSize::Letter());

doc.set_default_margins(
    rixlib::pdf::Margins::one_inch());

Pages added after the update use the new defaults.

Complete multi-page example

cpp
#include <rix.hpp>

int main()
{
  auto doc = rix.pdf.document(
      rixlib::pdf::PageSize::A4(),
      rixlib::pdf::Margins::one_inch());

  doc.set_title("Multi-page document")
      .set_author("Rix");

  auto &first = doc.add_page();

  first.heading(
      first.x_left(),
      first.y_top(),
      "First page",
      1);

  first.paragraph(
      first.x_left(),
      first.y_top() - 50.0F,
      first.content_width(),
      "This is the first page of a generated PDF document.");

  auto &second = doc.add_page(
      rixlib::pdf::PageSize::A4().as_landscape());

  second.heading(
      second.x_left(),
      second.y_top(),
      "Second page",
      1);

  second.paragraph(
      second.x_left(),
      second.y_top() - 50.0F,
      second.content_width(),
      "This page uses a landscape A4 page size.");

  auto saved = rix.pdf.save(doc, "multi-page.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:", "multi-page.pdf");
  rix.debug.print("pages:", doc.page_count());

  return 0;
}

Run:

bash
vix run document.cpp

Save the document

Use:

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

Always check errors:

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

  return 1;
}

Write the document to 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("bytes:", bytes.value().size());

Use write when you need the generated PDF bytes instead of a file.

Empty documents

If a document has no pages, the writer can still generate a valid PDF by adding a blank default page during serialization.

Even so, it is clearer to add pages explicitly:

cpp
auto &page = doc.add_page();

Do this when the output should contain visible content.

Use in a Vix project

Create a Vix application:

bash
vix new pdf-document --app
cd pdf-document

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-document"
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 doc = rix.pdf.document();

  doc.set_title("Document example");

  auto &page = doc.add_page();

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

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

Build and run:

bash
vix build
vix run

Single-file usage

For small scripts, examples, and experiments:

bash
vix run document.cpp

If Rix is installed globally for single-file usage:

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

  return rix.pdf.save(doc, "document.pdf").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 add a page

Wrong:

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

page.text(0.0F, 0.0F, "Hello");

Correct:

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

auto &page = doc.add_page();

page.text(
    page.x_left(),
    page.y_top(),
    "Hello");

Forgetting to save the document

Creating pages does not write a file.

Wrong:

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

Correct:

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

Not checking save errors

Wrong:

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

Better:

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

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

  return 1;
}

Using invalid page indexes

Wrong:

cpp
auto &page = doc.page(0);

when the document has no pages.

Better:

cpp
if (doc.page_count() > 0)
{
  auto &page = doc.page(0);
}

Confusing deps and packages

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

Wrong:

txt
packages = [
  "rix/rix",
]

Correct:

txt
deps = [
  "rix/rix",
]

deps is for Vix Registry packages.

packages is for CMake package discovery.

What you should remember

Create a document:

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

Create a document with defaults:

cpp
auto doc = rix.pdf.document(
    rixlib::pdf::PageSize::A4(),
    rixlib::pdf::Margins::one_inch());

Add a page:

cpp
auto &page = doc.add_page();

Add metadata:

cpp
doc.set_title("Title")
    .set_author("Rix");

Save:

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

Check errors:

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

For a Vix project, install Rix:

bash
vix add rix/rix
vix install

and use:

txt
deps = [
  "rix/rix",
]

Next step

Learn how to work with pages.

Next: Page

Released under the MIT License.