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

Text

This page explains how to write text with rix/pdf.

The examples use the public Rix facade:

cpp
#include <rix.hpp>

and access PDF through:

cpp
rix.pdf

Text is written on a page. A page provides helpers for normal text, aligned text, paragraphs, headings, and page numbers.

Basic text example

Create a file:

bash
mkdir -p ~/rix-pdf-text
cd ~/rix-pdf-text
touch text.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 is a second line of text.");

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

Run it:

bash
vix run text.cpp

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

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

Write one line of text

Use:

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

Example:

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 value.

Use page helper positions

A page provides useful layout helpers:

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

For normal text, start with:

cpp
page.x_left()
page.y_top()

Example:

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Top-left content text");

Move text down

To draw another line below, 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.

Use units

Instead of raw numbers, you can use unit helpers:

cpp
rixlib::pdf::inches(1.0F)
rixlib::pdf::millimeters(20.0F)
rixlib::pdf::centimeters(2.0F)

Example:

cpp
page.text(
    rixlib::pdf::inches(1.0F),
    rixlib::pdf::inches(10.0F),
    "Text positioned with inch units");

Text style

Use rixlib::pdf::TextStyle to control the font, size, color, and line height.

Example:

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Blue text",
    rixlib::pdf::TextStyle{
        rixlib::pdf::Font::Helvetica,
        12.0F,
        rixlib::pdf::Color::blue_color()});

The constructor accepts:

txt
font
font size
color
line height

Default text style

The default text style uses:

txt
Helvetica
12pt
black
1.2 line height

So this:

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Default text");

uses the normal text style.

Common text styles

Use:

cpp
rixlib::pdf::TextStyle::normal()
rixlib::pdf::TextStyle::heading()
rixlib::pdf::TextStyle::small()

Example:

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Small text",
    rixlib::pdf::TextStyle::small());

Change text size

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Large text",
    rixlib::pdf::TextStyle{
        rixlib::pdf::Font::Helvetica,
        18.0F});

Change text color

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Red text",
    rixlib::pdf::TextStyle{
        rixlib::pdf::Font::Helvetica,
        12.0F,
        rixlib::pdf::Color::red_color()});

Common colors:

cpp
rixlib::pdf::Color::black()
rixlib::pdf::Color::white()
rixlib::pdf::Color::red_color()
rixlib::pdf::Color::green_color()
rixlib::pdf::Color::blue_color()
rixlib::pdf::Color::gray()
rixlib::pdf::Color::light_gray()

Create a color from hexadecimal RGB:

cpp
auto color = rixlib::pdf::Color::from_hex(0x2C3E50);

Fonts

Standard PDF fonts are available through rixlib::pdf::Font.

Common fonts:

cpp
rixlib::pdf::Font::Helvetica
rixlib::pdf::Font::HelveticaBold
rixlib::pdf::Font::HelveticaOblique
rixlib::pdf::Font::Times
rixlib::pdf::Font::TimesBold
rixlib::pdf::Font::Courier
rixlib::pdf::Font::CourierBold

Example:

cpp
page.text(
    page.x_left(),
    page.y_top(),
    "Bold heading text",
    rixlib::pdf::TextStyle{
        rixlib::pdf::Font::HelveticaBold,
        18.0F});

Build a font from family and style

Use:

cpp
rixlib::pdf::make_font(family, style)

Example:

cpp
auto font = rixlib::pdf::make_font(
    rixlib::pdf::FontFamily::Helvetica,
    rixlib::pdf::FontStyle::Bold);

page.text(
    page.x_left(),
    page.y_top(),
    "Bold text",
    rixlib::pdf::TextStyle{
        font,
        14.0F});

Aligned text

Use:

cpp
page.text_aligned(x, y, width, value, align, style);

Example:

cpp
page.text_aligned(
    page.x_left(),
    page.y_top(),
    page.content_width(),
    "Centered text",
    rixlib::pdf::Align::Center);

Available alignment values:

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

For single-line text, use left, center, or right alignment.

For paragraphs, justify is more useful.

Left aligned text

cpp
page.text_aligned(
    page.x_left(),
    page.y_top(),
    page.content_width(),
    "Left aligned",
    rixlib::pdf::Align::Left);

Centered text

cpp
page.text_aligned(
    page.x_left(),
    page.y_top() - 30.0F,
    page.content_width(),
    "Centered",
    rixlib::pdf::Align::Center);

Right aligned text

cpp
page.text_aligned(
    page.x_left(),
    page.y_top() - 60.0F,
    page.content_width(),
    "Right aligned",
    rixlib::pdf::Align::Right);

Paragraphs

Use:

cpp
page.paragraph(x, y, width, value);

A paragraph wraps text inside the given width.

Example:

cpp
auto y = page.y_top();

y = page.paragraph(
    page.x_left(),
    y,
    page.content_width(),
    "Rix PDF can wrap longer text into multiple lines. "
    "This is useful for reports, generated documents, and simple text files.");

paragraph returns the next Y position after the paragraph.

Use the returned value to continue writing below.

Paragraph with spacing

cpp
auto y = page.y_top();

