PDF
rix/pdf is the Rix package for creating simple PDF documents from C++.
It provides a small document API for pages, text, paragraphs, headings, tables, drawing primitives, metadata, images, and PDF writing.
The examples on this page use the public Rix facade:
#include <rix.hpp>and access PDF through:
rix.pdfWhat rix/pdf provides
rix/pdf provides application-level PDF generation for Vix.cpp and Rix projects.
It can be used to create:
simple PDF files
text documents
reports
tables
generated documents
metadata-enabled PDF files
basic drawings
documents with JPEG imagesThe goal is to keep common PDF generation simple while hiding low-level PDF writer details.
Basic example
Create a file:
mkdir -p ~/rix-pdf-example
cd ~/rix-pdf-example
touch pdf.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 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:
vix run pdf.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run pdf.cppThis creates:
rix_pdf_basic.pdfInstall
For normal usage through the unified facade, install:
vix add rix/rix
vix installIn vix.app, declare:
deps = [
"rix/rix",
]If you want to use only the independent PDF package, install:
vix add rix/pdf
vix installand declare:
deps = [
"rix/pdf",
]For most documentation examples, prefer the facade package:
#include <rix.hpp>Create a document
Use:
auto doc = rix.pdf.document();This creates a PDF document with default page settings.
Then add a page:
auto &page = doc.add_page();A document owns its pages and metadata.
The PDF writer serializes the document when you call save or write.
Add text
Use:
page.text(x, y, value);Example:
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");x_left() and y_top() are convenient positions inside the page margins.
Add a heading
Use:
auto y = page.heading(
page.x_left(),
page.y_top(),
"Rix PDF",
1);The heading function returns the next Y position after the heading.
You can continue drawing below it:
y -= 10.0F;
page.text(
page.x_left(),
y,
"Document content starts here.");Add a paragraph
Use:
page.paragraph(
page.x_left(),
y,
page.content_width(),
"Long text goes here.");A paragraph wraps text inside the provided width.
Example:
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 gives Vix C++ projects a unified userland facade. "
"The PDF module keeps the public API simple while the writer internals stay hidden.");Text alignment
Use rixlib::pdf::Align when drawing aligned text or paragraphs:
page.paragraph(
page.x_left(),
y,
page.content_width(),
"This paragraph is centered.",
rixlib::pdf::Align::Center);Available alignment values:
rixlib::pdf::Align::Left
rixlib::pdf::Align::Center
rixlib::pdf::Align::Right
rixlib::pdf::Align::JustifyText style
Use rixlib::pdf::TextStyle to control font, size, color, and line height.
Example:
page.paragraph(
page.x_left(),
y,
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()});Common helpers:
rixlib::pdf::TextStyle::normal()
rixlib::pdf::TextStyle::heading()
rixlib::pdf::TextStyle::small()Colors
Use rixlib::pdf::Color for text, shapes, and table styles.
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);Page size and margins
Create a document with custom page settings:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::one_inch());Common page sizes:
rixlib::pdf::PageSize::A4()
rixlib::pdf::PageSize::A3()
rixlib::pdf::PageSize::Letter()
rixlib::pdf::PageSize::Legal()Create custom sizes:
auto size = rixlib::pdf::PageSize::from_inches(8.5F, 11.0F);Margins:
rixlib::pdf::Margins::one_inch()
rixlib::pdf::Margins::none()
rixlib::pdf::Margins::from_inches(1.0F, 1.0F, 1.0F, 1.0F)
rixlib::pdf::Margins::from_millimeters(20.0F, 20.0F, 20.0F, 20.0F)Units
PDF coordinates use points.
One point is 1/72 inch.
Helpers:
rixlib::pdf::inches(1.0F)
rixlib::pdf::millimeters(10.0F)
rixlib::pdf::centimeters(2.0F)Example:
page.text(
rixlib::pdf::inches(1.0F),
rixlib::pdf::inches(10.0F),
"Positioned with inch units");Add a table
Use rixlib::pdf::Table.
Example:
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);Tables are useful for generated reports and simple tabular documents.
Draw shapes
A page can draw simple primitives:
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.5F,
rixlib::pdf::Color::blue_color());
page.rect(
page.x_left(),
y - 70.0F,
140.0F,
50.0F);
page.fill_rect(
page.x_left() + 170.0F,
y - 70.0F,
140.0F,
50.0F,
rixlib::pdf::Color::light_gray());
page.circle(
page.x_left() + 70.0F,
y - 160.0F,
35.0F,
1.0F,
rixlib::pdf::Color::red_color());Useful drawing methods:
line
rect
fill_rect
fill_stroke_rect
circle
hruleAdd metadata
Document metadata can be set through chainable helpers:
auto doc = rix.pdf.document();
doc.set_title("Rix PDF Metadata Example")
.set_author("Rix")
.set_subject("PDF metadata")
.set_keywords("rix,pdf,vix,cpp");Metadata can include:
title
author
subject
creator
keywordsSave a PDF
Use:
auto saved = rix.pdf.save(doc, "output.pdf");Check the result:
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, not an exception.
Write PDF bytes
Use:
auto bytes = rix.pdf.write(doc);Example:
auto data = rix.pdf.write(doc);
if (data.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(data.error()),
data.error().message());
return 1;
}
rix.debug.print("pdf bytes:", data.value().size());Use write when you want the PDF content as a string of bytes.
Use save when you want to write directly to a file.
High-level text helper
Use:
auto saved = rix.pdf.make_text(
"hello.pdf",
"Hello from rix.pdf",
"Rix PDF");This creates a simple text PDF with an optional title.
It is the fastest way to generate a small document.
Error handling
PDF operations return explicit result or status objects.
Common pattern:
auto saved = rix.pdf.save(doc, "output.pdf");
if (saved.failed())
{
const auto &error = saved.error();
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
return 1;
}PDF errors have stable codes and messages.
Use the code for programmatic decisions.
Use the message for diagnostics.
PDF error helpers
Error helpers are available through:
rix.pdf.errorExample:
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
}Useful helpers:
rix.pdf.error.to_string(error)
rix.pdf.error.ok(error)
rix.pdf.error.failed(error)
rix.pdf.error.is(error, code)JPEG images
rix/pdf can load JPEG images:
auto image = rix.pdf.image.load_jpeg("photo.jpg");
if (image.failed())
{
rix.debug.eprint(
"image error:",
rix.pdf.error.to_string(image.error()),
image.error().message());
return 1;
}
page.image_fit(
image.value(),
page.x_left(),
page.y_top() - 250.0F,
300.0F,
200.0F);The first implementation supports JPEG images.
Complete text document example
#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()});
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 it:
vix run pdf.cppComplete table example
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
doc.set_title("Rix PDF Table Example");
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"});
table.add_row({
"Grace",
"Systems",
"PDF"});
page.table(
page.x_left(),
y,
table);
auto saved = rix.pdf.save(doc, "rix_pdf_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:", "rix_pdf_table.pdf");
return 0;
}Use in a Vix project
Create a Vix application:
vix new pdf-app --app
cd pdf-appAdd 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-app"
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:
#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:
vix build
vix runSingle-file usage
For small scripts, examples, and experiments:
vix run pdf.cppIf Rix is installed globally for single-file usage:
vix install -g rix/rix
vix run pdf.cppFor project usage, prefer:
vix add rix/rix
vix installand keep the dependency in vix.app:
deps = [
"rix/rix",
]Facade usage
The recommended documentation style is:
#include <rix.hpp>Then:
auto doc = rix.pdf.document();
auto saved = rix.pdf.save(doc, "output.pdf");This keeps PDF available through the same public object as the other Rix packages:
rix.csv
rix.debug
rix.auth
rix.pdfIndependent package usage
If your project only needs PDF, install:
vix add rix/pdf
vix installIn vix.app:
deps = [
"rix/pdf",
]Then include:
#include <rix/pdf.hpp>Use this style when you do not need the unified rix.* facade.
For most application documentation, prefer:
#include <rix.hpp>Lightweight facade usage
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 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.
Common mistakes
Forgetting to save the document
Creating a document is not enough:
auto doc = rix.pdf.document();
doc.add_page();Save it:
auto saved = rix.pdf.save(doc, "output.pdf");Not checking save errors
Wrong:
rix.pdf.save(doc, "output.pdf");Better:
auto saved = rix.pdf.save(doc, "output.pdf");
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 1;
}Using an empty output path
This fails:
rix.pdf.save(doc, "");Use a valid path:
rix.pdf.save(doc, "output.pdf");Confusing page coordinates
PDF coordinates use points.
Use helpers when you want readable units:
rixlib::pdf::inches(1.0F)
rixlib::pdf::millimeters(20.0F)Forgetting deps
For a Vix project, do not put Rix packages in packages.
Use:
deps = [
"rix/rix",
]packages is for CMake package discovery.
deps is for Vix Registry packages.
What you should remember
Use the facade:
#include <rix.hpp>Create a document:
auto doc = rix.pdf.document();Add a page:
auto &page = doc.add_page();Add text:
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");Save the document:
auto saved = rix.pdf.save(doc, "output.pdf");Check errors:
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:
rix.pdf.make_text(
"hello.pdf",
"Hello from rix.pdf",
"Rix PDF");For a Vix project, install Rix:
vix add rix/rix
vix installand use:
deps = [
"rix/rix",
]Next step
Learn the fastest PDF workflow.
Next: Quick Start