Tighten resume PDF spacing for 1-2 Letter pages

This commit is contained in:
2026-08-01 11:33:02 -04:00
parent af4e658e72
commit ee4412d911
+44 -46
View File
@@ -10,11 +10,11 @@ function escapeHtml(s: string): string {
.replace(/"/g, """); .replace(/"/g, """);
} }
/** Compact, professional resume CSS for 12 printed pages. */ /** Compact, professional resume CSS targeted at 12 Letter pages. */
const RESUME_CSS = ` const RESUME_CSS = `
@page { @page {
size: Letter; size: Letter;
margin: 0.55in 0.6in 0.55in 0.6in; margin: 0.45in 0.55in 0.45in 0.55in;
} }
* { box-sizing: border-box; } * { box-sizing: border-box; }
html, body { html, body {
@@ -23,8 +23,8 @@ const RESUME_CSS = `
color: #1a1a1a; color: #1a1a1a;
background: #fff; background: #fff;
font-family: "Liberation Serif", "Times New Roman", "Nimbus Roman", Times, serif; font-family: "Liberation Serif", "Times New Roman", "Nimbus Roman", Times, serif;
font-size: 10.5pt; font-size: 9.5pt;
line-height: 1.35; line-height: 1.28;
-webkit-print-color-adjust: exact; -webkit-print-color-adjust: exact;
print-color-adjust: exact; print-color-adjust: exact;
} }
@@ -37,38 +37,38 @@ const RESUME_CSS = `
header { header {
text-align: center; text-align: center;
margin-bottom: 10pt; margin-bottom: 7pt;
padding-bottom: 8pt; padding-bottom: 6pt;
border-bottom: 1.25pt solid #222; border-bottom: 1.1pt solid #222;
break-after: avoid; break-after: avoid;
page-break-after: avoid; page-break-after: avoid;
} }
h1 { h1 {
margin: 0 0 2pt; margin: 0 0 1pt;
font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 18pt; font-size: 16pt;
font-weight: 700; font-weight: 700;
letter-spacing: 0.02em; letter-spacing: 0.015em;
line-height: 1.15; line-height: 1.12;
} }
.title { .title {
margin: 0 0 4pt; margin: 0 0 3pt;
font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 10.5pt; font-size: 9.5pt;
font-weight: 500; font-weight: 500;
color: #333; color: #333;
} }
.contact { .contact {
margin: 0; margin: 0;
font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 9pt; font-size: 8.25pt;
color: #444; color: #444;
} }
.summary { .summary {
margin: 0 0 10pt; margin: 0 0 7pt;
font-size: 9.75pt; font-size: 8.75pt;
line-height: 1.4; line-height: 1.32;
text-align: justify; text-align: justify;
hyphens: auto; hyphens: auto;
break-inside: avoid; break-inside: avoid;
@@ -76,76 +76,75 @@ const RESUME_CSS = `
} }
.sec { .sec {
margin: 0 0 8pt; margin: 0 0 5pt;
break-inside: avoid-page;
} }
h2 { h2 {
margin: 10pt 0 5pt; margin: 7pt 0 3pt;
padding-bottom: 2pt; padding-bottom: 1.5pt;
font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 10pt; font-size: 9pt;
font-weight: 700; font-weight: 700;
letter-spacing: 0.08em; letter-spacing: 0.07em;
text-transform: uppercase; text-transform: uppercase;
color: #111; color: #111;
border-bottom: 0.75pt solid #888; border-bottom: 0.7pt solid #888;
break-after: avoid; break-after: avoid;
page-break-after: avoid; page-break-after: avoid;
} }
.item { .item {
margin: 0 0 7pt; margin: 0 0 5pt;
break-inside: avoid; break-inside: avoid;
page-break-inside: avoid; page-break-inside: avoid;
} }
.item:last-child { margin-bottom: 0; } .item:last-child { margin-bottom: 0; }
h3 { h3 {
margin: 0 0 1pt; margin: 0;
font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 10.25pt; font-size: 9.25pt;
font-weight: 700; font-weight: 700;
line-height: 1.25; line-height: 1.2;
break-after: avoid; break-after: avoid;
page-break-after: avoid; page-break-after: avoid;
} }
.meta { .meta {
margin: 0 0 3pt; margin: 0 0 2pt;
font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 8.75pt; font-size: 8pt;
color: #555; color: #555;
font-style: italic; font-style: italic;
break-after: avoid; break-after: avoid;
page-break-after: avoid; page-break-after: avoid;
} }
.item p { .item p {
margin: 0 0 2pt; margin: 0 0 1.5pt;
font-size: 9.5pt; font-size: 8.75pt;
line-height: 1.35; line-height: 1.28;
} }
ul { ul {
margin: 2pt 0 0 14pt; margin: 1pt 0 0 12pt;
padding: 0; padding: 0;
} }
li { li {
margin: 0 0 1.5pt; margin: 0 0 1pt;
font-size: 9.5pt; font-size: 8.75pt;
line-height: 1.32; line-height: 1.26;
} }
.skills-line { .skills-line {
margin: 0 0 3pt; margin: 0 0 2pt;
font-size: 9.5pt; font-size: 8.75pt;
line-height: 1.35; line-height: 1.28;
break-inside: avoid; break-inside: avoid;
page-break-inside: avoid; page-break-inside: avoid;
} }
.skills-line strong { .skills-line strong {
font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Liberation Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 9.25pt; font-size: 8.5pt;
} }
.links-list { .links-list {
margin: 2pt 0 0 14pt; margin: 1pt 0 0 12pt;
columns: 2; columns: 2;
column-gap: 18pt; column-gap: 14pt;
} }
.links-list li { .links-list li {
break-inside: avoid; break-inside: avoid;
@@ -162,8 +161,7 @@ const RESUME_CSS = `
break-after: avoid; break-after: avoid;
page-break-after: avoid; page-break-after: avoid;
} }
/* Prefer keeping a heading with at least part of the next block */ p, li {
h2 {
orphans: 3; orphans: 3;
widows: 3; widows: 3;
} }
@@ -274,7 +272,7 @@ export async function resumePdfRoutes(app: FastifyInstance) {
printBackground: true, printBackground: true,
preferCSSPageSize: true, preferCSSPageSize: true,
displayHeaderFooter: false, displayHeaderFooter: false,
margin: { top: "0.55in", bottom: "0.55in", left: "0.6in", right: "0.6in" }, margin: { top: "0.45in", bottom: "0.45in", left: "0.55in", right: "0.55in" },
}); });
const filename = `${(resume.fullName || "resume").replace(/\s+/g, "_")}_Resume.pdf`; const filename = `${(resume.fullName || "resume").replace(/\s+/g, "_")}_Resume.pdf`;
return reply return reply