Rand Stats



[Raku PDF Project] / PDF::Content

Actions Status


This Raku module is a library of roles and classes for basic PDF content creation and rendering, including text, images, basic colors, core fonts, marked content and general graphics.

It is centered around implementing a graphics state machine and provding support for the operators and graphics variables as listed in the PDF::API6 Graphics Documentation.

Key roles and classes:


implements a PDF graphics state machine for composition, or rendering:

use lib 't';
use PDF::Content;
use PDF::Content::Canvas;
use PDFTiny;
my PDFTiny $pdf .= new;
my PDF::Content::Canvas $canvas = $pdf.add-page;
my PDF::Content $gfx .= new: :$canvas;
$gfx.use-font: $pdf.core-font('Courier'); # define /F1 font
$gfx.Font = 'F1', 16;
$gfx.TextMove(10, 20);
$gfx.ShowText('Hello World');
say $gfx.Str;
# BT
#  /F1 16 Tf
#  10 20 Td
#  (Hello World) Tj
# ET


handles the loading of some common image formats

It currently supports: PNG, GIF and JPEG.

use PDF::Content::XObject;
my PDF::Content::XObject $image .= open: "t/images/lightbulb.gif";
say "image has size {$image.width} X {$image.height}";
say $image.data-uri;
# data:image/gif;base64,R0lGODlhEwATAMQA...


provides simple support for core fonts

use PDF::Content::Font::CoreFont;
my PDF::Content::Font::CoreFont $font .= load-font( :family<Times-Roman>, :weight<bold> );
say $font.encode("¶Hi");
say $font.stringwidth("RVX"); # 2166
say $font.stringwidth("RVX", :kern); # 2111


a utility class for creating boxed text content for output by print() or say():

use lib 't';
use PDFTiny;
my $page = PDFTiny.new.add-page;
use PDF::Content;
use PDF::Content::Font::CoreFont;
use PDF::Content::Text::Block;
my PDF::Content::Font::CoreFont $font .= load-font( :family<helvetica>, :weight<bold> );
my $text = "Hello.  Ting, ting-ting. Attention! … ATTENTION! ";
my PDF::Content::Text::Box $text-box .= new( :$text, :$font, :font-size(16) );
my PDF::Content $gfx = $page.gfx;
say $gfx.Str;


Simple Color construction functions:

use lib 't';
use PDFTiny;
my $page = PDFTiny.new.add-page;
use PDF::Content;
use PDF::Content::Color :color, :ColorName;
my PDF::Content $gfx = $page.gfx;
$gfx.FillColor = color Blue; # named color
$gfx.StrokeColor = color '#fa9'; # RGB mask, 3 digit
$gfx.StrokeColor = color '#ffaa99'; # RGB mask, 6 digit
$gfx.StrokeColor = color [1, .8, .1, .2]; # CMYK color values (0..1)
$gfx.StrokeColor = color [1, .5, .1];     # RGB color values (0..1)
$gfx.StrokeColor = color [255, 127, 25];  # RGB color values (0..255)
$gfx.StrokeColor = color .7; # Shade of gray
use Color;
my Color $red .= new(0xff, 0x0a, 0x0a);
$gfx.StrokeColor = color $red; # Color objects


This class assists in the detection or construction of marked content in page or xobject form content streams:

use lib 't';
use PDFTiny;
use PDF::Content::XObject;
use PDF::Content::Tag :ParagraphTags, :IllustrationTags;

my PDFTiny $pdf .= new;

my $page = $pdf.add-page;
my $header-font = $pdf.core-font: :family<Helvetica>, :weight<bold>;
my $body-font = $pdf.core-font: :family<Helvetica>;

$page.graphics: -> $gfx {
    my PDF::Content::Tag $tag;

    $tag = $gfx.mark: Header1, {
        .say('Header text',
             :position[50, 120]);

    say $tag.name.Str; # 'H1'
    say $tag.mcid;     # marked content id of 0

    $tag = $gfx.mark: Paragraph, {
        .say('Paragraph that contains a figure', :position[50, 100], :font($body-font), :font-size(12));

        # nested tag. Note: marks cannot be nested, but tags can
        .tag: Figure, {
            my PDF::Content::XObject $img .= open: "t/images/lightbulb.gif";
            .do: $img, :position[50,70];


    say $tag.name.Str;         # 'P'
    say $tag.mcid;             # marked content id of 1
    say $tag.kids[0].name.Str; # 'Figure'

say $page.gfx.tags.gist; # '<H1 MCID="0"/><P MCID="1"><Figure/></P>';


This class includes the methods:

page-fragment - produce a single page fragment, not attached to any PDF pages-fragment - produce a page-tree fragment, not attached to any PDF

These stand-alone fragments aim to be thread-safe to allow parallel construction of pages. The final PDF assembly needs to be synchronous.

use PDF::Content::Page;
use PDF::Content::PageTree;
use lib 't';
use PDFTiny;

my PDFTiny $pdf .= new;
my PDF::Content::Page @pages;

@pages = (1..20).hyper(:batch(1)).map: -> $page-num {
    my PDF::Content::Page:D $page = PDF::Content::PageTree.page-fragment;
    $page.text: {
        .text-position = 50, 400;
        .say: "Page $page-num";

$pdf.add-page($_) for @pages;

See Also