I wanted to give myself the Xmas gift of a 2026 pdf journal this year.
I had grand plans to create a fully fledged library for doing this, but ironically enough I just wasn’t that organised!
But courtesy or the comprehensive PDF api by dwarring https://raku.land/zef:dwarring/PDF::API6 and with help from tbrowder’s Date::Names for human dates https://raku.land/zef:tbrowder/Date::Names and, of course, all the folk who have brought raku/rakulang to us, here’s my script
First, let’s import all the libraries we need – you’ll need to use zef to install what you don’t have:
use PDF::API6;
use PDF::Content::Color :rgb;
use PDF::Annot::Link;
use PDF::Destination :Fit;
use PDF::Content::FontObj;
use Date::Names;
We’re going to do a lot of hardcoding towards A4, and just work for 2026 in this case so developing this further will certainly involve more variables! But create a pdf, set it to ‘A4’ and create our start date which has to be the first of the fist due to all the counting I’m doing on my hands and toes
my $pdf = PDF::API6.new();
$pdf.media-box = 'A4';
my $start_date = Date.new(2026, 1, 1);
my PDF::Page $page;
my PDF::Content::FontObj $font = $pdf.core-font('Helvetica-Bold');
We’re also going to add an index to the pdf, so let’s set up an array for that and a reusable title variable
my $bookmarks = [];
my $bookmark-title = '';
We’re going to create a pdf with all the pages we need. At first I was going to create page content as I went, but for linking to other pages, the page must exist.
So here we create a big, blank pdf that has 12 pages at the front for the months of the year and 365 pages following for every day of the year. And by using days-in-year we’ll be ready when we update to do years involving leap years.
for 1..(12 + $start_date.days-in-year) {
$page = $pdf.add-page;
};
This is a handy sub taken from the very full documentation for PDF::Api which will make our links tidier, and you’ll see used in the following loop
sub dest(|c) { :Dest($pdf.destination(|c)) }
Let’s start filling the pages!
We’ll do the first 12 pages will are our months of 2026. Use the handy raku loop shortcut of creating our $month variable. And we know our months are pages 1-12 of our blank pdf.
We want a variable we can change that starts as the start date, but we don’t want to change the actual start date. And also we’ll be using nice date names as we’ll be writing dates on our PDF for real people – me!
It’s worth mentioning that the co-ordinates on pdfs are (x, y) – with x being how far from the left and y being how far from the bottom you are plotting.
So (0,0) on a pdf is the bottom left.
my $current_date = $start_date;
my $nice_date_name = Date::Names.new;
for 1..12 ->$month {
$page = $pdf.page($month); # Access the first 12 pages in turn
my $text = ($nice_date_name.mon($current_date.month)); # Set text ie 'January', 'February'...
$bookmark-title = $nice_date_name.mon($current_date.month); # Also remember our document outline
$bookmarks.push( %(( :Title($bookmark-title)), dest(:page($page))));
$page.gfx.text: {
.font = $font, 20; # We set our font earlier as a variable, but hardcoding the size here
.text-position = 250, 800; # And we're targeting the top middle of the A4 pdf page
.say($text); # Boom! We've written on our page, but not done yet...
}
So, one page at a time we’re writing the month at the top of the pdf page – all in memory so far. But it might be nice to decorate a bit further.
We’ll continue the loop by putting in the day number of the month, with the day and a helpful horizontal line for writing notes on.
But we’re going to make that link to the page for that particular day too, so we can make overview notes here, but jump to the full page for more detailed notes.
(It should also be noted that I tried this in the kindle app on Android and the links didn’t work, but they worked fine on Samsung notes and my pdf app on my PC – think this is actually a limitation of kindle rather than me.)
my $height = 750; # So, this is nearish the top in 'points' on an A4 pdf
for 1..$current_date.days-in-month ->$day { # days-in-month - built into raku and very handy!
my $text = $day ~ " (" ~ $nice_date_name.dow($current_date.day-of-week, 3) ~ ")";
# So our text will look like '1 (Mon)', '2 (Tue)' or whatever…
$page.gfx.text: {
.font = $font, 20; # We're creating text as before
# We're going to draw a line under this text, so a little positional adjustment
.text-position = 20, $height + 2;
# This text is going to be a link rather than plain text!
my PDF::Annot::Link $link = $pdf.annotation(
:border-style({:width(0)}),
:page($page),
:text($text),
# Using our sub for the intricacies, let's make the target the main page for this date
# But adjusting it by 12 because our first 12 pages in the pdf are the months of the year.
# And this is why we created the whole pdf at the beginning - linking to a page that doesn't
# exist will break.
|dest(:page( $current_date.day-of-year + 12 )),
);
};
So we’ve done two types of text – plain text and linking text – now lets draw our lines under the date we just wrote.
$page.gfx.paint: :fill, :stroke, { # Painting rather than texting
.StrokeColor = rgb(.211, .211, .211);
.LineWidth = 2.5;
.current-point = (20, $height); # Lines go from somewhere...
.LineTo(575, $height); # ...to somewhere
};
$height = $height - 24; # We want our next date and the line to be lower
$current_date++; # Add we're in a loop for this month so let's go to the next day
};
};
So, we’ve now gone through our first 12 pages, putting the month name at the top and writing all the days vertically down the page, with a horizontal line for writing notes on. All very profesh!
Next we want to go through the daily pages of the journal. For these we’re just going to create a blank page with the day’s date on the top.
Obviously, there’s scope to go through the line drawing again, or perhaps look into drawing a border with a rectangle? Or maybe print a random quotation from somewhere on it, or use a pdf template you’ve drawn in some other app and just want to print the days date on.
We’re doing this for the joy of being able to personalise our digital stationary – you can of course spend not very much money buying something very nice off Etsy, but it’s nice to have so many options.
$current_date = $start_date; # Let's remember to reset to the first day of the year
for 13..($start_date.days-in-year + 12) ->$day { # Adjust for our 12 monthly pages at the beginning
$page = $pdf.page($day);
my $text = (
$nice_date_name.dow($current_date.day-of-week)
~ ' ' ~
$current_date.day
~ ' ' ~
$nice_date_name.mon($current_date.month)
); # So, 'Monday 12 January', 'Friday 29 May' or whatever
# Remembering we're doing our outline...
$bookmark-title = $nice_date_name.dow($current_date.day-of-week) ~ ' ' ~ $current_date.day ~ ' ' ~ $nice_date_name.mon($current_date.month);
$bookmarks.push( %(( :Title($bookmark-title)), dest(:page($page))));
$page.gfx.text: {
.font = $font, 20;
.text-position = 200, 800;
.text-position = 200, 800;
my PDF::Annot::Link $link = $pdf.annotation(
:border-style({:width(0)}),
:page($page),
:text($text),
# So we're creating a link as before and we want to link to one of the first 12 pages
# that corresponds to the month we're in
|dest(:page($current_date.month)),
);
};
$current_date++; # Move onto the next day
};
Then the last things we need to do are save our outline to the pdf and save our pdf for wrapping up and putting under the digital tree.
$pdf.outlines.kids = $bookmarks;
$pdf.save-as($start_date.year ~ '.pdf');
And we are ready to be organised next year with a PDF journal that has handy linking and lots of opportunities for improvement. Perhaps it might be nice to add more pages into the daily part – todo, to sketch, notes. Or perhaps another section that contains all the months on one page so you can jump from there to the individual months?
But there’s plenty of scope for making it more flexible to accept a year and a page size at the very least, but for now – I have what I need.
I know it’s a good article, it’s too bad WordPress has such an unusable editing interface. Being able to drop in a valid PDF file would be very slick!
LikeLike