“I mean, I do not like guns. I have guns. I do not like them. I always compare them to antibiotics. I never want to take one, but I’m glad they exist because I’m as small as some women.”
— Bill MaherAlso known as “adding epicycles”, referring to a specific variety of scope creep where each decision in the project design opens an entirely new layer of crap to deal with.
It started with the idea of using Cairo to create PDF files using modern fonts that don’t work well with PDF::API2’s aging codebase. That pulled in Font::FreeType, and together they were enough to convert one of my simple-but-useful scripts, and seemed like 90% of what I needed to create a drop-in replacement for PDF::API2::Lite.
Except that I had to create an extra save/restore state stack to handle the fact that PDF has both a stroke color and a fill color, and Cairo only has one.
Also, Cairo doesn’t support the standard 35 PDF fonts, which are the standard PostScript printer fonts, which are Times, Helvetica, Symbol, etc. In fact, unless you load font files from disk, you’re limited to specifying the CSS2 generic fonts (“serif”, “sans-serif”, “monospace”), and wondering what it will decide that means each time you run your scripts.
So I need to support and/or supply free alternatives, such as the ones from URW that are used with Ghostscript, and Adobe’s Source Han Serif and Sans. And, yes, even if you do have the real fonts Adobe supplies with Acrobat, they’ll end up embedded in the output; PDF output is really kind of a sideline for Cairo, and honestly, I don’t think the authors expect anyone to own commercial fonts.
That should be enough, right? Except that in all of the Cairo documentation, the text-display methods are referred to as the “toy” API, and if you want to really use fonts for more than basic “this un-kerned string goes on the page at x,y”, you need to use a real layout engine like Pango.
Which doesn’t let you load arbitrary font files from disk, blowing up all the scaffolding I’ve just built around the assumption that Cairo’s FreeType support is supposed to actually be used.
Pango doesn’t just layout text, it uses span tags to mark up a string with font family, weight, etc, and uses Fontconfig to figure out what font you really wanted, quickly falling back all the way to the most generic font it can find on your computer, which on my Mac is Verdana.
So now it’s not enough to download a set of metrics-compatible OpenType fonts, I also need to create custom XML config files that override whatever else may be installed on a machine, and install them in a location that fontconfig is guaranteed to process first. XML, because there’s nothing like a text-based config parser that, by definition, must abort when it detects even the tiniest error.
There is a way to tell Pango to tell fontconfig to load a single font file from disk, but those methods aren’t exposed in the Perl Pango module. Which means I’d have to dig into the guts of how you link C libraries into Perl modules, add all the necessary functions and a set of tests, submit a patch to that project, wait for a release, and then at last C-ko would be mine!
Spoiler: yeah, not gonna happen.
By the way, it looks like no one has ever published a non-trivial example of how to use the Cairo, Pango, or Font::FreeType Perl modules, separately or together. Even a lot of the test code is little more than “yup, that called the C API and returned something”. Saying “see the C library web site” isn’t terribly Perl-y, given that everyone in the open source world has basically decided that automatically-generated API dumps are all the documentation anyone should ever need.
Which created Yet Another Problem, because it seems no one has ever
even used the create_for_stream() method to render a PDF into
memory and save it at the end. Which I need in order to transparently
emulate PDF::API2::Lite’s saveas() method.
Therefore I present what is apparently the world’s first working Perl script that uses a Cairo PDF stream:
#!/usr/bin/env perl
use Cairo;
my $DATA;
my $surface = Cairo::PdfSurface->create_for_stream(
sub {
my ($whatever, $data) = @_;
$DATA .= $data;
},
"whatever", 320, 240,
);
my $pdf = Cairo::Context->create($surface);
$pdf->move_to(10, 48);
$pdf->set_font_size(32);
$pdf->show_text("Hamburgefontsiv");
$surface->flush;
$surface->finish;
open(Out,'>:raw', "test.pdf");
print Out $DATA;
close(Out);
exit 0;
(if you just use the create() method that requires a file name, then
you don’t have to flush, finish, or save your output at all; it just
happens when the script exits)
But the fun never stops! Would you believe that there is no user documentation for the lookup strings used by Fontconfig? Everyone just copies the examples from the ‘user’ documentation that tells you all about the XML config format and very little about exactly what properties and constants you can specify:
Fontconfig provides a textual representation for patterns that the
library can both accept and generate. The representation is in three
parts, first a list of family names, second a list of point sizes and
finally a list of additional properties:
<families>-<point sizes>:<name1>=<values1>:<name2>=<values2>...
Values in a list are separated with commas. The name needn't include
either families or point sizes; they can be elided. In addition, there
are symbolic constants that simultaneously indicate both a name and a
value. Here are some examples:
Name Meaning
----------------------------------------------------------
Times-12 12 point Times Roman
Times-12:bold 12 point Times Bold
Courier:italic Courier Italic in the default size
Monospace:matrix=1 .1 0 1 The users preferred monospace font
with artificial obliquing
That’s all you get. Everywhere. The rest of the “user” documentation
is about the zillions of XML config files that get parsed to decide
what to do with these strings, which adds another half-dozen
epicycles.
The only complete documentation I can find on how these strings are
parsed is the commentless source code in src/fcname.c. Maybe.
This is the kind of stuff that’s under the hood of desktop Linux, which is why everyone uses Windows and Mac.
First script successfully converted using the PDF::API2::Lite
compatibility methods. Well, except for the font-specific methods that
I need to create a wrapper class for; I can get all the data I need
from Cairo’s font_extents() and text_extents() methods, but those
only return data for the current font at the current size, where the
ones from PDF::API2 calculate them at load time for a size of 1 point.
Not a huge issue, just another epicycle…
New epicycle: fonts lie. The text_extents() methods do the right thing, but the Cairo documentation confesses that many of the elements returned by font_extents() are the font designers’ opinion on the correct vertical sizes of the font. This has always annoyed me, so as part of building my wrapper class, I’m going to actually render representative characters into a recording surface in order to determine the precise values of ascender(), descender(), capheight(), xheight(), and fontbbox(). Also latin_baseline(), which can be pretty goofy in some kanji fonts.
I hope the DanMachi spinoffs eventually produce a story focused on second-tier adventurer Anakitty Autumn, who is apparently the only non-insane catgirl in that universe.

