Přeskočit na obsah

PDFKit autopage při doc.text() pod margins.bottom

Jednostránková faktura/dokument se renderuje jako 2–3+ stránek. Druhá+ stránka má prakticky nic (jen footer / brand pásek), první stránka vypadá v pořádku.

PDFKit’s automatický page-break mechanismus se nedotýká jen wrap-text. Při volání doc.text(text, x, y, opts) s absolutní Y souřadnicí, která spadá pod hranici pageHeight - margins.bottom, knihovna interně zavolá doc.addPage() a text vykreslí na novou stránku.

Vzorový smrtící bug — footer “Strana X/Y” v patičce každé stránky:

const doc = new PDFDocument({ size: "A4", margins: { bottom: 50 } });
// ... obsah stránky ...
const range = doc.bufferedPageRange();
for (let i = 0; i < range.count; i++) {
doc.switchToPage(range.start + i);
// PAGE_HEIGHT = 841, margins.bottom = 50 → safe zone end = 791
// Y = 841 - 50 + 14 = 805 > 791 → PDFKit volá addPage!
doc.text("Strana", MARGIN, 805);
}

Každé volání ve footer loop přidá další prázdnou stránku, a ta nová stránka má vlastní footer výzvu → kaskáda.

Buď posuň footer do safe zone, nebo sniž margins.bottom:

const doc = new PDFDocument({
size: "A4",
margins: { top: 42, bottom: 18, left: 52, right: 45 }, // bottom small
});
// Footer Y kreslit těsně PŘED page edge, ale aby bottom margin
// měl alespoň ~6pt rezervu pod textem
const fy = PAGE_HEIGHT - 28; // 28pt nad bottom edge = bezpečné

Sanity check: footerY + textHeight < pageHeight - margins.bottom

  • Stejné chování má doc.heightOfString() při measure — pokud text overflow, PDFKit addPage během render. Vždy spočítat requiredSpace před začátkem bloku a v případě potřeby ručně addPage().
  • bufferPages: true umožňuje pozdější switchToPage() ale nezruší tuto auto-pagination logiku.

Po vygenerování zkontroluj počet stránek:

const pageCount = (pdfBuffer.toString("latin1").match(/\/Type\s*\/Page[^s]/g) ?? []).length;
if (pageCount > expectedPages) {
console.warn(`PDF má ${pageCount} stránek, čekáno ${expectedPages}`);
}

Lokálně přes Python:

Terminál
python3 -c "import re; d=open('out.pdf','rb').read(); print(len(re.findall(rb'/Type\s*/Page[^s]',d)))"
Přidal aiarchitekt.cz · 5. 6. 2026 2:00
Provozuje aiarchitekt.cz