Drawing
This page explains how to draw simple shapes with rix/pdf.
The examples use the public Rix facade:
#include <rix.hpp>and access PDF through:
rix.pdfDrawing primitives are useful for separators, boxes, highlights, simple diagrams, report sections, cards, badges, and visual structure inside generated PDF documents.
Basic drawing example
Create a file:
mkdir -p ~/rix-pdf-drawing
cd ~/rix-pdf-drawing
touch drawing.cppAdd:
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
doc.set_title("Rix PDF Drawing Example");
auto &page = doc.add_page();
auto y = page.heading(
page.x_left(),
page.y_top(),
"Drawing primitives",
1);
y -= 20.0F;
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.5F,
rixlib::pdf::Color::blue_color());
y -= 70.0F;
page.rect(
page.x_left(),
y,
140.0F,
50.0F);
page.fill_rect(
page.x_left() + 170.0F,
y,
140.0F,
50.0F,
rixlib::pdf::Color::light_gray());
page.fill_stroke_rect(
page.x_left() + 340.0F,
y,
140.0F,
50.0F,
rixlib::pdf::Color::white(),
rixlib::pdf::Color::black());
y -= 100.0F;
page.circle(
page.x_left() + 70.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::red_color());
page.circle(
page.x_left() + 230.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);
page.circle(
page.x_left() + 390.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::blue_color());
auto saved = rix.pdf.save(doc, "drawing.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:", "drawing.pdf");
return 0;
}Run it:
vix run drawing.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run drawing.cppThis creates:
drawing.pdfDrawing coordinates
Drawing happens on a page.
PDF coordinates use points.
One point is 1/72 inch.
The page exposes helpers:
page.x_left()
page.x_right()
page.y_top()
page.y_bottom()
page.content_width()
page.content_height()Use these helpers to stay inside the visible content area.
Draw a line
Use:
page.line(x1, y1, x2, y2);Example:
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top());This draws a horizontal line across the content area.
Line width and color
You can pass a line width and color:
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top(),
1.5F,
rixlib::pdf::Color::blue_color());The line width is expressed in points.
Line styles
Use rixlib::pdf::LineStyle for stroke patterns:
rixlib::pdf::LineStyle::Solid
rixlib::pdf::LineStyle::Dashed
rixlib::pdf::LineStyle::DottedExample:
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top(),
1.0F,
rixlib::pdf::Color::gray(),
rixlib::pdf::LineStyle::Dashed);Horizontal rule
Use hrule for a simple horizontal separator:
page.hrule(page.y_top() - 40.0F);You can customize the range:
page.hrule(
page.y_top() - 40.0F,
page.x_left(),
page.x_right(),
0.5F,
rixlib::pdf::Color::gray());Use hrule when you want a clean divider between document sections.
Draw a rectangle
Use:
page.rect(x, y, width, height);Example:
page.rect(
page.x_left(),
page.y_top() - 80.0F,
180.0F,
60.0F);This draws a stroked rectangle.
Rectangle line width and color
page.rect(
page.x_left(),
page.y_top() - 80.0F,
180.0F,
60.0F,
1.0F,
rixlib::pdf::Color::blue_color());Draw a filled rectangle
Use:
page.fill_rect(x, y, width, height, color);Example:
page.fill_rect(
page.x_left(),
page.y_top() - 80.0F,
180.0F,
60.0F,
rixlib::pdf::Color::light_gray());Filled rectangles are useful for highlights, cards, headers, and table-like sections.
Draw a filled and stroked rectangle
Use:
page.fill_stroke_rect(
x,
y,
width,
height,
fill_color,
stroke_color,
line_width);Example:
page.fill_stroke_rect(
page.x_left(),
page.y_top() - 80.0F,
180.0F,
60.0F,
rixlib::pdf::Color::white(),
rixlib::pdf::Color::black(),
1.0F);Use this when a box needs both background and border.
Draw a circle
Use:
page.circle(cx, cy, radius);Example:
page.circle(
page.x_left() + 40.0F,
page.y_top() - 80.0F,
30.0F);The first two values are the center position.
The third value is the radius.
Circle width and color
page.circle(
page.x_left() + 40.0F,
page.y_top() - 80.0F,
30.0F,
1.0F,
rixlib::pdf::Color::red_color());Filled circle
Pass true as the final argument:
page.circle(
page.x_left() + 40.0F,
page.y_top() - 80.0F,
30.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);Use filled circles for badges, status indicators, and simple diagrams.
Colors
Use rixlib::pdf::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);Example:
page.fill_rect(
page.x_left(),
page.y_top() - 80.0F,
200.0F,
60.0F,
rixlib::pdf::Color::from_hex(0xF3F4F6));Use units
Drawing values are points, but you can use helpers:
rixlib::pdf::inches(1.0F)
rixlib::pdf::millimeters(20.0F)
rixlib::pdf::centimeters(2.0F)Example:
page.rect(
rixlib::pdf::inches(1.0F),
rixlib::pdf::inches(9.0F),
rixlib::pdf::inches(3.0F),
rixlib::pdf::inches(1.0F));Use helpers when the layout is easier to think about in inches or millimeters.
Draw a card
A card is just a filled and stroked rectangle with text inside.
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
auto x = page.x_left();
auto y = page.y_top() - 80.0F;
page.fill_stroke_rect(
x,
y,
260.0F,
80.0F,
rixlib::pdf::Color::light_gray(),
rixlib::pdf::Color::gray(),
0.75F);
page.text(
x + 16.0F,
y + 50.0F,
"Rix PDF",
rixlib::pdf::TextStyle{
rixlib::pdf::Font::HelveticaBold,
14.0F});
page.text(
x + 16.0F,
y + 28.0F,
"Generated with C++ and Rix.");
auto saved = rix.pdf.save(doc, "card.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:", "card.pdf");
return 0;
}Run:
vix run drawing.cppDraw section dividers
Use headings with horizontal rules:
auto y = page.heading(
page.x_left(),
page.y_top(),
"Report",
1);
y -= 10.0F;
page.hrule(
y,
page.x_left(),
page.x_right(),
0.5F,
rixlib::pdf::Color::gray());
y -= 25.0F;
page.paragraph(
page.x_left(),
y,
page.content_width(),
"This section starts below the divider.");This is useful for reports and generated documents.
Draw a simple status badge
auto x = page.x_left();
auto y = page.y_top() - 80.0F;
page.fill_stroke_rect(
x,
y,
120.0F,
32.0F,
rixlib::pdf::Color::light_gray(),
rixlib::pdf::Color::gray(),
0.5F);
page.circle(
x + 16.0F,
y + 16.0F,
5.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);
page.text(
x + 30.0F,
y + 11.0F,
"Ready",
rixlib::pdf::TextStyle::small());Use this pattern for generated status reports.
Draw a simple layout grid
auto x = page.x_left();
auto y = page.y_top() - 80.0F;
const auto card_width = 150.0F;
const auto card_height = 70.0F;
const auto gap = 20.0F;
for (int i = 0; i < 3; ++i)
{
const auto card_x = x + static_cast<float>(i) * (card_width + gap);
page.fill_stroke_rect(
card_x,
y,
card_width,
card_height,
rixlib::pdf::Color::white(),
rixlib::pdf::Color::gray(),
0.5F);
page.text(
card_x + 12.0F,
y + 42.0F,
"Card");
}This can be useful for simple dashboards and summary pages.
Complete drawing document
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
doc.set_title("Rix PDF Drawing Example");
auto &page = doc.add_page();
auto y = page.heading(
page.x_left(),
page.y_top(),
"Drawing primitives",
1);
y -= 20.0F;
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.5F,
rixlib::pdf::Color::blue_color());
y -= 70.0F;
page.rect(
page.x_left(),
y,
140.0F,
50.0F);
page.fill_rect(
page.x_left() + 170.0F,
y,
140.0F,
50.0F,
rixlib::pdf::Color::light_gray());
page.fill_stroke_rect(
page.x_left() + 340.0F,
y,
140.0F,
50.0F,
rixlib::pdf::Color::white(),
rixlib::pdf::Color::black());
y -= 100.0F;
page.circle(
page.x_left() + 70.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::red_color());
page.circle(
page.x_left() + 230.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);
page.circle(
page.x_left() + 390.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::blue_color());
auto saved = rix.pdf.save(doc, "rix_pdf_drawing.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_drawing.pdf");
return 0;
}Run:
vix run drawing.cppCombine drawing and tables
Drawing primitives can be used around tables.
auto y = page.heading(
page.x_left(),
page.y_top(),
"Report",
1);
y -= 15.0F;
page.hrule(
y,
page.x_left(),
page.x_right());
y -= 25.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"});
y = page.table(
page.x_left(),
y,
table);Use drawing for structure.
Use tables for data.
Combine drawing and text
auto x = page.x_left();
auto y = page.y_top() - 80.0F;
page.fill_rect(
x,
y,
page.content_width(),
70.0F,
rixlib::pdf::Color::light_gray());
page.text(
x + 16.0F,
y + 42.0F,
"Summary",
rixlib::pdf::TextStyle{
rixlib::pdf::Font::HelveticaBold,
14.0F});
page.text(
x + 16.0F,
y + 20.0F,
"This block uses a filled rectangle behind text.");Use this for highlighted sections.
Save the PDF
Use:
auto saved = rix.pdf.save(doc, "drawing.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 to return PDF bytes from another API or save them yourself.
Use save when you want to write directly to a file.
Use in a Vix project
Create a Vix application:
vix new pdf-drawing --app
cd pdf-drawingAdd 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-drawing"
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 drawing in src/main.cpp:
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top(),
1.0F,
rixlib::pdf::Color::blue_color());
auto saved = rix.pdf.save(doc, "drawing.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:", "drawing.pdf");
return 0;
}Build and run:
vix build
vix runSingle-file usage
For small scripts, examples, and experiments:
vix run drawing.cppIf Rix is installed globally for single-file usage:
vix install -g rix/rix
vix run drawing.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.rect(
page.x_left(),
page.y_top() - 80.0F,
180.0F,
60.0F);
return rix.pdf.save(doc, "drawing.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
Drawing outside the page
This may not be visible:
page.rect(0.0F, -50.0F, 100.0F, 50.0F);Use page helpers:
page.x_left()
page.y_top()
page.y_bottom()Forgetting that Y moves downward by subtracting
To draw lower on the page, subtract from Y:
auto y = page.y_top();
page.text(page.x_left(), y, "First");
y -= 40.0F;
page.text(page.x_left(), y, "Second");Using too large shapes
A rectangle wider than the content area can overflow:
page.rect(
page.x_left(),
page.y_top(),
1000.0F,
100.0F);Prefer:
page.rect(
page.x_left(),
page.y_top() - 100.0F,
page.content_width(),
100.0F);Not checking save errors
Wrong:
rix.pdf.save(doc, "drawing.pdf");Better:
auto saved = rix.pdf.save(doc, "drawing.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
Draw a line:
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top());Draw a rectangle:
page.rect(
page.x_left(),
page.y_top() - 80.0F,
180.0F,
60.0F);Draw a filled rectangle:
page.fill_rect(
page.x_left(),
page.y_top() - 80.0F,
180.0F,
60.0F,
rixlib::pdf::Color::light_gray());Draw a circle:
page.circle(
page.x_left() + 40.0F,
page.y_top() - 80.0F,
30.0F);Use colors:
rixlib::pdf::Color::blue_color()
rixlib::pdf::Color::gray()
rixlib::pdf::Color::from_hex(0x2C3E50)Save the document:
auto saved = rix.pdf.save(doc, "drawing.pdf");For a Vix project, install Rix:
vix add rix/rix
vix installand use:
deps = [
"rix/rix",
]Next step
Learn PDF metadata.
Next: Metadata