Text
This page explains how to write text with rix/pdf.
The examples use the public Rix facade:
#include <rix.hpp>and access PDF through:
rix.pdfText 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:
mkdir -p ~/rix-pdf-text
cd ~/rix-pdf-text
touch text.cppAdd:
#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:
vix run text.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run text.cppWrite one line of text
Use:
page.text(x, y, value);Example:
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:
page.x_left()
page.x_right()
page.y_top()
page.y_bottom()
page.content_width()
page.content_height()For normal text, start with:
page.x_left()
page.y_top()Example:
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:
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:
rixlib::pdf::inches(1.0F)
rixlib::pdf::millimeters(20.0F)
rixlib::pdf::centimeters(2.0F)Example:
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:
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:
font
font size
color
line heightDefault text style
The default text style uses:
Helvetica
12pt
black
1.2 line heightSo this:
page.text(
page.x_left(),
page.y_top(),
"Default text");uses the normal text style.
Common text styles
Use:
rixlib::pdf::TextStyle::normal()
rixlib::pdf::TextStyle::heading()
rixlib::pdf::TextStyle::small()Example:
page.text(
page.x_left(),
page.y_top(),
"Small text",
rixlib::pdf::TextStyle::small());Change text size
page.text(
page.x_left(),
page.y_top(),
"Large text",
rixlib::pdf::TextStyle{
rixlib::pdf::Font::Helvetica,
18.0F});Change text color
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:
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:
auto color = rixlib::pdf::Color::from_hex(0x2C3E50);Fonts
Standard PDF fonts are available through rixlib::pdf::Font.
Common fonts:
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::CourierBoldExample:
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:
rixlib::pdf::make_font(family, style)Example:
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:
page.text_aligned(x, y, width, value, align, style);Example:
page.text_aligned(
page.x_left(),
page.y_top(),
page.content_width(),
"Centered text",
rixlib::pdf::Align::Center);Available alignment values:
rixlib::pdf::Align::Left
rixlib::pdf::Align::Center
rixlib::pdf::Align::Right
rixlib::pdf::Align::JustifyFor single-line text, use left, center, or right alignment.
For paragraphs, justify is more useful.
Left aligned text
page.text_aligned(
page.x_left(),
page.y_top(),
page.content_width(),
"Left aligned",
rixlib::pdf::Align::Left);Centered text
page.text_aligned(
page.x_left(),
page.y_top() - 30.0F,
page.content_width(),
"Centered",
rixlib::pdf::Align::Center);Right aligned text
page.text_aligned(
page.x_left(),
page.y_top() - 60.0F,
page.content_width(),
"Right aligned",
rixlib::pdf::Align::Right);Paragraphs
Use:
page.paragraph(x, y, width, value);A paragraph wraps text inside the given width.
Example:
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
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
page.paragraph(
page.x_left(),
page.y_top(),
page.content_width(),
"This paragraph is centered.",
rixlib::pdf::Align::Center);Styled paragraph
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:
page.heading(x, y, value, level);Example:
auto y = page.heading(
page.x_left(),
page.y_top(),
"Rix PDF",
1);The heading function returns the next Y position.
Continue below it:
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:
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
page.heading(
page.x_left(),
page.y_top(),
"Blue heading",
1,
rixlib::pdf::Color::blue_color());Page numbers
Use:
page.page_number(number, total);Example:
page.page_number(1, 3);You can set a custom Y position:
page.page_number(
1,
3,
page.y_bottom() - 20.0F);You can also pass a text style:
page.page_number(
1,
3,
page.y_bottom() - 20.0F,
rixlib::pdf::TextStyle::small());Complete text document
#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:
vix run text.cppMultiple text sections
#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:
auto saved = rix.pdf.make_text(
"hello.pdf",
"Hello from rix.pdf",
"Rix PDF");Complete example:
#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:
auto saved = rix.pdf.save(doc, "text.pdf");Always check errors:
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 1;
}Write PDF bytes
Use:
auto bytes = rix.pdf.write(doc);Example:
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:
vix new pdf-text --app
cd pdf-textAdd Rix:
vix add rix/rix
vix installIn vix.app, make sure Rix is listed under deps:
deps = [
"rix/rix",
]A small vix.app can look like this:
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:
#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:
vix build
vix runSingle-file usage
For small scripts, examples, and experiments:
vix run text.cppIf Rix is installed globally for single-file usage:
vix install -g rix/rix
vix run text.cppFor project usage, prefer:
vix add rix/rix
vix installand keep the dependency in vix.app:
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:
#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:
vix add rix/pdf
vix installIn vix.app:
deps = [
"rix/pdf",
]Then include:
#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:
#include <rix.hpp>Common mistakes
Forgetting to add a page
Wrong:
auto doc = rix.pdf.document();
page.text(
page.x_left(),
page.y_top(),
"Hello");Correct:
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:
page.text(0.0F, -50.0F, "Hidden text");Use page helpers:
page.x_left()
page.y_top()
page.y_bottom()Forgetting to update Y
This draws both lines on top of each other:
page.text(page.x_left(), page.y_top(), "First");
page.text(page.x_left(), page.y_top(), "Second");Move the second line down:
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:
rix.pdf.save(doc, "text.pdf");Better:
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:
packages = [
"rix/rix",
]Correct:
deps = [
"rix/rix",
]deps is for Vix Registry packages.
packages is for CMake package discovery.
What you should remember
Create a document:
auto doc = rix.pdf.document();Add a page:
auto &page = doc.add_page();Write text:
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");Write a paragraph:
page.paragraph(
page.x_left(),
y,
page.content_width(),
"Long text here.");Write a heading:
auto y = page.heading(
page.x_left(),
page.y_top(),
"Title",
1);Use style:
rixlib::pdf::TextStyle{
rixlib::pdf::Font::Helvetica,
12.0F,
rixlib::pdf::Color::blue_color()}Save:
auto saved = rix.pdf.save(doc, "text.pdf");For a Vix project, install Rix:
vix add rix/rix
vix installand use:
deps = [
"rix/rix",
]Next step
Learn how to work with tables.
Next: Tables