Fix PDF page breaks to properly respect margins on all pages

- Rewrote PDF export to render content page-by-page instead of as one image
- Calculate total content height and number of pages needed
- Create separate canvas for each page with proper dimensions
- Use overflow:hidden wrapper to show only one page's worth of content at a time
- Position content with negative top offset for each subsequent page
- Each page gets proper 20mm margins on all sides
- Fixes issue where content flowed continuously across pages
- Bottom margins now properly respected on every page
This commit is contained in:
drelich
2026-03-17 10:20:36 +01:00
parent 12579d6198
commit e018b9e1e9

View File

@@ -153,22 +153,41 @@ export function NoteEditor({ note, onUpdateNote, fontSize, onUnsavedChanges }: N
return; return;
} }
// Create a temporary container with better styling for PDF // Create PDF
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'mm',
format: 'a4',
});
const pageWidth = 210; // A4 width in mm
const pageHeight = 297; // A4 height in mm
const margin = 20; // 20mm margins on all sides
const contentWidth = pageWidth - (2 * margin); // 170mm
const contentHeight = pageHeight - (2 * margin); // 257mm
// Create a temporary container with proper page dimensions
const container = document.createElement('div'); const container = document.createElement('div');
const contentWidth = 170; // A4 width (210mm) - margins (20mm each side)
container.style.width = `${contentWidth}mm`; container.style.width = `${contentWidth}mm`;
container.style.minHeight = `${contentHeight}mm`;
container.style.padding = '0'; container.style.padding = '0';
container.style.backgroundColor = 'white'; container.style.backgroundColor = 'white';
container.style.position = 'absolute'; container.style.position = 'absolute';
container.style.left = '-9999px'; container.style.left = '-9999px';
container.style.top = '0';
container.style.fontFamily = 'Arial, sans-serif'; container.style.fontFamily = 'Arial, sans-serif';
container.style.fontSize = '12px';
container.style.lineHeight = '1.6';
container.style.color = '#000000';
// Add title // Add title
const titleElement = document.createElement('h1'); const titleElement = document.createElement('h1');
titleElement.textContent = localTitle || 'Untitled'; titleElement.textContent = localTitle || 'Untitled';
titleElement.style.marginTop = '0';
titleElement.style.marginBottom = '20px'; titleElement.style.marginBottom = '20px';
titleElement.style.fontSize = '24px'; titleElement.style.fontSize = '24px';
titleElement.style.fontWeight = 'bold'; titleElement.style.fontWeight = 'bold';
titleElement.style.color = '#000000';
container.appendChild(titleElement); container.appendChild(titleElement);
// Clone and add content // Clone and add content
@@ -180,46 +199,57 @@ export function NoteEditor({ note, onUpdateNote, fontSize, onUnsavedChanges }: N
document.body.appendChild(container); document.body.appendChild(container);
// Convert to canvas // Get the total height of content
const canvas = await html2canvas(container, { const totalHeight = container.scrollHeight;
scale: 2, const pixelsPerMm = container.offsetWidth / contentWidth;
useCORS: true, const contentHeightPx = contentHeight * pixelsPerMm;
backgroundColor: '#ffffff',
});
// Remove temporary container // Calculate number of pages needed
document.body.removeChild(container); const numPages = Math.ceil(totalHeight / contentHeightPx);
// Create PDF with multi-page support and margins // Render each page separately
const pdf = new jsPDF({ for (let pageNum = 0; pageNum < numPages; pageNum++) {
orientation: 'portrait', // Create a wrapper for this page's content
unit: 'mm', const pageWrapper = document.createElement('div');
format: 'a4', pageWrapper.style.width = `${contentWidth}mm`;
}); pageWrapper.style.height = `${contentHeight}mm`;
pageWrapper.style.overflow = 'hidden';
pageWrapper.style.position = 'absolute';
pageWrapper.style.left = '-9999px';
pageWrapper.style.top = '0';
pageWrapper.style.backgroundColor = 'white';
const pageWidth = 210; // A4 width in mm // Clone the container and position it to show only this page's content
const pageHeight = 297; // A4 height in mm const pageContent = container.cloneNode(true) as HTMLElement;
const margin = 20; // 20mm margins on all sides pageContent.style.position = 'relative';
const contentWidthMm = pageWidth - (2 * margin); pageContent.style.top = `-${pageNum * contentHeightPx}px`;
const contentHeightMm = pageHeight - (2 * margin); pageWrapper.appendChild(pageContent);
const imgWidth = contentWidthMm; document.body.appendChild(pageWrapper);
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = 0;
// Add first page with margins // Render this page to canvas
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', margin, margin + position, imgWidth, imgHeight); const canvas = await html2canvas(pageWrapper, {
heightLeft -= contentHeightMm; scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
width: pageWrapper.offsetWidth,
height: pageWrapper.offsetHeight,
});
// Add additional pages if needed // Remove the page wrapper
while (heightLeft > 0) { document.body.removeChild(pageWrapper);
position = heightLeft - imgHeight;
pdf.addPage(); // Add page to PDF (add new page if not first)
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', margin, margin + position, imgWidth, imgHeight); if (pageNum > 0) {
heightLeft -= contentHeightMm; pdf.addPage();
}
const imgData = canvas.toDataURL('image/png');
pdf.addImage(imgData, 'PNG', margin, margin, contentWidth, contentHeight);
} }
// Remove the original container
document.body.removeChild(container);
// Save the PDF // Save the PDF
const fileName = `${localTitle || 'note'}.pdf`; const fileName = `${localTitle || 'note'}.pdf`;