diff --git a/public/datasheets/h1z2z2-k-de.pdf b/public/datasheets/h1z2z2-k-de.pdf index ae6364c6..cc4d88ea 100644 Binary files a/public/datasheets/h1z2z2-k-de.pdf and b/public/datasheets/h1z2z2-k-de.pdf differ diff --git a/public/datasheets/h1z2z2-k-en.pdf b/public/datasheets/h1z2z2-k-en.pdf index efb89dd1..ed321404 100644 Binary files a/public/datasheets/h1z2z2-k-en.pdf and b/public/datasheets/h1z2z2-k-en.pdf differ diff --git a/public/datasheets/n2x2y-2-de.pdf b/public/datasheets/n2x2y-2-de.pdf index 4607204d..6463558c 100644 Binary files a/public/datasheets/n2x2y-2-de.pdf and b/public/datasheets/n2x2y-2-de.pdf differ diff --git a/public/datasheets/n2x2y-en.pdf b/public/datasheets/n2x2y-en.pdf index 24bfc181..0847b927 100644 Binary files a/public/datasheets/n2x2y-en.pdf and b/public/datasheets/n2x2y-en.pdf differ diff --git a/public/datasheets/n2xfk2y-de.pdf b/public/datasheets/n2xfk2y-de.pdf index 61ef9878..3e0322ab 100644 Binary files a/public/datasheets/n2xfk2y-de.pdf and b/public/datasheets/n2xfk2y-de.pdf differ diff --git a/public/datasheets/n2xfk2y-en.pdf b/public/datasheets/n2xfk2y-en.pdf index 4cb56748..3c45d492 100644 Binary files a/public/datasheets/n2xfk2y-en.pdf and b/public/datasheets/n2xfk2y-en.pdf differ diff --git a/public/datasheets/n2xfkld2y-de.pdf b/public/datasheets/n2xfkld2y-de.pdf index 8863b08d..e09d3373 100644 Binary files a/public/datasheets/n2xfkld2y-de.pdf and b/public/datasheets/n2xfkld2y-de.pdf differ diff --git a/public/datasheets/n2xfkld2y-en.pdf b/public/datasheets/n2xfkld2y-en.pdf index f846b890..49589750 100644 Binary files a/public/datasheets/n2xfkld2y-en.pdf and b/public/datasheets/n2xfkld2y-en.pdf differ diff --git a/public/datasheets/n2xs2y-2-de.pdf b/public/datasheets/n2xs2y-2-de.pdf index 8b9b0da0..c0bce1f9 100644 Binary files a/public/datasheets/n2xs2y-2-de.pdf and b/public/datasheets/n2xs2y-2-de.pdf differ diff --git a/public/datasheets/n2xs2y-en.pdf b/public/datasheets/n2xs2y-en.pdf index 00e59e34..f6a45d70 100644 Binary files a/public/datasheets/n2xs2y-en.pdf and b/public/datasheets/n2xs2y-en.pdf differ diff --git a/public/datasheets/n2xsf2y-2-de.pdf b/public/datasheets/n2xsf2y-2-de.pdf index 89b69c43..e6306112 100644 Binary files a/public/datasheets/n2xsf2y-2-de.pdf and b/public/datasheets/n2xsf2y-2-de.pdf differ diff --git a/public/datasheets/n2xsf2y-en.pdf b/public/datasheets/n2xsf2y-en.pdf index 49105eb0..beedeec0 100644 Binary files a/public/datasheets/n2xsf2y-en.pdf and b/public/datasheets/n2xsf2y-en.pdf differ diff --git a/public/datasheets/n2xsfl2y-2-de.pdf b/public/datasheets/n2xsfl2y-2-de.pdf index fecc4bcb..569aa889 100644 Binary files a/public/datasheets/n2xsfl2y-2-de.pdf and b/public/datasheets/n2xsfl2y-2-de.pdf differ diff --git a/public/datasheets/n2xsfl2y-3-en.pdf b/public/datasheets/n2xsfl2y-3-en.pdf index 441ad932..0cf68733 100644 Binary files a/public/datasheets/n2xsfl2y-3-en.pdf and b/public/datasheets/n2xsfl2y-3-en.pdf differ diff --git a/public/datasheets/n2xsfl2y-de.pdf b/public/datasheets/n2xsfl2y-de.pdf index 25e33007..f9c42116 100644 Binary files a/public/datasheets/n2xsfl2y-de.pdf and b/public/datasheets/n2xsfl2y-de.pdf differ diff --git a/public/datasheets/n2xsfl2y-en.pdf b/public/datasheets/n2xsfl2y-en.pdf index 487bc890..f34a6269 100644 Binary files a/public/datasheets/n2xsfl2y-en.pdf and b/public/datasheets/n2xsfl2y-en.pdf differ diff --git a/public/datasheets/n2xsy-2-de.pdf b/public/datasheets/n2xsy-2-de.pdf index dafdfa4f..f0c0492c 100644 Binary files a/public/datasheets/n2xsy-2-de.pdf and b/public/datasheets/n2xsy-2-de.pdf differ diff --git a/public/datasheets/n2xsy-en.pdf b/public/datasheets/n2xsy-en.pdf index 03c8733f..588bf17d 100644 Binary files a/public/datasheets/n2xsy-en.pdf and b/public/datasheets/n2xsy-en.pdf differ diff --git a/public/datasheets/n2xy-2-de.pdf b/public/datasheets/n2xy-2-de.pdf index 42596d2d..fd133f66 100644 Binary files a/public/datasheets/n2xy-2-de.pdf and b/public/datasheets/n2xy-2-de.pdf differ diff --git a/public/datasheets/n2xy-en.pdf b/public/datasheets/n2xy-en.pdf index a888dac4..f743bb71 100644 Binary files a/public/datasheets/n2xy-en.pdf and b/public/datasheets/n2xy-en.pdf differ diff --git a/public/datasheets/na2x2y-2-de.pdf b/public/datasheets/na2x2y-2-de.pdf index 07db2518..9fa956cf 100644 Binary files a/public/datasheets/na2x2y-2-de.pdf and b/public/datasheets/na2x2y-2-de.pdf differ diff --git a/public/datasheets/na2x2y-en.pdf b/public/datasheets/na2x2y-en.pdf index d944fe44..8868b256 100644 Binary files a/public/datasheets/na2x2y-en.pdf and b/public/datasheets/na2x2y-en.pdf differ diff --git a/public/datasheets/na2xfk2y-de.pdf b/public/datasheets/na2xfk2y-de.pdf index 03f7814b..425a8306 100644 Binary files a/public/datasheets/na2xfk2y-de.pdf and b/public/datasheets/na2xfk2y-de.pdf differ diff --git a/public/datasheets/na2xfk2y-en.pdf b/public/datasheets/na2xfk2y-en.pdf index 5ef29750..7e19ccef 100644 Binary files a/public/datasheets/na2xfk2y-en.pdf and b/public/datasheets/na2xfk2y-en.pdf differ diff --git a/public/datasheets/na2xfkld2y-de.pdf b/public/datasheets/na2xfkld2y-de.pdf index 5c55d612..ca1236e3 100644 Binary files a/public/datasheets/na2xfkld2y-de.pdf and b/public/datasheets/na2xfkld2y-de.pdf differ diff --git a/public/datasheets/na2xfkld2y-en.pdf b/public/datasheets/na2xfkld2y-en.pdf index af037058..d89e3aee 100644 Binary files a/public/datasheets/na2xfkld2y-en.pdf and b/public/datasheets/na2xfkld2y-en.pdf differ diff --git a/public/datasheets/na2xs2y-2-de.pdf b/public/datasheets/na2xs2y-2-de.pdf index 956d076b..765dbd29 100644 Binary files a/public/datasheets/na2xs2y-2-de.pdf and b/public/datasheets/na2xs2y-2-de.pdf differ diff --git a/public/datasheets/na2xs2y-en.pdf b/public/datasheets/na2xs2y-en.pdf index 22203d9d..598d7850 100644 Binary files a/public/datasheets/na2xs2y-en.pdf and b/public/datasheets/na2xs2y-en.pdf differ diff --git a/public/datasheets/na2xsf2y-2-de.pdf b/public/datasheets/na2xsf2y-2-de.pdf index afe9d21a..2f744591 100644 Binary files a/public/datasheets/na2xsf2y-2-de.pdf and b/public/datasheets/na2xsf2y-2-de.pdf differ diff --git a/public/datasheets/na2xsf2y-en.pdf b/public/datasheets/na2xsf2y-en.pdf index cd9d305b..c82872bf 100644 Binary files a/public/datasheets/na2xsf2y-en.pdf and b/public/datasheets/na2xsf2y-en.pdf differ diff --git a/public/datasheets/na2xsfl2y-2-de.pdf b/public/datasheets/na2xsfl2y-2-de.pdf index cff99248..364c9250 100644 Binary files a/public/datasheets/na2xsfl2y-2-de.pdf and b/public/datasheets/na2xsfl2y-2-de.pdf differ diff --git a/public/datasheets/na2xsfl2y-3-en.pdf b/public/datasheets/na2xsfl2y-3-en.pdf index 80de1e3d..8a779887 100644 Binary files a/public/datasheets/na2xsfl2y-3-en.pdf and b/public/datasheets/na2xsfl2y-3-en.pdf differ diff --git a/public/datasheets/na2xsfl2y-de.pdf b/public/datasheets/na2xsfl2y-de.pdf index 0e688d84..65430ae8 100644 Binary files a/public/datasheets/na2xsfl2y-de.pdf and b/public/datasheets/na2xsfl2y-de.pdf differ diff --git a/public/datasheets/na2xsfl2y-en.pdf b/public/datasheets/na2xsfl2y-en.pdf index 22c32413..a51b4ea6 100644 Binary files a/public/datasheets/na2xsfl2y-en.pdf and b/public/datasheets/na2xsfl2y-en.pdf differ diff --git a/public/datasheets/na2xsy-2-de.pdf b/public/datasheets/na2xsy-2-de.pdf index b235a2d1..1ee8ae46 100644 Binary files a/public/datasheets/na2xsy-2-de.pdf and b/public/datasheets/na2xsy-2-de.pdf differ diff --git a/public/datasheets/na2xsy-en.pdf b/public/datasheets/na2xsy-en.pdf index 32cb44d7..96b11391 100644 Binary files a/public/datasheets/na2xsy-en.pdf and b/public/datasheets/na2xsy-en.pdf differ diff --git a/public/datasheets/na2xy-2-de.pdf b/public/datasheets/na2xy-2-de.pdf index 348354f3..d4cea438 100644 Binary files a/public/datasheets/na2xy-2-de.pdf and b/public/datasheets/na2xy-2-de.pdf differ diff --git a/public/datasheets/na2xy-en.pdf b/public/datasheets/na2xy-en.pdf index 6bb06c32..45e8960e 100644 Binary files a/public/datasheets/na2xy-en.pdf and b/public/datasheets/na2xy-en.pdf differ diff --git a/public/datasheets/nay2y-2-de.pdf b/public/datasheets/nay2y-2-de.pdf index b515c3c2..d4f539bf 100644 Binary files a/public/datasheets/nay2y-2-de.pdf and b/public/datasheets/nay2y-2-de.pdf differ diff --git a/public/datasheets/nay2y-en.pdf b/public/datasheets/nay2y-en.pdf index 2c360469..1748c6a4 100644 Binary files a/public/datasheets/nay2y-en.pdf and b/public/datasheets/nay2y-en.pdf differ diff --git a/public/datasheets/naycwy-2-de.pdf b/public/datasheets/naycwy-2-de.pdf index fc5a630a..b179f0d4 100644 Binary files a/public/datasheets/naycwy-2-de.pdf and b/public/datasheets/naycwy-2-de.pdf differ diff --git a/public/datasheets/naycwy-en.pdf b/public/datasheets/naycwy-en.pdf index d97e6e0f..03cac12b 100644 Binary files a/public/datasheets/naycwy-en.pdf and b/public/datasheets/naycwy-en.pdf differ diff --git a/public/datasheets/nayy-2-de.pdf b/public/datasheets/nayy-2-de.pdf index c3e9256f..c9d2ee0c 100644 Binary files a/public/datasheets/nayy-2-de.pdf and b/public/datasheets/nayy-2-de.pdf differ diff --git a/public/datasheets/nayy-en.pdf b/public/datasheets/nayy-en.pdf index 56a37571..a61410d5 100644 Binary files a/public/datasheets/nayy-en.pdf and b/public/datasheets/nayy-en.pdf differ diff --git a/public/datasheets/ny2y-2-de.pdf b/public/datasheets/ny2y-2-de.pdf index 48585aaa..0148e5b3 100644 Binary files a/public/datasheets/ny2y-2-de.pdf and b/public/datasheets/ny2y-2-de.pdf differ diff --git a/public/datasheets/ny2y-en.pdf b/public/datasheets/ny2y-en.pdf index 6b50cb8a..df428a19 100644 Binary files a/public/datasheets/ny2y-en.pdf and b/public/datasheets/ny2y-en.pdf differ diff --git a/public/datasheets/nycwy-2-de.pdf b/public/datasheets/nycwy-2-de.pdf index 72f79c76..cfb0d3af 100644 Binary files a/public/datasheets/nycwy-2-de.pdf and b/public/datasheets/nycwy-2-de.pdf differ diff --git a/public/datasheets/nycwy-en.pdf b/public/datasheets/nycwy-en.pdf index 67a73299..37057a11 100644 Binary files a/public/datasheets/nycwy-en.pdf and b/public/datasheets/nycwy-en.pdf differ diff --git a/public/datasheets/nyy-2-de.pdf b/public/datasheets/nyy-2-de.pdf index f53186f4..7c7f236d 100644 Binary files a/public/datasheets/nyy-2-de.pdf and b/public/datasheets/nyy-2-de.pdf differ diff --git a/public/datasheets/nyy-en.pdf b/public/datasheets/nyy-en.pdf index 19144d72..1737dfe8 100644 Binary files a/public/datasheets/nyy-en.pdf and b/public/datasheets/nyy-en.pdf differ diff --git a/scripts/generate-pdf-datasheets.ts b/scripts/generate-pdf-datasheets.ts index 5059425e..462b6050 100644 --- a/scripts/generate-pdf-datasheets.ts +++ b/scripts/generate-pdf-datasheets.ts @@ -80,19 +80,76 @@ function drawKeyValueGrid(args: { navy: ReturnType; darkGray: ReturnType; mediumGray: ReturnType; + lightGray?: ReturnType; + almostWhite?: ReturnType; allowNewPage?: boolean; + boxed?: boolean; }): number { let { title, items, newPage, getPage, page, y, margin, contentWidth, contentMinY, font, fontBold, navy, darkGray, mediumGray } = args; const allowNewPage = args.allowNewPage ?? true; + const boxed = args.boxed ?? false; + const lightGray = args.lightGray ?? rgb(0.9020, 0.9137, 0.9294); + const almostWhite = args.almostWhite ?? rgb(0.9725, 0.9765, 0.9804); + + // Inner layout (boxed vs. plain) + const padX = boxed ? 14 : 0; + const padY = boxed ? 12 : 0; + const xBase = margin + padX; + const innerWidth = contentWidth - padX * 2; const colGap = 14; - const colW = (contentWidth - colGap) / 2; - const rowH = 20; + const colW = (innerWidth - colGap) / 2; + const rowH = 18; + const headerH = boxed ? 18 : 0; + + // Draw a strict rectangular section container (no rounding) + if (boxed && items.length) { + const rows = Math.ceil(items.length / 2); + const boxH = padY + headerH + rows * rowH + padY; + const bottomY = y - boxH; + if (bottomY < contentMinY) { + if (!allowNewPage) return contentMinY - 1; + y = newPage(); + } + + page = getPage(); + page.drawRectangle({ + x: margin, + y: y - boxH, + width: contentWidth, + height: boxH, + borderColor: lightGray, + borderWidth: 1, + color: rgb(1, 1, 1), + }); + + // Header band for the title + page.drawRectangle({ + x: margin, + y: y - headerH, + width: contentWidth, + height: headerH, + color: almostWhite, + }); + } const drawTitle = () => { page = getPage(); - page.drawText(title, { x: margin, y, size: 10, font: fontBold, color: navy }); - y -= 16; + if (boxed) { + // Align title inside the header band. + page.drawText(title, { x: xBase, y: y - 13, size: 9.5, font: fontBold, color: navy }); + // Divider line below header band + page.drawLine({ + start: { x: margin, y: y - headerH }, + end: { x: margin + contentWidth, y: y - headerH }, + thickness: 1, + color: lightGray, + }); + y -= headerH + padY; + } else { + page.drawText(title, { x: margin, y, size: 10, font: fontBold, color: navy }); + y -= 16; + } }; if (y - 22 < contentMinY) { @@ -105,7 +162,7 @@ function drawKeyValueGrid(args: { let rowY = y; for (let i = 0; i < items.length; i++) { const col = i % 2; - const x = margin + col * (colW + colGap); + const x = xBase + col * (colW + colGap); const { label, value } = items[i]; if (col === 0 && rowY - rowH < contentMinY) { @@ -116,13 +173,13 @@ function drawKeyValueGrid(args: { rowY = y; } - page.drawText(label, { x, y: rowY, size: 8, font: fontBold, color: mediumGray, maxWidth: colW }); + page.drawText(label, { x, y: rowY, size: 7.5, font: fontBold, color: mediumGray, maxWidth: colW }); page.drawText(value, { x, y: rowY - 10, size: 8, font, color: darkGray, maxWidth: colW }); if (col === 1) rowY -= rowH; } - return rowY - rowH; + return boxed ? rowY - rowH - padY : rowY - rowH; } function ensureOutputDir(): void { @@ -711,15 +768,14 @@ function drawCrossSectionChipsRow(args: { page.drawText(summaryParts.join(' · '), { x: margin, y, size: 8, font, color: mediumGray, maxWidth: contentWidth }); y -= summaryH; - // Chips (wrapping). We draw small bordered rectangles and wrap to next line. + // Tags (wrapping). Rectangular, engineered (no playful rounding). const padX = 7; const chipFontSize = 7.5; const chipGap = 6; const chipPadTop = 4; const startY = y - chipH; // baseline for first chip row - const maxLinesBySpace = Math.max(1, Math.floor((startY - contentMinY + lineGap) / (chipH + lineGap))); - const maxLines = Math.min(6, maxLinesBySpace); // visual cap + const maxLines = Math.max(1, Math.floor((startY - contentMinY + lineGap) / (chipH + lineGap))); const chipWidth = (text: string) => font.widthOfTextAtSize(text, chipFontSize) + padX * 2; @@ -762,32 +818,120 @@ function drawCrossSectionChipsRow(args: { return { placements, shown }; }; - // First pass: lay out as many as possible. - const moreTextBase = locale === 'de' ? 'weitere' : 'more'; - let shown = 0; - let placements: Placement[] = []; + // Group by cores: label on the left, mm² tags to the right. + const byCores = new Map(); + const other: string[] = []; + for (const cs of items) { + const p = parseCoresAndMm2(cs); + if (p.cores !== null && p.mm2 !== null) { + const arr = byCores.get(p.cores) ?? []; + arr.push(p.mm2); + byCores.set(p.cores, arr); + } else { + other.push(cs); + } + } - // We reserve space for the "+N" chip when there is truncation by iteratively reducing shown. - // This produces stable output without needing to "erase" drawings. - const allTexts = items; - let tmp = layout(allTexts, false, ''); - shown = tmp.shown; - const remaining0 = total - shown; - if (remaining0 <= 0) { - placements = tmp.placements; - } else { - // Reduce shown until the "+N" chip fits. - for (let cut = shown; cut >= 0; cut--) { - const remaining = total - cut; - const moreText = `+${remaining} ${moreTextBase}`; - const res = layout(allTexts.slice(0, cut), true, moreText); - // Ensure the more-chip actually placed (variant 'more' exists) - const hasMore = res.placements.some(p => p.variant === 'more'); - if (hasMore) { - shown = cut; - placements = res.placements; + const coreKeys = Array.from(byCores.keys()).sort((a, b) => a - b); + for (const k of coreKeys) { + const uniq = Array.from(new Set(byCores.get(k) ?? [])).sort((a, b) => a - b); + byCores.set(k, uniq); + } + + const fmtMm2 = (v: number) => { + const s = Number.isInteger(v) ? String(v) : String(v).replace(/\.0+$/, ''); + return s; + }; + + // Layout engine with group labels. + const labelW = 34; + const placements: Placement[] = []; + let line = 0; + let cy = startY; + let x = margin + labelW; + + const canAdvanceLine = () => line + 1 < maxLines; + const advanceLine = () => { + if (!canAdvanceLine()) return false; + line += 1; + cy -= chipH + lineGap; + x = margin + labelW; + return true; + }; + + const drawGroupLabel = (label: string) => { + // Draw label on each new line for the group (keeps readability when wrapping). + page.drawText(label, { + x: margin, + y: cy + 4, + size: 8, + font: fontBold, + color: mediumGray, + maxWidth: labelW - 4, + }); + }; + + const placeChip = (text: string, variant: 'normal' | 'more') => { + const w = chipWidth(text); + if (w > contentWidth - labelW) return false; + if (x + w > margin + contentWidth) { + if (!advanceLine()) return false; + } + placements.push({ text, x, y: cy, w, variant }); + x += w + chipGap; + return true; + }; + + let truncated = false; + let renderedCount = 0; + const totalChips = coreKeys.reduce((sum, k) => sum + (byCores.get(k)?.length ?? 0), 0) + other.length; + + for (const cores of coreKeys) { + const values = byCores.get(cores) ?? []; + const label = `${cores}×`; + // Ensure label is shown at least once per line block. + drawGroupLabel(label); + for (const v of values) { + const ok = placeChip(fmtMm2(v), 'normal'); + if (!ok) { + truncated = true; break; } + renderedCount++; + } + if (truncated) break; + // Add a tiny gap between core groups (only if we have room on the current line) + x += 4; + if (x > margin + contentWidth - 20) { + if (!advanceLine()) { + // out of vertical space; stop + truncated = true; + break; + } + } + } + + if (!truncated && other.length) { + const label = locale === 'de' ? 'Sonst.' : 'Other'; + drawGroupLabel(label); + for (const t of other) { + const ok = placeChip(t, 'normal'); + if (!ok) { + truncated = true; + break; + } + renderedCount++; + } + } + + if (truncated) { + const remaining = Math.max(0, totalChips - renderedCount); + const moreText = locale === 'de' ? `+${remaining} weitere` : `+${remaining} more`; + // Try to place on current line; if not possible, try next line. + if (!placeChip(moreText, 'more')) { + if (advanceLine()) { + placeChip(moreText, 'more'); + } } } @@ -800,7 +944,7 @@ function drawCrossSectionChipsRow(args: { height: chipH, borderColor: lightGray, borderWidth: 1, - color: p.variant === 'more' ? almostWhite : rgb(1, 1, 1), + color: almostWhite, }); page.drawText(p.text, { x: p.x + padX, @@ -950,6 +1094,15 @@ async function generatePDF(product: ProductData, locale: 'en' | 'de'): Promise y - needed >= contentMinY; // Page 1 + // Page background (STYLEGUIDE.md) + page.drawRectangle({ + x: 0, + y: 0, + width, + height, + color: almostWhite, + }); + drawFooter(ctx); let y = drawHeader(ctx, height - margin); @@ -1114,7 +1267,10 @@ async function generatePDF(product: ProductData, locale: 'en' | 'de'): Promise