mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-14 23:07:01 +10:00
fix(printer): make PDF margins match resume background color (#2770)
* fix(printer): match PDF margins to resume background color * fix(printer): preserve vertical margins when content wraps pages
This commit is contained in:
@@ -99,15 +99,15 @@ export const printerService = {
|
||||
const token = generatePrinterToken(id);
|
||||
const url = `${baseUrl}/printer/${id}?token=${token}`;
|
||||
|
||||
// Step 3: Calculate PDF margins
|
||||
// Some templates require margins to be applied via PDF (they use print:p-0 to remove CSS padding)
|
||||
// Convert from CSS pixels to PDF points (divide by 0.75 since 1pt = 0.75px at 72dpi)
|
||||
let marginX = 0;
|
||||
let marginY = 0;
|
||||
// Step 3: Calculate print paddings for templates that disable CSS padding in print mode.
|
||||
// We render these margins inside the page (not via Puppeteer's PDF margins), so the margin
|
||||
// area matches the resume background color instead of staying white.
|
||||
let pagePaddingX = 0;
|
||||
let pagePaddingY = 0;
|
||||
|
||||
if (printMarginTemplates.includes(template)) {
|
||||
marginX = Math.round(data.metadata.page.marginX / 0.75);
|
||||
marginY = Math.round(data.metadata.page.marginY / 0.75);
|
||||
pagePaddingX = data.metadata.page.marginX;
|
||||
pagePaddingY = data.metadata.page.marginY;
|
||||
}
|
||||
|
||||
let browser: Browser | null = null;
|
||||
@@ -135,21 +135,65 @@ export const printerService = {
|
||||
const isFreeForm = format === "free-form";
|
||||
|
||||
const contentHeight = await page.evaluate(
|
||||
(marginY: number, isFreeForm: boolean, minPageHeight: number) => {
|
||||
(
|
||||
pagePaddingX: number,
|
||||
pagePaddingY: number,
|
||||
isFreeForm: boolean,
|
||||
minPageHeight: number,
|
||||
backgroundColor: string,
|
||||
) => {
|
||||
const root = document.documentElement;
|
||||
const body = document.body;
|
||||
const pageElements = document.querySelectorAll("[data-page-index]");
|
||||
const pageContentElements = document.querySelectorAll(".page-content");
|
||||
const container = document.querySelector(".resume-preview-container") as HTMLElement | null;
|
||||
|
||||
// Ensure PDF margins inherit the resume background color instead of defaulting to white.
|
||||
root.style.backgroundColor = backgroundColor;
|
||||
body.style.backgroundColor = backgroundColor;
|
||||
root.style.margin = "0";
|
||||
body.style.margin = "0";
|
||||
root.style.padding = "0";
|
||||
body.style.padding = "0";
|
||||
|
||||
for (const el of pageElements) {
|
||||
const pageWrapper = el as HTMLElement;
|
||||
const pageSurface = pageWrapper.querySelector(".page") as HTMLElement | null;
|
||||
|
||||
pageWrapper.style.backgroundColor = backgroundColor;
|
||||
pageWrapper.style.breakInside = "auto";
|
||||
|
||||
if (pageSurface) pageSurface.style.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
// Apply print-only margins as padding inside each page's content surface.
|
||||
if (pagePaddingX > 0 || pagePaddingY > 0) {
|
||||
for (const el of pageContentElements) {
|
||||
const pageContent = el as HTMLElement;
|
||||
|
||||
pageContent.style.boxSizing = "border-box";
|
||||
// Ensure padding is repeated on every printed fragment when content
|
||||
// flows across physical PDF pages (not just the first fragment).
|
||||
pageContent.style.boxDecorationBreak = "clone";
|
||||
pageContent.style.setProperty("-webkit-box-decoration-break", "clone");
|
||||
if (pagePaddingX > 0) {
|
||||
pageContent.style.paddingLeft = `${pagePaddingX}pt`;
|
||||
pageContent.style.paddingRight = `${pagePaddingX}pt`;
|
||||
}
|
||||
if (pagePaddingY > 0) {
|
||||
pageContent.style.paddingTop = `${pagePaddingY}pt`;
|
||||
pageContent.style.paddingBottom = `${pagePaddingY}pt`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFreeForm) {
|
||||
// For free-form: add visual gaps between pages, then measure total height
|
||||
// Convert marginY from PDF points to CSS pixels (1pt = 0.75px)
|
||||
const marginYAsPixels = marginY * 0.75;
|
||||
const numberOfPages = pageElements.length;
|
||||
|
||||
// Add margin between pages (except the last one)
|
||||
for (let i = 0; i < numberOfPages - 1; i++) {
|
||||
const pageEl = pageElements[i] as HTMLElement;
|
||||
pageEl.style.marginBottom = `${marginYAsPixels}px`;
|
||||
if (pagePaddingY > 0) pageEl.style.marginBottom = `${pagePaddingY}pt`;
|
||||
}
|
||||
|
||||
// Now measure the total height (margins are now part of the DOM)
|
||||
@@ -169,8 +213,8 @@ export const printerService = {
|
||||
// For A4/Letter
|
||||
const heightValue = minPageHeight;
|
||||
|
||||
// Subtract top + bottom margins from page height
|
||||
const newHeight = `${heightValue - marginY}px`;
|
||||
// Keep page height fixed and let in-page padding (if any) define content bounds.
|
||||
const newHeight = `${heightValue}px`;
|
||||
if (container) container.style.setProperty("--page-height", newHeight);
|
||||
root.style.setProperty("--page-height", newHeight);
|
||||
|
||||
@@ -181,7 +225,10 @@ export const printerService = {
|
||||
const index = Number.parseInt(element.getAttribute("data-page-index") ?? "0", 10);
|
||||
|
||||
// Force a page break before each page except the first
|
||||
if (index > 0) element.style.breakBefore = "page";
|
||||
if (index > 0) {
|
||||
element.style.breakBefore = "page";
|
||||
element.style.pageBreakBefore = "always";
|
||||
}
|
||||
|
||||
// Allow content within a page to break naturally if it overflows
|
||||
// (e.g., if a single page has more content than fits on one PDF page)
|
||||
@@ -190,9 +237,11 @@ export const printerService = {
|
||||
|
||||
return null; // Fixed height from pageDimensionsAsPixels for A4/Letter
|
||||
},
|
||||
marginY,
|
||||
pagePaddingX,
|
||||
pagePaddingY,
|
||||
isFreeForm,
|
||||
pageDimensionsAsPixels[format].height,
|
||||
data.metadata.design.colors.background,
|
||||
);
|
||||
|
||||
// Step 6: Generate the PDF with the specified dimensions and margins
|
||||
@@ -208,9 +257,9 @@ export const printerService = {
|
||||
printBackground: true, // Includes background colors and images
|
||||
margin: {
|
||||
bottom: 0,
|
||||
top: marginY,
|
||||
right: marginX,
|
||||
left: marginX,
|
||||
top: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user