Document
This page explains how to create and manage a PDF document with rix/pdf.
The examples use the public Rix facade:
#include <rix.hpp>and access PDF through:
rix.pdfA PDF document is the root object. It owns pages and metadata. Pages contain the visible content.
Basic document
Create a file:
mkdir -p ~/rix-pdf-document
cd ~/rix-pdf-document
touch document.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 a Rix PDF document");
auto saved = rix.pdf.save(doc, "document.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:", "document.pdf");
return 0;
}Run it:
vix run document.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run document.cppCreate a document
Use:
auto doc = rix.pdf.document();This creates a document with default settings:
page size: A4
margins: default margins
pages: none
metadata: empty user metadataA document does not automatically create a page.
You add pages explicitly:
auto &page = doc.add_page();Create a document with page settings
You can create a document with a default page size and margins:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::one_inch());Every page created with add_page() uses these defaults.
auto &page = doc.add_page();Page size
Page sizes use PDF points.
One point is 1/72 inch.
Common page sizes:
rixlib::pdf::PageSize::A4()
rixlib::pdf::PageSize::A3()
rixlib::pdf::PageSize::Letter()
rixlib::pdf::PageSize::Legal()Example:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::Letter());Landscape pages
Use as_landscape() when you want a landscape page size:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4().as_landscape());Use as_portrait() when you want portrait:
auto size = rixlib::pdf::PageSize::A4().as_portrait();Custom page size
Create a custom page size with points:
auto size = rixlib::pdf::PageSize::custom(
500.0F,
700.0F);Or use inch helpers:
auto size = rixlib::pdf::PageSize::from_inches(
8.5F,
11.0F);Or millimeter helpers:
auto size = rixlib::pdf::PageSize::from_millimeters(
210.0F,
297.0F);Margins
Use default margins:
auto doc = rix.pdf.document();Use one-inch margins:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::one_inch());Use no margins:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::none());Use custom margins in inches:
auto margins = rixlib::pdf::Margins::from_inches(
1.0F,
1.0F,
0.75F,
0.75F);The order is:
top
bottom
left
rightUse custom margins in millimeters:
auto margins = rixlib::pdf::Margins::from_millimeters(
20.0F,
20.0F,
20.0F,
20.0F);Add a page
Use:
auto &page = doc.add_page();This uses the document default page size and margins.
Example:
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"First page");Add a page with custom size
Use:
auto &page = doc.add_page(
rixlib::pdf::PageSize::Letter());This page uses the custom size and the document default margins.
Add a page with custom size and margins
Use:
auto &page = doc.add_page(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::from_millimeters(
20.0F,
20.0F,
20.0F,
20.0F));This is useful when one page needs different layout rules.
Multiple pages
Add several pages:
auto doc = rix.pdf.document();
auto &first = doc.add_page();
first.text(
first.x_left(),
first.y_top(),
"First page");
auto &second = doc.add_page();
second.text(
second.x_left(),
second.y_top(),
"Second page");Save:
auto saved = rix.pdf.save(doc, "multi-page.pdf");Page count
Use:
doc.page_count()Example:
rix.debug.print("pages:", doc.page_count());Check whether the document has no pages:
if (doc.empty())
{
rix.debug.print("document has no pages");
}Access pages
Access a page by index:
auto &page = doc.page(0);Indexes are zero-based.
Use this only when you know the page exists.
Example:
if (doc.page_count() > 0)
{
auto &page = doc.page(0);
page.text(
page.x_left(),
page.y_top() - 40.0F,
"Added later");
}Clear pages
Remove all pages:
doc.clear_pages();After this:
doc.page_count() == 0
doc.empty() == trueMetadata
A document stores metadata.
Common metadata fields:
title
author
subject
creator
keywordsSet metadata with chainable helpers:
doc.set_title("Rix PDF Document")
.set_author("Rix")
.set_subject("Document generation")
.set_keywords("rix,pdf,vix,cpp");You can also set creator:
doc.set_creator("my-app");If the creator is empty, it is normalized back to the default creator.
Metadata example
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
doc.set_title("Rix PDF Document")
.set_author("Rix")
.set_subject("Document metadata")
.set_keywords("rix,pdf,vix,cpp");
auto &page = doc.add_page();
page.heading(
page.x_left(),
page.y_top(),
"Document metadata",
1);
auto saved = rix.pdf.save(doc, "metadata.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:", "metadata.pdf");
return 0;
}Run:
vix run document.cppRead metadata
Access metadata:
const auto &metadata = doc.metadata();
rix.debug.print("title:", metadata.title());
rix.debug.print("author:", metadata.author());Mutable access is also available:
doc.metadata().set_title("Updated title");The chainable helpers are usually clearer:
doc.set_title("Updated title");Default page settings
Read the default page size:
const auto &size = doc.default_page_size();
rix.debug.print("width:", size.width());
rix.debug.print("height:", size.height());Read the default margins:
const auto &margins = doc.default_margins();
rix.debug.print("left:", margins.left());
rix.debug.print("right:", margins.right());Update defaults:
doc.set_default_page_size(
rixlib::pdf::PageSize::Letter());
doc.set_default_margins(
rixlib::pdf::Margins::one_inch());Pages added after the update use the new defaults.
Complete multi-page example
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::one_inch());
doc.set_title("Multi-page document")
.set_author("Rix");
auto &first = doc.add_page();
first.heading(
first.x_left(),
first.y_top(),
"First page",
1);
first.paragraph(
first.x_left(),
first.y_top() - 50.0F,
first.content_width(),
"This is the first page of a generated PDF document.");
auto &second = doc.add_page(
rixlib::pdf::PageSize::A4().as_landscape());
second.heading(
second.x_left(),
second.y_top(),
"Second page",
1);
second.paragraph(
second.x_left(),
second.y_top() - 50.0F,
second.content_width(),
"This page uses a landscape A4 page size.");
auto saved = rix.pdf.save(doc, "multi-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:", "multi-page.pdf");
rix.debug.print("pages:", doc.page_count());
return 0;
}Run:
vix run document.cppSave the document
Use:
auto saved = rix.pdf.save(doc, "document.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 the document to memory
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("bytes:", bytes.value().size());Use write when you need the generated PDF bytes instead of a file.
Empty documents
If a document has no pages, the writer can still generate a valid PDF by adding a blank default page during serialization.
Even so, it is clearer to add pages explicitly:
auto &page = doc.add_page();Do this when the output should contain visible content.
Use in a Vix project
Create a Vix application:
vix new pdf-document --app
cd pdf-documentAdd 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-document"
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 doc = rix.pdf.document();
doc.set_title("Document example");
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");
auto saved = rix.pdf.save(doc, "document.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:", "document.pdf");
return 0;
}Build and run:
vix build
vix runSingle-file usage
For small scripts, examples, and experiments:
vix run document.cppIf Rix is installed globally for single-file usage:
vix install -g rix/rix
vix run document.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.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");
return rix.pdf.save(doc, "document.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
Forgetting to add a page
Wrong:
auto doc = rix.pdf.document();
page.text(0.0F, 0.0F, "Hello");Correct:
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"Hello");Forgetting to save the document
Creating pages does not write a file.
Wrong:
auto doc = rix.pdf.document();
doc.add_page();Correct:
auto saved = rix.pdf.save(doc, "document.pdf");Not checking save errors
Wrong:
rix.pdf.save(doc, "document.pdf");Better:
auto saved = rix.pdf.save(doc, "document.pdf");
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 1;
}Using invalid page indexes
Wrong:
auto &page = doc.page(0);when the document has no pages.
Better:
if (doc.page_count() > 0)
{
auto &page = doc.page(0);
}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
Create a document:
auto doc = rix.pdf.document();Create a document with defaults:
auto doc = rix.pdf.document(
rixlib::pdf::PageSize::A4(),
rixlib::pdf::Margins::one_inch());Add a page:
auto &page = doc.add_page();Add metadata:
doc.set_title("Title")
.set_author("Rix");Save:
auto saved = rix.pdf.save(doc, "document.pdf");Check errors:
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
}For a Vix project, install Rix:
vix add rix/rix
vix installand use:
deps = [
"rix/rix",
]Next step
Learn how to work with pages.
Next: Page