BUILD 15/30: Super Simple PDF Progress

Posted Friday, October 31, 2025 by Sri. Tagged JOURNAL

I'm not feeling well today, so I just ported the minimum PDF page generation code to refamiliarize myself with pdf-lib. Notes follow.

About the Title Picture: I'm not feeling well today, so I limited today's work to generating this simple PDF with some lines and text.Adobe Acrobat interface displaying a very simple PDF with text 'FORM.WCC-01' and copyright information drawn inside light blue page marginAdobe Acrobat interface displaying a very simple PDF with text 'FORM.WCC-01' and copyright information drawn inside light blue page margin (full size image)

Currently I'm still experiencing unusually lethargic, nauseous, and brain-fogged days. This appears to be cyclical, and typically strikes around the end of the month and lasts for about a week. It's unusual because I can't identify a particular trigger like bad eating, bad sleep, or other stressors. During times like this, I try to get done what I can get done, so today I'm just making a TINY BIT OF PROGRESS on the Word Counting Calendar.

Revisiting PDF Generation

I'm using pdf-lib, a NodeJS library that is pretty modern but is not maintained. I wrote code last year to generate 365-page Emergent Task Planner Journals (ETPJ) that I used to sell, but now provide as a perk for follows on my Patreon. The ETP relatively simple because it just places some static text and a few form boxes on a top of a blank ETP pdf. The Word Counting Calendar, by comparison, requires a lot more dynamic generation.

For today, I copied the PDF page setup from the ETPJ code into the WCC code:

async function MakePDF(year = 2025, month = 11, pageSize = 'Letter') {  
const mm = String(month).padStart(2, '0');
const yyyy = String(year);
let path = `${yyyy}-word-counting-calendar-${mm}.pdf`;

// Create a new PDFDocument
const pdfDoc = await PDFDocument.create();
// set metadata
pdfDoc.setTitle(`Word Counting Calendar for ${yyyy}/${mm}`);
pdfDoc.setAuthor('DSri Seah');
pdfDoc.setSubject('Word Tracking Calendar');
pdfDoc.setCreator('DSRI-PDFGEN v0.1.0');
pdfDoc.setCreationDate(new Date('2025-10-31'));
pdfDoc.setModificationDate(new Date());

// create page (landscape flip width and height)
const page = pdfDoc.addPage(PageSizes[pageSize].reverse();

const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);

// draw guides
const clr_guide = cmyk(1, 0, 0, 0);
page.pushOperators(
moveTo(18, 18),
lineTo(width - 18, 18),
lineTo(width - 18, height - 18),
lineTo(18, height - 18),
lineTo(18, 18),
setLineWidth(1),
setStrokingColor(clr_guide),
stroke()
);

// write copyright line bottom right
let credit = `FORM WCC-01 • Copyright © ${yyyy} by David "Sri" Seah • dsriseah.com`;
const textWidth = helveticaFont.widthOfTextAtSize(credit.toUpperCase(), 9);
page.drawText(credit.toUpperCase(), {
x: width - textWidth - 18,
y: 18,
size: 9
});

// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc.save();
await Files.WriteFile(`${OUTDIR}/${path}`, pdfBytes);
LOG(`wrote ${path} with ${pdfBytes.length} bytes`);
}

As I was inline-porting these bits of code from the ETPJ code library, I was irked by how barebones the pdf-lib library API docs are. It's not really the library's fault, as it presumes familiarity with the PDF specification itself. I have a fading understanding of Postscript, on which PDF loosely based, but I don't have a conceptual model for using pdf-lib. The API docs is just a reference without that information.

I did find this excellent store of PDF Cheat Sheets from the PDF Association, the standards development organization for PDF. Over the weekend I'll start to assemble a "drawing reference" for pdf-lib while I build a working model of PDF in my head. As a specification, PDF is a 1990s drawing API that has its roots in early 1980s graphics technology. It inherits the printer-focused conventions from that time, so they are still somewhat familiar to me. I just need to know what the hell their names are in the pdf-lib API.

All Building Challenge Posts

This challenge starts October 11th and ends when 30 artifacts have been posted. Weekends are exempt from production.

URSYS Web App Template

Embedded TypeScript Apps in Eleventy

A Review of Old Work and Stories

Eleventy Templates for Atom Feeds

Productivity Energy Crash

Workshopping the 'Activity Bingo' Form (part 1)

Last Run of ETP Notebook Production

Activity Bingo Form Progress (part 2)

ETP Mini Notebook Printing Press Tour

Identity and Logo Thinking Pass

Unprofessional Business Cards

Word Counting Calendar Reboot

Word Counting Calendar Interim Release

Calendar Layout Code Progress

Super Simple PDF Progress

BUILD CHALLENGE COMMENTARY

I am in self-care mode, deliberate not pushing myself too hard so I can stay well-rested while retaining my working context on the software project. I had hoped I would finish the Word Counting Calendar update by November, but at least I have coverage on the Word Counting Calendar Nanowrimo Page.

This is also the end of the third week of the 30 Day Build Challenge, which marks the halfway point of the challenge! I have stayed the course, even pushing through some tough days instead of giving up for the day.

BONUS ACHIEVEMENTS

Some preliminary findings:

  • I'm getting my "blogging legs" again with blogging workflow
  • I'm learning to pace myself better to avoid burnout and frustration
  • This does feel like fulfilling work, though I'm starting to worry about revenue generation
  • I'm starting to see how all the websites fit together to create a marketplace for my work
  • I am getting all kinds of ideas for new products, but staying focused on building foundational infrastructure

While none of these are very impressive in by themselves, they are providing me with a sense of progress that makes a big difference in my mood. Optimism remains high after three weeks. I feel I am becoming more polished and smooth in my execution.


We chat about personal projects and challenges on the DS|CAFE Community Discord Server every day. Come visit! Maybe you'll make some friends!

You can reach me at Mastodon or Bluesky. Or subscribe to the blog feed to stay up-to-date.