Decision letters, generated

September 2023 · internal

The Korean Language Flagship Center sends decision letters on branded letterhead. Staff were filling those by hand. I built a small PHP page that takes a name, a date, and an accept or deny, then writes a PDF with the logo, the flag, and a signature already in the right place.

It is an internal tool. The interesting part is not the form. It is getting Times at 12pt to sit on a page the way a paper letter sits on a desk.

Form for generating a KLFC decision letter.
The form. Four fields, on purpose.
  GET  status, firstName, lastName, date
              |
              v
        FPDF subclass
          Header()  logo + flag + date
          body      decision text
          footer    signature
              |
              v
           letter.pdf
        

FPDF, coordinates, and Times

FPDF does not care about your CSS. You place images in inches and hope the printer margin agrees. The header subclass draws logo.png on the left and flag.png on the right, sets Times, then prints a date like "January 1, 2024" from whatever string the form sent.

PHP FPDF header code placing logo, flag, and formatted date.
Header override. Names come in through query params, then ucfirst, then the page.
More of the FPDF PHP used to write the letter body.
The rest of the letter is still just coordinates and cells.
class PDF extends FPDF {
  function Header() {
    $this->Image('images/logo.png', 1, 0.44, 2, 0.7);
    $this->Image('images/flag.png', 5.37, 0.44, 2.13, 0.7);
    $this->SetFont('Times', '', 12);

    $pretty = (new DateTime($this->letterDate))->format('F j, Y');
    $this->Cell(0, 0.4, $pretty, 0, 1, 'R');
  }
}

That block matches the screenshot. I would not pass raw query params into a PDF in a public app without a stricter allowlist. This tool sits inside the center. The threat model is a tired staff member, not the open internet.

Then you click generate

The output has to look like the letters they already mailed, or nobody will switch. Letterhead, body, signature. Bootstrap 5 on the form because it was 2023 and I wanted the button to be a button.

Generated decision letter preview.
Generate. If the flag is a millimeter off, someone will say so.

Later I rebuilt the public application form with a third-party service, which is a different essay. This one is the PDF. A two-hour job that saved a lot of copy and paste, and taught me that official paper is a layout problem dressed up as a software problem.

All projects