Drawing
This example shows how to draw lines, rectangles, filled shapes, and circles with rix/pdf.
The example uses the public Rix facade:
#include <rix.hpp>and accesses PDF through:
rix.pdfUse this example when you want to create simple visual elements inside a PDF page.
Create the file
mkdir -p ~/rix-pdf-drawing-example
cd ~/rix-pdf-drawing-example
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, "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 it:
vix run drawing.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run drawing.cppThis creates:
rix_pdf_drawing.pdfWhat this example does
The example creates a PDF document:
auto doc = rix.pdf.document();It adds a page:
auto &page = doc.add_page();It draws a heading:
auto y = page.heading(
page.x_left(),
page.y_top(),
"Drawing primitives",
1);It draws a horizontal line:
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.5F,
rixlib::pdf::Color::blue_color());It draws rectangles:
page.rect(...);
page.fill_rect(...);
page.fill_stroke_rect(...);It draws circles:
page.circle(...);Then it saves the file:
auto saved = rix.pdf.save(doc, "rix_pdf_drawing.pdf");Drawing coordinates
PDF coordinates are expressed in points.
One inch is 72 points.
The example uses page helpers:
page.x_left()
page.x_right()
page.y_top()
page.y_bottom()
page.content_width()
page.content_height()These helpers keep drawing inside the page margins.
Draw a line
Use:
page.line(
x1,
y1,
x2,
y2,
width,
color);Example:
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.5F,
rixlib::pdf::Color::blue_color());The arguments are:
start x
start y
end x
end y
line width
line colorDraw a dashed line
Use the line style argument:
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.0F,
rixlib::pdf::Color::gray(),
rixlib::pdf::LineStyle::Dashed);Available line styles are:
rixlib::pdf::LineStyle::Solid
rixlib::pdf::LineStyle::Dashed
rixlib::pdf::LineStyle::DottedDraw a rectangle
Use:
page.rect(
x,
y,
width,
height);Example:
page.rect(
page.x_left(),
y,
140.0F,
50.0F);This draws a stroked rectangle.
Draw a filled rectangle
Use:
page.fill_rect(
x,
y,
width,
height,
color);Example:
page.fill_rect(
page.x_left(),
y,
140.0F,
50.0F,
rixlib::pdf::Color::light_gray());This draws a rectangle filled with the given color.
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(),
y,
140.0F,
50.0F,
rixlib::pdf::Color::white(),
rixlib::pdf::Color::black(),
1.0F);Use this when you want both a background and a border.
Draw a circle
Use:
page.circle(
center_x,
center_y,
radius,
line_width,
color);Example:
page.circle(
page.x_left() + 70.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::red_color());Draw a filled circle
Pass true as the last argument:
page.circle(
page.x_left() + 70.0F,
y,
35.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);The last argument controls whether the circle is filled.
Draw a horizontal rule
Use:
page.hrule(y);Example:
page.hrule(
y,
page.x_left(),
page.x_right(),
0.5F,
rixlib::pdf::Color::gray());hrule is useful for separating sections.
Use colors
Common 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);Then use it:
page.line(
page.x_left(),
y,
page.x_right(),
y,
1.0F,
color);Complete drawing page
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
doc.set_title("Drawing Page")
.set_author("Rix");
auto &page = doc.add_page();
auto y = page.heading(
page.x_left(),
page.y_top(),
"Drawing Page",
1);
y -= 20.0F;
page.hrule(
y,
page.x_left(),
page.x_right(),
0.75F,
rixlib::pdf::Color::gray());
y -= 70.0F;
page.fill_stroke_rect(
page.x_left(),
y,
180.0F,
60.0F,
rixlib::pdf::Color::light_gray(),
rixlib::pdf::Color::black(),
1.0F);
page.text(
page.x_left() + 20.0F,
y + 25.0F,
"Box");
page.circle(
page.x_left() + 280.0F,
y + 30.0F,
30.0F,
1.0F,
rixlib::pdf::Color::blue_color(),
true);
auto saved = rix.pdf.save(doc, "drawing-page.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-page.pdf");
return 0;
}Run:
vix run drawing.cppBuild a simple card
You can combine filled rectangles, text, and borders to create a card.
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
const auto x = page.x_left();
auto y = page.y_top();
page.fill_stroke_rect(
x,
y - 100.0F,
page.content_width(),
100.0F,
rixlib::pdf::Color::light_gray(),
rixlib::pdf::Color::gray(),
1.0F);
page.text(
x + 20.0F,
y - 35.0F,
"Rix PDF Card",
rixlib::pdf::TextStyle{
rixlib::pdf::Font::HelveticaBold,
16.0F,
rixlib::pdf::Color::black()});
page.text(
x + 20.0F,
y - 65.0F,
"This card uses drawing primitives.");
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.cppBuild a simple status row
#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(),
"Status",
1);
y -= 40.0F;
page.circle(
page.x_left() + 10.0F,
y + 4.0F,
5.0F,
1.0F,
rixlib::pdf::Color::green_color(),
true);
page.text(
page.x_left() + 30.0F,
y,
"Service is running");
auto saved = rix.pdf.save(doc, "status.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:", "status.pdf");
return 0;
}Run:
vix run drawing.cppDraw section separators
Use horizontal rules between sections:
auto y = page.heading(
page.x_left(),
page.y_top(),
"Report",
1);
y -= 15.0F;
page.hrule(y);
y -= 25.0F;
page.text(
page.x_left(),
y,
"Section content");This is useful in reports, invoices, summaries, and exported documents.
Use drawing with tables
Drawing primitives can be mixed with tables.
auto y = page.heading(
page.x_left(),
page.y_top(),
"Report",
1);
y -= 10.0F;
page.hrule(y);
y -= 25.0F;
rixlib::pdf::Table table;
table.set_column_widths({
160.0F,
160.0F});
table.add_header({
"Name",
"Status"});
table.add_row({
"Ada",
"Ready"});
page.table(
page.x_left(),
y,
table);Use drawing helpers to make tables and reports easier to read.
Save the document
Use:
auto saved = rix.pdf.save(doc, "drawing.pdf");Check errors:
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 1;
}Write bytes instead of saving
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 for HTTP responses or custom output handling.
Use save for direct file output.
Use in a Vix project
Create a project:
vix new rix-pdf-drawing --app
cd rix-pdf-drawingAdd Rix:
vix add rix/rix
vix installMake sure vix.app contains:
deps = [
"rix/rix",
]A minimal vix.app can look like this:
name = "rix-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",
]Put the example code in:
src/main.cppBuild and run:
vix build
vix runSingle-file usage
For examples, tests, and quick experiments:
vix run drawing.cppIf needed:
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.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");
return saved.ok() ? 0 : 1;
}When at least one RIX_ENABLE_* macro is defined, only selected modules are mounted.
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 &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;
}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>Example:
#include <rix/pdf.hpp>
int main()
{
auto pdf = rixlib::pdf::module();
auto doc = 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 = pdf.save(doc, "drawing.pdf");
return saved.ok() ? 0 : 1;
}The examples in this documentation prefer the public facade:
#include <rix.hpp>and:
rix.pdfCommon mistakes
Forgetting to install Rix
If rix.hpp is not found, install Rix first.
For a project:
vix add rix/rix
vix installFor single-file usage:
vix install -g rix/rixPutting Rix in packages
Wrong:
packages = [
"rix/rix",
]Correct:
deps = [
"rix/rix",
]deps is for Vix Registry packages.
packages is for CMake package discovery.
Forgetting that Y moves downward by subtraction
To draw lower on the page, subtract from Y:
y -= 70.0F;Then draw:
page.rect(
page.x_left(),
y,
140.0F,
50.0F);Drawing outside the page margins
Prefer:
page.x_left()
page.x_right()
page.y_top()
page.y_bottom()over hardcoded positions.
Forgetting to save the document
Drawing commands only update the document model.
To create a file, call:
rix.pdf.save(doc, "drawing.pdf");Not checking save errors
Wrong:
rix.pdf.save(doc, "drawing.pdf");Correct:
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;
}What you should remember
Create a document:
auto doc = rix.pdf.document();Add a page:
auto &page = doc.add_page();Draw a line:
page.line(
page.x_left(),
page.y_top(),
page.x_right(),
page.y_top(),
1.0F,
rixlib::pdf::Color::blue_color());Draw a rectangle:
page.rect(
page.x_left(),
page.y_top() - 80.0F,
140.0F,
50.0F);Draw a circle:
page.circle(
page.x_left() + 70.0F,
page.y_top() - 160.0F,
35.0F,
1.0F,
rixlib::pdf::Color::red_color());Save:
auto saved = rix.pdf.save(doc, "drawing.pdf");For project usage:
vix add rix/rix
vix installand keep:
deps = [
"rix/rix",
]Next step
Continue with PDF metadata.
Next: Metadata