(screenshot from the mobile game)
Stack Overflow has released their latest survey of straight white american male junior devs who ask questions about Javascript, Rust, and Redis. There, saved you a click.
Shattering the Rushuna barrier, it’s Ryoka Narusawa aka Ryotas, from Occultic;Nine. For the most part, fan artists are not exaggerating her bustline. Her huge tracts of land have huge tracts of land.
“She carried a pair of 44s. She also had a gun.”
While waiting on a fix for my Perl PDF font
woes, I started thinking about some of the
other long-standing issues I’ve had with
PDF::API2. I actually use
PDF::API2::Lite most of the time, with a few extra functions (like
clip) merged back in, because I don’t need color spaces, bar codes,
metadata, outlines, etc, etc. I just need all of the drawing functions
and decent font handling, including proper subsetting in the output
file.
Which it doesn’t do. Pro tip: the easiest way to clean up a PDF file
full of cruft like giant embedded fonts is to run it through the
ps2pdf utility supplied with
Ghostscript. Despite the name, it works
just fine as a pdf-to-pdf converter and optimizer.
Bottom line, PDF::API2 has been getting staler every year, and the font thing encouraged me to look elsewhere. Phil Perry is taking a stab at it with his fork PDF::Builder, but he’s still feeling his way around, and it’s a big job.
But I’ve written a lot of code over the years, and the PDF APIs I’ve looked at in other languages are either convoluted, limited in function (report writers and form fillers, mostly), just plain missing, and in at least one case commercial, and that generally applies to the other modules I rely on, too, like the Swiss Army Knife of date handling, DateTime.
By happy accident, I discovered that the Cairo library will generate PDF files, and integrates nicely with FreeType for robust font support, and they both have decent Perl modules. Being C libraries, they also have some performance advantages, but the most important thing they have is support.
Documentation and examples are pretty limited for the Perl modules, but I just finished successfully converting my calendar generator to use them, and it was only moderately annoying. The two biggest issues were that Cairo thinks (0,0) is the upper left corner, and that it only tracks one color in its graphics state, compared to PDF’s separate stroke and fill colors. Shimming around these problems added about 55 lines of code. Text positioning is improved thanks to Font::FreeType (and can be improved a lot more by exploiting the API fully), and output with embedded fonts is significantly smaller. Also, all my fonts work.
Now that I have a working example, I’ve started work on a wrapper module that works like PDF::API2::Lite, so that I can convert my old scripts by just editing a few lines at the top. Shouldn’t be too much work, and I’m tentatively calling it PDF::Cairo.
There’s also a Pango module for advanced text layout, but integrating that is definitely a “phase 2” item.
As far as I can tell, Pango offers the exact opposite of what I want, turning font lookup into a game where the penalty for guessing wrong is having all your text rendered in Verdana.