Back to blog
Article

Report cards that print on the school's existing laser printer

Report cards that print on the school's existing laser printer
S

StriveBit

3 min readCustom Software

Report cards that print on the school's existing laser printer

A school asks us to build their report card module. They have twelve classes, two terms a year, and the printer is an HP LaserJet 1020 connected to the admin office Windows machine. The headmistress prints the entire batch — six hundred PDFs, collated — and she hand-signs each one before sending it home with students on the last day of term.

The previous system produced A4-sized PDFs that rendered correctly in a browser preview. On paper, the right margin crept into the unprintable edge zone on about twenty sheets per batch, clipping the remarks column. The headmistress's assistant had been manually re-printing those sheets.

The core issue: the printer's hardware margin is 6.35mm on all sides. The PDF generator treated A4 as a flat 210×297mm canvas with content placed right to the edge.

We set the page size explicitly in the generator — wkhtmltopdf, in this case — and define a content box inside the hardware margin.

@dataclass
class ReportCardConfig:
    page_width_mm: float = 210.0
    page_height_mm: float = 297.0
    margin_mm: float = 6.35
    # 1mm at 96dpi
    px_per_mm: float = 3.7795275591

    @property
    def margin_px(self) -> float:
        return self.margin_mm * self.px_per_mm

    @property
    def content_width_px(self) -> float:
        return (self.page_width_mm - 2 * self.margin_mm) * self.px_per_mm

    @property
    def content_height_px(self) -> float:
        return (self.page_height_mm - 2 * self.margin_mm) * self.px_per_mm

We pass these into the CSS as variables so the layout adapts at generation time rather than in the print dialog.

<style>
  @page {
    size: A4;
    margin: 6.35mm;
  }
  .report-container {
    width: var(--content-width);
    min-height: var(--content-height);
    padding: 2mm;
    box-sizing: border-box;
  }
  .marks-table {
    table-layout: fixed;
    width: 100%;
  }
  .marks-table th, .marks-table td {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 1.5mm 1mm;
  }
</style>

We measured the printer's actual margin by printing a calibration sheet with a 5mm border and checking which edges survived. The spec sheet said 6.35mm; the calibration confirmed it. We use the larger of the two numbers.

The second problem is marks tables that overflow. A class with eight subjects fits cleanly. The optional subjects added in Class 11 push the table past the page break, splitting a student's report across two sheets in a way that looks like two separate reports. We set the row height and font size so a single student's table always fits within one content box, and we force a page break before each student.

.student-report {
  break-before: page;
}
.student-report:first-child {
  break-before: avoid;
}

The third problem is signatures. The headmistress signs by hand, so we leave a 25mm gap at the bottom of each report. But she also wanted a digital signature on the duplicate stored in the system. We render the gap in the print version and fill it with the signature image in the archive version — same layout, different data source.

The tradeoff: table-layout: fixed prevents the browser from auto-sizing columns to fit content. A subject name longer than the column width gets truncated. We set the subject name column to 40mm and abbreviate "Computer Science" to "Comp. Sci." in the data layer. The class teacher reviewed and approved each abbreviation.

We sent the first batch of ten test PDFs to the school. The admin assistant printed them, checked all four edges on every sheet, and confirmed nothing was clipped. We generated the full six hundred. The assistant did not need to re-print any sheets.

The headmistress signed and distributed them on the last Friday of the term.

Back to all articles

Ready to build something great?

We help ambitious teams build software that lasts. If you're interested in working with us or want to discuss your project, let's connect.

Get in touch