A real PDF CV (not an image) with window.print

PDF document with selectable text next to an image screenshot
PDF document with selectable text next to an image screenshot

Many web CVs are exported as an image (html2canvas → PDF). They look fine, but they have a serious problem: an ATS (the automated recruiting filter) can’t read an image. Your experience ends up as an invisible .png for whoever decides if you make the first cut. I wanted the opposite: a PDF with real, selectable, parseable text, and no libraries.

The trick: window.print() + @media print

There’s no need to generate the PDF by hand. The browser already knows how: the download button just calls print, and the user picks “Save as PDF”.

<button type="button" data-no-print onclick="window.print()">
  Download PDF
</button>

The key is the print CSS. With @page I set a fixed size and remove the browser margin (that gets rid of the header/footer with the URL and date that Chrome injects):

@page {
  size: A4;
  margin: 0;
}

Force the light palette and hide what shouldn’t reach paper

The site is dark; the PDF must be light. In @media print I redefine the color variables and hide everything that isn’t the CV: the header, the footer, and anything flagged as data-no-print (the button itself, banners, etc.):

@media print {
  :root,
  .dark {
    --color-background: #ffffff;
    --color-foreground: #101216;
  }
  #header,
  footer,
  [data-no-print] {
    display: none !important;
  }
}

Don’t break badly across pages

The last detail is avoiding a section splitting with its heading orphaned at the bottom of a page. With break-inside: avoid on each block, if it doesn’t fit, it jumps to the next page whole:

#resume-content > section {
  break-inside: avoid;
}

Conclusion

window.print() + @media print + @page give you a real-text PDF, with no html2canvas or PDF libraries, and one concrete advantage: an ATS can read it. For a CV — which is exactly what an ATS is going to process — that’s the difference between passing the filter or not.