Rix is the official package layer for Vix.cpp Browse packages
Skip to content

Drawing

This example shows how to draw lines, rectangles, filled shapes, and circles with rix/pdf.

The example uses the public Rix facade:

cpp
#include <rix.hpp>

and accesses PDF through:

cpp
rix.pdf

Use this example when you want to create simple visual elements inside a PDF page.

Create the file

bash
mkdir -p ~/rix-pdf-drawing-example
cd ~/rix-pdf-drawing-example
touch drawing.cpp

Add:

cpp
#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:

bash
vix run drawing.cpp

If Rix is not available yet for single-file usage:

bash
vix install -g rix/rix
vix run drawing.cpp

This creates:

txt
rix_pdf_drawing.pdf

What this example does

The example creates a PDF document:

cpp
auto doc = rix.pdf.document();

It adds a page:

cpp
auto &page = doc.add_page();

It draws a heading:

cpp
auto y = page.heading(
    page.x_left(),
    page.y_top(),
    "Drawing primitives",
    1);

It draws a horizontal line:

cpp
page.line(
    page.x_left(),
    y,
    page.x_right(),
    y,
    1.5F,
    rixlib::pdf::Color::blue_color());

It draws rectangles:

cpp
page.rect(...);
page.fill_rect(...);
page.fill_stroke_rect(...);

It draws circles:

cpp
page.circle(...);

Then it saves the file:

cpp
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:

cpp
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:

cpp
page.line(
    x1,
    y1,
    x2,
    y2,
    width,
    color);

Example:

cpp
page.line(
    page.x_left(),
    y,
    page.x_right(),
    y,
    1.5F,
    rixlib::pdf::Color::blue_color());

The arguments are:

txt
start x
start y
end x
end y
line width
line color

Draw a dashed line

Use the line style argument:

cpp
page.line(
    page.x_left(),
    y,
    page.x_right(),
    y,
    1.0F,
    rixlib::pdf::Color::gray(),
    rixlib::pdf::LineStyle::Dashed);

Available line styles are:

cpp
rixlib::pdf::LineStyle::Solid
rixlib::pdf::LineStyle::Dashed
rixlib::pdf::LineStyle::Dotted

Draw a rectangle

Use:

cpp
page.rect(
    x,
    y,
    width,
    height);

Example:

cpp
page.rect(
    page.x_left(),
    y,
    140.0F,
    50.0F);

This draws a stroked rectangle.

Draw a filled rectangle

Use:

cpp
page.fill_rect(
    x,
    y,
    width,
    height,
    color);

Example:

cpp
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:

cpp
page.fill_stroke_rect(
    x,
    y,
    width,
    height,
    fill_color,
    stroke_color,
    line_width);

Example:

cpp
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:

cpp
page.circle(
    center_x,
    center_y,
    radius,
    line_width,
    color);

Example:

cpp
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:

cpp
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:

cpp
page.hrule(y);

Example:

cpp
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:

cpp
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:

cpp
auto color = rixlib::pdf::Color::from_hex(0x2C3E50);

Then use it:

cpp
page.line(
    page.x_left(),
    y,
    page.x_right(),
    y,
    1.0F,
    color);

Complete drawing page

cpp
#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:

bash
vix run drawing.cpp

Build a simple card

You can combine filled rectangles, text, and borders to create a card.

cpp
#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:

bash
vix run drawing.cpp

Build a simple status row

cpp
#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:

bash
vix run drawing.cpp

Draw section separators

Use horizontal rules between sections:

cpp
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.

cpp
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:

cpp
auto saved = rix.pdf.save(doc, "drawing.pdf");

Check errors:

cpp
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:

cpp
auto bytes = rix.pdf.write(doc);

Example:

cpp
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:

bash
vix new rix-pdf-drawing --app
cd rix-pdf-drawing

Add Rix:

bash
vix add rix/rix
vix install

Make sure vix.app contains:

txt
deps = [
  "rix/rix",
]

A minimal vix.app can look like this:

txt
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:

txt
src/main.cpp

Build and run:

bash
vix build
vix run

Single-file usage

For examples, tests, and quick experiments:

bash
vix run drawing.cpp

If needed:

bash
vix install -g rix/rix
vix run drawing.cpp

For project usage, prefer:

bash
vix add rix/rix
vix install

and keep the dependency in vix.app:

txt
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:

cpp
#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:

cpp
#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:

bash
vix add rix/pdf
vix install

In vix.app:

txt
deps = [
  "rix/pdf",
]

Then include:

cpp
#include <rix/pdf.hpp>

Example:

cpp
#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:

cpp
#include <rix.hpp>

and:

cpp
rix.pdf

Common mistakes

Forgetting to install Rix

If rix.hpp is not found, install Rix first.

For a project:

bash
vix add rix/rix
vix install

For single-file usage:

bash
vix install -g rix/rix

Putting Rix in packages

Wrong:

txt
packages = [
  "rix/rix",
]

Correct:

txt
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:

cpp
y -= 70.0F;

Then draw:

cpp
page.rect(
    page.x_left(),
    y,
    140.0F,
    50.0F);

Drawing outside the page margins

Prefer:

cpp
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:

cpp
rix.pdf.save(doc, "drawing.pdf");

Not checking save errors

Wrong:

cpp
rix.pdf.save(doc, "drawing.pdf");

Correct:

cpp
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:

cpp
auto doc = rix.pdf.document();

Add a page:

cpp
auto &page = doc.add_page();

Draw a line:

cpp
page.line(
    page.x_left(),
    page.y_top(),
    page.x_right(),
    page.y_top(),
    1.0F,
    rixlib::pdf::Color::blue_color());

Draw a rectangle:

cpp
page.rect(
    page.x_left(),
    page.y_top() - 80.0F,
    140.0F,
    50.0F);

Draw a circle:

cpp
page.circle(
    page.x_left() + 70.0F,
    page.y_top() - 160.0F,
    35.0F,
    1.0F,
    rixlib::pdf::Color::red_color());

Save:

cpp
auto saved = rix.pdf.save(doc, "drawing.pdf");

For project usage:

bash
vix add rix/rix
vix install

and keep:

txt
deps = [
  "rix/rix",
]

Next step

Continue with PDF metadata.

Next: Metadata

Released under the MIT License.