y = page.paragraph(
    page.x_left(),
    y,
    page.content_width(),
    "First paragraph.");

y -= 15.0F;

y = page.paragraph(
    page.x_left(),
    y,
    page.content_width(),
    "Second paragraph.");

Centered paragraph

cpp
page.paragraph(
    page.x_left(),
    page.y_top(),
    page.content_width(),
    "This paragraph is centered.",
    rixlib::pdf::Align::Center);

Styled paragraph

cpp
page.paragraph(
    page.x_left(),
    page.y_top(),
    page.content_width(),
    "This paragraph uses blue text.",
    rixlib::pdf::Align::Left,
    rixlib::pdf::TextStyle{
        rixlib::pdf::Font::Helvetica,
        12.0F,
        rixlib::pdf::Color::blue_color()});

Headings

Use:

cpp
page.heading(x, y, value, level);

Example:

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

The heading function returns the next Y position.

Continue below it:

cpp
y -= 10.0F;

page.paragraph(
    page.x_left(),
    y,
    page.content_width(),
    "The document starts below the heading.");

Heading levels

Heading levels go from 1 to 6:

cpp
page.heading(page.x_left(), y, "Heading 1", 1);
page.heading(page.x_left(), y, "Heading 2", 2);
page.heading(page.x_left(), y, "Heading 3", 3);

Larger heading levels use smaller visual sizes.

Heading color

cpp
page.heading(
    page.x_left(),
    page.y_top(),
    "Blue heading",
    1,
    rixlib::pdf::Color::blue_color());

Page numbers

Use:

cpp
page.page_number(number, total);

Example:

cpp
page.page_number(1, 3);

You can set a custom Y position:

cpp
page.page_number(
    1,
    3,
    page.y_bottom() - 20.0F);

You can also pass a text style:

cpp
page.page_number(
    1,
    3,
    page.y_bottom() - 20.0F,
    rixlib::pdf::TextStyle::small());

Complete text document

cpp
#include <rix.hpp>

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

  doc.set_title("Rix PDF Text Example");
  doc.set_author("Rix");

  auto &page = doc.add_page();

  auto y = page.y_top();

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

  y -= 10.0F;

  y = page.paragraph(
      page.x_left(),
      y,
      page.content_width(),
      "Rix gives Vix C++ projects a unified userland facade. "
      "The PDF module keeps the public API simple while the writer internals stay hidden.");

  y -= 20.0F;

  page.paragraph(
      page.x_left(),
      y,
      page.content_width(),
      "This paragraph is centered to show text alignment.",
      rixlib::pdf::Align::Center,
      rixlib::pdf::TextStyle{
          rixlib::pdf::Font::Helvetica,
          12.0F,
          rixlib::pdf::Color::blue_color()});

  page.page_number(1, 1);

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

Run:

bash
vix run text.cpp

Multiple text sections

cpp
#include <rix.hpp>

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

  auto &page = doc.add_page();

  auto y = page.y_top();

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

  y -= 15.0F;

  y = page.paragraph(
      page.x_left(),
      y,
      page.content_width(),
      "This is the introduction section.");

  y -= 20.0F;

  y = page.heading(
      page.x_left(),
      y,
      "Details",
      2);

  y -= 10.0F;

  page.paragraph(
      page.x_left(),
      y,
      page.content_width(),
      "This is the details section.");

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

Use with make_text

For a very simple text-only 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",
      "This file was generated with the high-level rix.pdf.make_text helper.",
      "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 for the fastest simple text document.

Use document() when you need more control.

Save the PDF

Use:

cpp
auto saved = rix.pdf.save(doc, "text.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 PDF bytes

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 when you want PDF bytes in memory.

Use save when you want to write a PDF file directly.

Use in a Vix project

Create a Vix application:

bash
vix new pdf-text --app
cd pdf-text

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-text"
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 text in src/main.cpp:

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

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

Build and run:

bash
vix build
vix run

Single-file usage

For small scripts, examples, and experiments:

bash
vix run text.cpp

If Rix is installed globally for single-file usage:

bash
vix install -g rix/rix
vix run text.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, "text.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(
    page.x_left(),
    page.y_top(),
    "Hello");

Correct:

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

auto &page = doc.add_page();

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

Drawing text outside the visible page

Wrong:

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

Use page helpers:

cpp
page.x_left()
page.y_top()
page.y_bottom()

Forgetting to update Y

This draws both lines on top of each other:

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

Move the second line down:

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

Not checking save errors

Wrong:

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

Better:

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

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

  return 1;
}

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();

Add a page:

cpp
auto &page = doc.add_page();

Write text:

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

Write a paragraph:

cpp
page.paragraph(
    page.x_left(),
    y,
    page.content_width(),
    "Long text here.");

Write a heading:

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

Use style:

cpp
rixlib::pdf::TextStyle{
    rixlib::pdf::Font::Helvetica,
    12.0F,
    rixlib::pdf::Color::blue_color()}

Save:

cpp
auto saved = rix.pdf.save(doc, "text.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 work with tables.

Next: Tables

Released under the MIT License.