PDF API
This page documents the public rix/pdf API.
Use PDF through the unified Rix facade:
#include <rix.hpp>Then access PDF with:
rix.pdfThe PDF API provides helpers for:
creating documents
adding pages
drawing text
drawing paragraphs
drawing tables
drawing lines and shapes
adding metadata
loading JPEG images
writing PDF bytes
saving PDF files
handling PDF errorsPackage
The PDF package is:
rix/pdfFor facade usage, install:
vix add rix/rix
vix installIn vix.app:
deps = [
"rix/rix",
]For independent package usage, install:
vix add rix/pdf
vix installIn vix.app:
deps = [
"rix/pdf",
]Header
Facade usage:
#include <rix.hpp>Independent usage:
#include <rix/pdf.hpp>Most application examples should use the facade.
Facade member
The PDF facade member is:
rix.pdfExample:
#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, "hello.pdf");
return saved.ok() ? 0 : 1;
}Main operations
Common PDF operations are:
rix.pdf.document()
rix.pdf.save(...)
rix.pdf.write(...)
rix.pdf.make_text(...)
rix.pdf.image.load_jpeg(...)
rix.pdf.error.to_string(...)Use document when you need layout control.
Use make_text when you only need a simple text PDF.
Use write when you need PDF bytes in memory.
Use save when you want to write a file.
Create a document
Use:
auto doc = rix.pdf.document();A document owns pages and metadata.
It does not create a file until you call:
rix.pdf.save(doc, "output.pdf");or:
rix.pdf.write(doc);Create a document with page settings
Use:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::one_inch());You can also use:
rixlib::pdf::PageSize::A3()
rixlib::pdf::PageSize::A4()
rixlib::pdf::PageSize::Letter()
rixlib::pdf::PageSize::Legal()
rixlib::pdf::PageSize::custom(...)Margins can be created with:
rixlib::pdf::Margins::one_inch()
rixlib::pdf::Margins::none()
rixlib::pdf::Margins::from_inches(...)
rixlib::pdf::Margins::from_millimeters(...)Add a page
Use:
auto &page = doc.add_page();A page is the drawing surface.
You can draw text, paragraphs, headings, lines, rectangles, circles, images, and tables.
Add a custom page
auto &page = doc.add_page(
rixlib::pdf::PageSize::Letter());With custom margins:
auto &page = doc.add_page(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::from_inches(
1.0F,
1.0F,
1.0F,
1.0F));Page helpers
Common page helpers are:
page.width()
page.height()
page.content_width()
page.content_height()
page.x_left()
page.x_right()
page.y_top()
page.y_bottom()Use them to keep content inside page margins.
Example:
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");Draw text
Use:
page.text(
x,
y,
"Text");Example:
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");With style:
page.text(
page.x_left(),
page.y_top(),
"Styled text",
rixlib::pdf::TextStyle{
rixlib::pdf::Font::HelveticaBold,
14.0F,
rixlib::pdf::Color::blue_color()});Draw aligned text
Use:
page.text_aligned(
page.x_left(),
page.y_top(),
page.content_width(),
"Centered text",
rixlib::pdf::Align::Center);Available alignments:
rixlib::pdf::Align::Left
rixlib::pdf::Align::Center
rixlib::pdf::Align::Right
rixlib::pdf::Align::JustifyDraw a heading
Use:
auto y = page.heading(
page.x_left(),
page.y_top(),
"Report",
1);The last argument is the heading level.
Common levels are:
1
2
3
4
5
6heading returns the next Y position after the heading.
Draw a paragraph
Use:
auto y = page.paragraph(
page.x_left(),
page.y_top(),
page.content_width(),
"This paragraph will wrap inside the available width.");With alignment and style:
auto y = page.paragraph(
page.x_left(),
page.y_top(),
page.content_width(),
"Centered blue paragraph.",
rixlib::pdf::Align::Center,
rixlib::pdf::TextStyle{
rixlib::pdf::Font::Helvetica,
12.0F,
rixlib::pdf::Color::blue_color()});paragraph returns the Y position after the paragraph.
Text styles
Use:
rixlib::pdf::TextStyleCommon helpers:
rixlib::pdf::TextStyle::normal()
rixlib::pdf::TextStyle::heading()
rixlib::pdf::TextStyle::small()Example:
auto style = rixlib::pdf::TextStyle{
rixlib::pdf::Font::HelveticaBold,
16.0F,
rixlib::pdf::Color::black()};A text style controls:
font
size
color
line heightFonts
Standard PDF fonts are available through:
rixlib::pdf::FontExamples:
rixlib::pdf::Font::Helvetica
rixlib::pdf::Font::HelveticaBold
rixlib::pdf::Font::HelveticaOblique
rixlib::pdf::Font::Times
rixlib::pdf::Font::Courier
rixlib::pdf::Font::Symbol
rixlib::pdf::Font::ZapfDingbatsYou can also create a font from family and style:
auto font = rixlib::pdf::make_font(
rixlib::pdf::FontFamily::Helvetica,
rixlib::pdf::FontStyle::Bold);Colors
Use:
rixlib::pdf::ColorCommon colors:
rixlib::pdf::Color::black()
rixlib::pdf::Color::white()
rixlib::pdf::Color::gray()
rixlib::pdf::Color::light_gray()
rixlib::pdf::Color::red_color()
rixlib::pdf::Color::green_color()
rixlib::pdf::Color::blue_color()Create a color from hex:
auto color = rixlib::pdf::Color::from_hex(0x2C3E50);Units
PDF coordinates use points.
One inch is 72 points.
Helpers:
rixlib::pdf::inches(...)
rixlib::pdf::millimeters(...)
rixlib::pdf::centimeters(...)
rixlib::pdf::points_to_inches(...)
rixlib::pdf::points_to_millimeters(...)
rixlib::pdf::points_to_centimeters(...)Example:
auto one_inch = rixlib::pdf::inches(1.0F);Draw lines
Use:
page.line(
x1,
y1,
x2,
y2,
width,
color);Example:
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top(),
1.0F,
rixlib::pdf::Color::blue_color());With line style:
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top(),
1.0F,
rixlib::pdf::Color::gray(),
rixlib::pdf::LineStyle::Dashed);Line styles:
rixlib::pdf::LineStyle::Solid
rixlib::pdf::LineStyle::Dashed
rixlib::pdf::LineStyle::DottedDraw rectangles
Stroked rectangle:
page.rect(
page.x_left(),
page.y_top() - 80.0F,
140.0F,
50.0F);Filled rectangle:
page.fill_rect(
page.x_left(),
page.y_top() - 80.0F,
140.0F,
50.0F,
rixlib::pdf::Color::light_gray());Filled and stroked rectangle:
page.fill_stroke_rect(
page.x_left(),
page.y_top() - 80.0F,
140.0F,
50.0F,
rixlib::pdf::Color::white(),
rixlib::pdf::Color::black());Draw circles
Stroked circle:
page.circle(
page.x_left() + 70.0F,
page.y_top() - 120.0F,
35.0F,
1.0F,
rixlib::pdf::Color::red_color());Filled circle:
page.circle(
page.x_left() + 70.0F,
page.y_top() - 120.0F,
35.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);Draw a horizontal rule
Use:
page.hrule(y);Example:
page.hrule(
page.y_top() - 50.0F,
page.x_left(),
page.x_right(),
0.5F,
rixlib::pdf::Color::gray());Tables
Create a table:
rixlib::pdf::Table table;Set column widths:
table.set_column_widths({
160.0F,
160.0F,
160.0F});Add a header:
table.add_header({
"Name",
"Language",
"Project"});Add rows:
table.add_row({
"Ada",
"C++",
"Rix"});Draw the table:
auto y_after = page.table(
page.x_left(),
page.y_top() - 80.0F,
table);Table cells
Use TableCell when you need cell-level control.
rixlib::pdf::TableCell cell{"Ready"};
cell.set_align(rixlib::pdf::Align::Center);
cell.set_text_color(rixlib::pdf::Color::green_color());
cell.set_background_color(rixlib::pdf::Color::light_gray());
cell.set_colspan(2);Add it to a row:
rixlib::pdf::TableRow row;
row.add_cell(std::move(cell));
table.add_row(std::move(row));Table style
Access style with:
table.style()Example:
table.style()
.set_font(rixlib::pdf::Font::Helvetica)
.set_font_size(10.0F)
.set_header_font(rixlib::pdf::Font::HelveticaBold)
.set_header_size(10.0F)
.set_row_height(24.0F)
.set_cell_padding(6.0F)
.set_stripe_rows(true);Border style
Use:
rixlib::pdf::BorderStyleThin border:
table.style().set_border(
rixlib::pdf::BorderStyle::thin());No border:
table.style().set_border(
rixlib::pdf::BorderStyle::none());Custom border:
rixlib::pdf::BorderStyle border{
0.75F,
rixlib::pdf::Color::gray(),
rixlib::pdf::LineStyle::Solid};
table.style().set_border(border);Images
JPEG images can be loaded with:
auto image = rix.pdf.image.load_jpeg("photo.jpg");Check the result:
if (image.failed())
{
rix.debug.eprint(
"image error:",
rix.pdf.error.to_string(image.error()),
image.error().message());
return 1;
}Place the image:
page.image(
image.value(),
page.x_left(),
page.y_top() - 200.0F,
200.0F,
120.0F);Fit while preserving aspect ratio:
page.image_fit(
image.value(),
page.x_left(),
page.y_top() - 200.0F,
200.0F,
120.0F);The first image implementation supports JPEG images.
Image bytes
Create a JPEG image from bytes:
auto image = rix.pdf.image.from_jpeg_bytes(bytes);The bytes must contain JPEG data.
Check the result before using value().
Metadata
Set metadata through document helpers:
doc.set_title("Report")
.set_author("Rix")
.set_subject("Generated report")
.set_creator("my-app")
.set_keywords("report,rix,pdf");Access metadata directly:
auto &metadata = doc.metadata();
metadata.set_title("Report");
metadata.set_author("Rix");Read metadata:
doc.metadata().title()
doc.metadata().author()
doc.metadata().subject()
doc.metadata().creator()
doc.metadata().keywords()Clear metadata:
doc.metadata().clear();Check if user-provided metadata is empty:
if (doc.metadata().empty())
{
rix.debug.print("metadata is empty");
}The default creator is:
rix/pdfSave a PDF
Use:
auto saved = rix.pdf.save(doc, "output.pdf");Check the status:
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 1;
}Use save when writing directly to disk.
Write PDF bytes
Use:
auto bytes = rix.pdf.write(doc);Check the result:
if (bytes.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(bytes.error()),
bytes.error().message());
return 1;
}
const auto &pdf_data = bytes.value();Use write for HTTP responses, storage adapters, or custom output.
Make a simple text PDF
Use:
auto saved = rix.pdf.make_text(
"hello.pdf",
"Hello from rix.pdf",
"Rix PDF");The arguments are:
output path
text content
optional titlemake_text creates a document, adds a page, writes text, and saves the file.
Use document() when you need custom layout.
Error API
Error helpers are available through:
rix.pdf.errorCommon helpers:
rix.pdf.error.to_string(error)
rix.pdf.error.is(error, code)
rix.pdf.error.make(code, message)
rix.pdf.error.none()Example:
if (saved.failed())
{
const auto &error = saved.error();
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
}PDF error codes
PDF error codes include:
rixlib::pdf::PdfErrorCode::InvalidInput
rixlib::pdf::PdfErrorCode::InvalidState
rixlib::pdf::PdfErrorCode::InvalidPageSize
rixlib::pdf::PdfErrorCode::InvalidMargins
rixlib::pdf::PdfErrorCode::InvalidText
rixlib::pdf::PdfErrorCode::InvalidImage
rixlib::pdf::PdfErrorCode::InvalidTable
rixlib::pdf::PdfErrorCode::UnsupportedImageFormat
rixlib::pdf::PdfErrorCode::FileOpenFailed
rixlib::pdf::PdfErrorCode::FileReadFailed
rixlib::pdf::PdfErrorCode::FileWriteFailed
rixlib::pdf::PdfErrorCode::SerializationFailed
rixlib::pdf::PdfErrorCode::WriterError
rixlib::pdf::PdfErrorCode::UnknownUse codes for programmatic decisions.
Use messages for diagnostics.
Check a specific error
if (rix.pdf.error.is(
saved.error(),
rixlib::pdf::PdfErrorCode::InvalidInput))
{
rix.debug.eprint("hint:", "use a real output path");
}Result and status types
save returns a status:
auto saved = rix.pdf.save(doc, "output.pdf");Use:
saved.ok()
saved.failed()
saved.error()write and image loading return results:
auto bytes = rix.pdf.write(doc);
auto image = rix.pdf.image.load_jpeg("photo.jpg");Use:
result.ok()
result.failed()
result.value()
result.error()Never call value() before checking success.
Version API
The PDF module exposes version helpers:
rix.pdf.version()
rix.pdf.version_major()
rix.pdf.version_minor()
rix.pdf.version_patch()
rix.pdf.version_number()Example:
rix.debug.print("rix/pdf:", rix.pdf.version());Use these for diagnostics, examples, and compatibility checks.
Complete basic example
#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, "hello.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;
}Run:
vix run pdf.cppComplete table example
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
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, "table.pdf");
return saved.ok() ? 0 : 1;
}Complete drawing example
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
auto y = page.heading(
page.x_left(),
page.y_top(),
"Drawing",
1);
y -= 20.0F;
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.0F,
rixlib::pdf::Color::blue_color());
y -= 70.0F;
page.fill_stroke_rect(
page.x_left(),
y,
180.0F,
60.0F,
rixlib::pdf::Color::light_gray(),
rixlib::pdf::Color::black());
page.circle(
page.x_left() + 280.0F,
y + 30.0F,
30.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);
auto saved = rix.pdf.save(doc, "drawing.pdf");
return saved.ok() ? 0 : 1;
}Complete error example
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
rix.debug.eprint(
"expected pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 0;
}
return 1;
}Use in a Vix route
Use write to return PDF bytes from an HTTP route.
#include <vix.hpp>
#include <rix.hpp>
int main()
{
vix::App app;
app.get("/report.pdf", [](vix::Request &, vix::Response &res) {
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
res.status(500).json({
"ok", false,
"error", rix.pdf.error.to_string(bytes.error()),
"message", bytes.error().message()});
return;
}
res.header("Content-Type", "application/pdf");
res.header("Content-Disposition", "inline; filename=\"report.pdf\"");
res.send(bytes.value());
});
app.run();
return 0;
}Use in a Vix project
Create a Vix app:
vix new rix-pdf-api --app
cd rix-pdf-apiAdd Rix:
vix add rix/rix
vix installMake sure vix.app contains:
deps = [
"rix/rix",
]Use in src/main.cpp:
#include <rix.hpp>Build and run:
vix build
vix runSingle-file usage
Create a file:
mkdir -p ~/rix-pdf-api
cd ~/rix-pdf-api
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");
return rix.pdf.save(doc, "hello.pdf").ok() ? 0 : 1;
}Run:
vix run pdf.cppIf Rix is not available globally:
vix install -g rix/rix
vix run pdf.cppIndependent usage
Install only PDF:
vix add rix/pdf
vix installIn vix.app:
deps = [
"rix/pdf",
]Then include:
#include <rix/pdf.hpp>Example:
#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 independent rix/pdf");
auto saved = pdf.save(doc, "independent.pdf");
return saved.ok() ? 0 : 1;
}Use independent package APIs when you intentionally do not want the unified facade.
For most documentation and application examples, prefer:
#include <rix.hpp>and:
rix.pdfEnable only PDF in the facade
Use feature macros when you want the facade style but only want PDF mounted:
#define RIX_ENABLE_PDF
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
return rix.pdf.save(doc, "pdf-only.pdf").ok() ? 0 : 1;
}If you also want debug output:
#define RIX_ENABLE_PDF
#define RIX_ENABLE_DEBUG
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto saved = rix.pdf.save(doc, "pdf-debug.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:", "pdf-debug.pdf");
return 0;
}Common mistakes
Forgetting to install Rix
If your code uses:
#include <rix.hpp>install:
vix add rix/rix
vix installIf your code uses:
#include <rix/pdf.hpp>install:
vix add rix/pdf
vix installPutting Rix in packages
Wrong:
packages = [
"rix/pdf",
]Correct:
deps = [
"rix/pdf",
]deps is for Vix Registry packages.
packages is for CMake package discovery.
Installing rix/pdf but including <rix.hpp>
If your code uses:
#include <rix.hpp>then install:
vix add rix/rix
vix installIf your code uses:
#include <rix/pdf.hpp>then install:
vix add rix/pdf
vix installCalling value() before checking success
Wrong:
auto bytes = rix.pdf.write(doc);
res.send(bytes.value());Correct:
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
return;
}
res.send(bytes.value());Not checking save errors
Wrong:
rix.pdf.save(doc, "output.pdf");Correct:
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;
}Expecting metadata to be visible
This sets metadata:
doc.set_title("Report");This draws visible text:
page.heading(
page.x_left(),
page.y_top(),
"Report",
1);Use both when you want a document title and a visible title.
Saving to a missing folder
This can fail:
rix.pdf.save(doc, "output/report.pdf");Create the folder first:
mkdir -p outputUsing make_text for complex layout
make_text is for simple text PDFs.
Use document() for:
tables
drawings
images
custom styles
multiple sections
manual positioningUsing load_jpeg for PNG files
load_jpeg expects JPEG data.
This can fail:
rix.pdf.image.load_jpeg("logo.png");Use JPEG input for this helper.
Using debug output as production logging
rix.debug is useful for examples and small tools.
For real Vix application logs, prefer:
vix::logWhat you should remember
Create a document:
auto doc = rix.pdf.document();Add a page:
auto &page = doc.add_page();Draw text:
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");Save:
auto saved = rix.pdf.save(doc, "hello.pdf");Write bytes:
auto bytes = rix.pdf.write(doc);Make simple text PDF:
auto saved = rix.pdf.make_text(
"hello.pdf",
"Hello from rix.pdf",
"Rix PDF");Always check results before calling value().
For project usage:
vix add rix/rix
vix installand keep:
deps = [
"rix/rix",
]For independent PDF usage:
vix add rix/pdf
vix installand:
#include <rix/pdf.hpp>Next step
Continue with troubleshooting.
Next: Troubleshooting