@gmoney2000/dygarn-pdf-kit 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pdf-lib.cjs CHANGED
@@ -23,6 +23,7 @@ __export(pdf_lib_exports, {
23
23
  BRANDED_FOOTER_HEIGHT: () => BRANDED_FOOTER_HEIGHT,
24
24
  BRANDED_HEADER_HEIGHT: () => BRANDED_HEADER_HEIGHT,
25
25
  BRANDED_HEADER_MINIMAL_H: () => BRANDED_HEADER_MINIMAL_H,
26
+ BRANDED_HEADER_PROMINENT_H: () => BRANDED_HEADER_PROMINENT_H,
26
27
  drawBrandedCover: () => drawBrandedCover,
27
28
  drawBrandedFooter: () => drawBrandedFooter,
28
29
  drawBrandedHeader: () => drawBrandedHeader,
@@ -139,6 +140,22 @@ async function loadBookendAssets(opts) {
139
140
 
140
141
  // src/pdf-lib/cover.ts
141
142
  var import_pdf_lib3 = require("pdf-lib");
143
+ function wrapByWidth(text, font, size, maxWidth) {
144
+ const out = [];
145
+ const words = text.split(/\s+/).filter(Boolean);
146
+ let line = "";
147
+ for (const w of words) {
148
+ const candidate = line ? `${line} ${w}` : w;
149
+ if (font.widthOfTextAtSize(candidate, size) <= maxWidth) {
150
+ line = candidate;
151
+ } else {
152
+ if (line) out.push(line);
153
+ line = w;
154
+ }
155
+ }
156
+ if (line) out.push(line);
157
+ return out.length > 0 ? out : [text];
158
+ }
142
159
  function drawBrandedCover(page, ctx, fonts) {
143
160
  const { helv, helvBold, helvOblique } = fonts;
144
161
  const { bandColor, accentColor, logoImage, logoAspect, themeMode } = ctx;
@@ -181,15 +198,39 @@ function drawBrandedCover(page, ctx, fonts) {
181
198
  });
182
199
  cursorY -= 30;
183
200
  if (ctx.projectName) {
184
- const projW = helvBold.widthOfTextAtSize(ctx.projectName, 22);
185
- page.drawText(ctx.projectName, {
186
- x: CX - projW / 2,
187
- y: cursorY,
188
- size: 22,
189
- font: helvBold,
190
- color: (0, import_pdf_lib3.rgb)(0.08, 0.08, 0.08)
191
- });
192
- cursorY -= 24;
201
+ const PROJ_MARGIN = 36;
202
+ const PROJ_MAX_W = W - PROJ_MARGIN * 2;
203
+ const PROJ_MAX_SIZE = 22;
204
+ const PROJ_MIN_SIZE = 14;
205
+ let projSize = PROJ_MAX_SIZE;
206
+ while (projSize > PROJ_MIN_SIZE && helvBold.widthOfTextAtSize(ctx.projectName, projSize) > PROJ_MAX_W) {
207
+ projSize -= 1;
208
+ }
209
+ if (helvBold.widthOfTextAtSize(ctx.projectName, projSize) <= PROJ_MAX_W) {
210
+ const projW = helvBold.widthOfTextAtSize(ctx.projectName, projSize);
211
+ page.drawText(ctx.projectName, {
212
+ x: CX - projW / 2,
213
+ y: cursorY,
214
+ size: projSize,
215
+ font: helvBold,
216
+ color: (0, import_pdf_lib3.rgb)(0.08, 0.08, 0.08)
217
+ });
218
+ cursorY -= projSize + 2;
219
+ } else {
220
+ const lines = wrapByWidth(ctx.projectName, helvBold, PROJ_MIN_SIZE, PROJ_MAX_W);
221
+ for (const line of lines) {
222
+ const lineW = helvBold.widthOfTextAtSize(line, PROJ_MIN_SIZE);
223
+ page.drawText(line, {
224
+ x: CX - lineW / 2,
225
+ y: cursorY,
226
+ size: PROJ_MIN_SIZE,
227
+ font: helvBold,
228
+ color: (0, import_pdf_lib3.rgb)(0.08, 0.08, 0.08)
229
+ });
230
+ cursorY -= PROJ_MIN_SIZE + 2;
231
+ }
232
+ cursorY -= 2;
233
+ }
193
234
  }
194
235
  if (ctx.location) {
195
236
  const locW = helv.widthOfTextAtSize(ctx.location, 12);
@@ -258,12 +299,18 @@ function drawBrandedCover(page, ctx, fonts) {
258
299
  var import_pdf_lib4 = require("pdf-lib");
259
300
  var BRANDED_HEADER_HEIGHT = 110;
260
301
  var BRANDED_HEADER_MINIMAL_H = 56;
302
+ var BRANDED_HEADER_PROMINENT_H = 180;
261
303
  function clip(s, max) {
262
304
  if (s.length <= max) return s;
263
305
  return `${s.slice(0, max - 1)}\u2026`;
264
306
  }
265
307
  function drawBrandedHeader(page, ctx, fonts, opts) {
266
308
  const showFixtureCard = opts?.showFixtureCard ?? false;
309
+ const prominent = opts?.prominent ?? false;
310
+ if (prominent) {
311
+ drawProminentHeader(page, ctx, fonts);
312
+ return;
313
+ }
267
314
  const { helv, helvBold } = fonts;
268
315
  const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;
269
316
  const W = page.getWidth();
@@ -378,6 +425,96 @@ function drawSimpleInfoStrip(page, ctx, fonts, W, CREAM_H, headerTop, PAD) {
378
425
  page.drawText(ctx.docType, { x: W - PAD - dtW, y: stripY, size: 8, font: helv, color: (0, import_pdf_lib4.rgb)(0.4, 0.4, 0.4) });
379
426
  }
380
427
  }
428
+ function drawProminentHeader(page, ctx, fonts) {
429
+ const { helv, helvBold } = fonts;
430
+ const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;
431
+ const W = page.getWidth();
432
+ const H = page.getHeight();
433
+ const TOP_BANNER_H = BRANDED_HEADER_PROMINENT_H;
434
+ const PAD = 20;
435
+ const STRIPE_H = 3;
436
+ const CREAM_H = 28;
437
+ const DARK_H = TOP_BANNER_H - STRIPE_H - CREAM_H;
438
+ const darkBandY = H - DARK_H;
439
+ const stripeY = darkBandY - STRIPE_H;
440
+ const headerTop = H - TOP_BANNER_H;
441
+ page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });
442
+ page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });
443
+ page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });
444
+ page.drawLine({
445
+ start: { x: 0, y: headerTop },
446
+ end: { x: W, y: headerTop },
447
+ thickness: 0.5,
448
+ color: (0, import_pdf_lib4.rgb)(0.8, 0.8, 0.78)
449
+ });
450
+ if (logoImage) {
451
+ const LOGO_MAX_H = 120;
452
+ const LOGO_MAX_W = Math.min(W - PAD * 2, 320);
453
+ let logoH = LOGO_MAX_H;
454
+ let logoW = logoH * logoAspect;
455
+ if (logoW > LOGO_MAX_W) {
456
+ logoW = LOGO_MAX_W;
457
+ logoH = logoW / logoAspect;
458
+ }
459
+ const logoX = PAD;
460
+ const logoY = darkBandY + (DARK_H - logoH) / 2;
461
+ page.drawImage(logoImage, { x: logoX, y: logoY, width: logoW, height: logoH });
462
+ } else {
463
+ const nameColor = themeMode === "dark" ? (0, import_pdf_lib4.rgb)(1, 1, 1) : (0, import_pdf_lib4.rgb)(0.08, 0.08, 0.08);
464
+ const nameSize = 28;
465
+ page.drawText(ctx.brand.agencyName, {
466
+ x: PAD,
467
+ y: darkBandY + (DARK_H - nameSize) / 2,
468
+ size: nameSize,
469
+ font: helvBold,
470
+ color: nameColor
471
+ });
472
+ }
473
+ if (ctx.docNumber) {
474
+ const docColor = themeMode === "dark" ? (0, import_pdf_lib4.rgb)(0.9, 0.9, 0.9) : (0, import_pdf_lib4.rgb)(0.15, 0.15, 0.15);
475
+ const docSize = 12;
476
+ const docW = helvBold.widthOfTextAtSize(ctx.docNumber, docSize);
477
+ page.drawText(ctx.docNumber, {
478
+ x: W - PAD - docW,
479
+ y: darkBandY + 14,
480
+ size: docSize,
481
+ font: helvBold,
482
+ color: docColor
483
+ });
484
+ if (ctx.dateStamp) {
485
+ const dateColor = themeMode === "dark" ? (0, import_pdf_lib4.rgb)(0.7, 0.7, 0.7) : (0, import_pdf_lib4.rgb)(0.45, 0.45, 0.45);
486
+ const dateSize = 9;
487
+ const dateW = helv.widthOfTextAtSize(ctx.dateStamp, dateSize);
488
+ page.drawText(ctx.dateStamp, {
489
+ x: W - PAD - dateW,
490
+ y: darkBandY + 2,
491
+ size: dateSize,
492
+ font: helv,
493
+ color: dateColor
494
+ });
495
+ }
496
+ }
497
+ const stripY = headerTop + (CREAM_H - 9) / 2;
498
+ if (ctx.projectName) {
499
+ page.drawText(clip(ctx.projectName, 70), {
500
+ x: PAD,
501
+ y: stripY,
502
+ size: 9,
503
+ font: helvBold,
504
+ color: (0, import_pdf_lib4.rgb)(0.4, 0.4, 0.4)
505
+ });
506
+ }
507
+ if (ctx.docType) {
508
+ const dtW = helv.widthOfTextAtSize(ctx.docType, 8);
509
+ page.drawText(ctx.docType, {
510
+ x: W - PAD - dtW,
511
+ y: stripY + 0.5,
512
+ size: 8,
513
+ font: helv,
514
+ color: (0, import_pdf_lib4.rgb)(0.45, 0.45, 0.45)
515
+ });
516
+ }
517
+ }
381
518
 
382
519
  // src/pdf-lib/footer.ts
383
520
  var import_pdf_lib5 = require("pdf-lib");
@@ -465,6 +602,7 @@ function addGoToFirstPageLink(page, rect) {
465
602
  BRANDED_FOOTER_HEIGHT,
466
603
  BRANDED_HEADER_HEIGHT,
467
604
  BRANDED_HEADER_MINIMAL_H,
605
+ BRANDED_HEADER_PROMINENT_H,
468
606
  drawBrandedCover,
469
607
  drawBrandedFooter,
470
608
  drawBrandedHeader,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pdf-lib/index.ts","../src/pdf-lib/fonts.ts","../src/pdf-lib/assets.ts","../src/brand.ts","../src/pdf-lib/cover.ts","../src/pdf-lib/header.ts","../src/pdf-lib/footer.ts"],"sourcesContent":["export { loadBookendFonts } from \"./fonts\";\nexport type { BookendFonts } from \"./fonts\";\n\nexport { loadBookendAssets } from \"./assets\";\nexport type { LoadBookendAssetsOptions, ResolvedBookendAssets } from \"./assets\";\n\nexport type { BookendContext } from \"./context\";\n\nexport { drawBrandedCover } from \"./cover\";\n\nexport { BRANDED_HEADER_HEIGHT, BRANDED_HEADER_MINIMAL_H, drawBrandedHeader } from \"./header\";\n\nexport { BRANDED_FOOTER_HEIGHT, drawBrandedFooter } from \"./footer\";\n","import type { PDFDocument, PDFFont } from \"pdf-lib\";\nimport { StandardFonts } from \"pdf-lib\";\n\nexport interface BookendFonts {\n helv: PDFFont;\n helvBold: PDFFont;\n helvOblique: PDFFont;\n}\n\nexport async function loadBookendFonts(doc: PDFDocument): Promise<BookendFonts> {\n const [helv, helvBold, helvOblique] = await Promise.all([\n doc.embedFont(StandardFonts.Helvetica),\n doc.embedFont(StandardFonts.HelveticaBold),\n doc.embedFont(StandardFonts.HelveticaOblique),\n ]);\n return { helv, helvBold, helvOblique };\n}\n","import type { PDFDocument, PDFImage, RGB } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport { darkenHex, hexToRgb01, resolveBandThemeMode, resolveBestLogoUrl } from \"../brand\";\nimport type { BrandInput } from \"../types\";\n\nexport interface ResolvedBookendAssets {\n logoImage: PDFImage | null;\n logoAspect: number;\n bandColor: RGB;\n accentColor: RGB;\n cardColors: {\n cream: RGB;\n cardBorder: RGB;\n chipBg: RGB;\n };\n themeMode: \"dark\" | \"light\";\n}\n\nexport interface LoadBookendAssetsOptions {\n brand: BrandInput;\n doc: PDFDocument;\n /**\n * Optional URL resolver. Use when your app needs to convert relative paths\n * (e.g. /demo-files/logo.png) into absolute URLs for server-side fetching.\n * Defaults to identity (returns the URL unchanged).\n */\n resolveUrl?: (url: string) => string;\n /** Optional fetch timeout in ms. Default 8s. */\n fetchTimeoutMs?: number;\n}\n\n/**\n * Resolve brand colors + load logo PDFImage from URL.\n * Mirrors RepFirm's loadTenantBookendAssets behavior.\n */\nexport async function loadBookendAssets(opts: LoadBookendAssetsOptions): Promise<ResolvedBookendAssets> {\n const { brand, doc, resolveUrl = (u) => u, fetchTimeoutMs = 8_000 } = opts;\n const themeMode = resolveBandThemeMode(brand);\n const themeVars = brand.themeVars ?? {};\n\n // Band color\n let bandColorHex: string;\n if (themeMode === \"dark\") {\n bandColorHex =\n themeVars[\"--sidebar-bg\"] ??\n themeVars[\"--header-bg\"] ??\n (brand.primaryColor ? darkenHex(brand.primaryColor, 0.45) : \"#1e3a5f\");\n } else {\n bandColorHex = \"#fbfaf6\";\n }\n\n const accentHex = brand.primaryColor ?? themeVars[\"--accent\"] ?? \"#1e3a5f\";\n\n const [br, bg2, bb] = hexToRgb01(bandColorHex);\n const [ar, ag, ab] = hexToRgb01(accentHex);\n\n const cardColors = {\n cream: rgb(0.984, 0.98, 0.965),\n cardBorder: rgb(0.78, 0.78, 0.78),\n chipBg: rgb(0.937, 0.937, 0.933),\n };\n\n // Logo URL resolution\n const candidate = resolveBestLogoUrl(brand, themeMode);\n const logoUrl = candidate ? resolveUrl(candidate) : null;\n\n let logoImage: PDFImage | null = null;\n let logoAspect = 1;\n\n if (logoUrl && !logoUrl.toLowerCase().endsWith(\".svg\")) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), fetchTimeoutMs);\n let res: Response;\n try {\n res = await fetch(logoUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n if (res.ok) {\n const buf = new Uint8Array(await res.arrayBuffer());\n const lower = logoUrl.toLowerCase();\n if (lower.includes(\".png\") || (res.headers.get(\"content-type\") ?? \"\").includes(\"png\")) {\n logoImage = await doc.embedPng(buf);\n } else {\n logoImage = await doc.embedJpg(buf);\n }\n const dims = logoImage.scale(1);\n logoAspect = dims.width / dims.height;\n }\n } catch {\n // fall back to text-only header\n }\n }\n\n return {\n logoImage,\n logoAspect,\n bandColor: rgb(br, bg2, bb),\n accentColor: rgb(ar, ag, ab),\n cardColors,\n themeMode,\n };\n}\n","import type { BrandInput } from \"./types\";\n\n/**\n * Adapter: RepFirm TenantBranding shape -> BrandInput.\n *\n * RepFirm tenants store branding on the `tenants.branding` JSONB column with\n * keys like primary_color, logo_url, logo_dark_url, etc. (snake_case). Pair\n * with the tenant name from the parent row when calling.\n */\nexport interface TenantBrandingShape {\n primary_color?: string;\n logo_url?: string | null;\n logo_wordmark_url?: string | null;\n logo_mark_url?: string | null;\n logo_dark_url?: string | null;\n logo_light_url?: string | null;\n pdf_header_theme?: \"dark\" | \"light\" | \"auto\";\n theme_vars?: Record<string, string>;\n tagline?: string | null;\n company_address?: string | null;\n company_phone?: string | null;\n company_email?: string | null;\n}\n\nexport function brandFromTenantBranding(\n tenantName: string,\n branding: TenantBrandingShape | null | undefined,\n fallbackPrimary = \"#1e3a5f\",\n): BrandInput {\n const b = branding ?? {};\n return {\n agencyName: tenantName,\n primaryColor: b.primary_color ?? fallbackPrimary,\n logoUrl: b.logo_url ?? null,\n logoDarkUrl: b.logo_dark_url ?? null,\n logoLightUrl: b.logo_light_url ?? null,\n logoWordmarkUrl: b.logo_wordmark_url ?? null,\n logoMarkUrl: b.logo_mark_url ?? null,\n pdfHeaderTheme: b.pdf_header_theme ?? \"auto\",\n themeVars: b.theme_vars ?? {},\n tagline: b.tagline ?? null,\n address: b.company_address ?? null,\n phone: b.company_phone ?? null,\n email: b.company_email ?? null,\n };\n}\n\n/**\n * Adapter: dygarn-dashboard prospect brand shape -> BrandInput.\n *\n * Dygarn-dashboard stores per-prospect brand kits in outreach_prospect_brands\n * with simpler shape (camelCase or snake — be liberal in what we accept).\n */\nexport interface ProspectBrandShape {\n agency_name?: string;\n agencyName?: string;\n legal_name?: string | null;\n legalName?: string | null;\n logo_url?: string | null;\n logoUrl?: string | null;\n primary_color?: string;\n primaryColor?: string;\n secondary_color?: string;\n secondaryColor?: string;\n address?: string | null;\n phone?: string | null;\n website?: string | null;\n}\n\nexport function brandFromProspectBrand(p: ProspectBrandShape): BrandInput {\n const agency = p.agencyName ?? p.agency_name ?? \"Agency\";\n return {\n agencyName: agency,\n legalName: p.legalName ?? p.legal_name ?? null,\n logoUrl: p.logoUrl ?? p.logo_url ?? null,\n primaryColor: p.primaryColor ?? p.primary_color ?? \"#1a73e8\",\n address: p.address ?? null,\n phone: p.phone ?? null,\n website: p.website ?? null,\n pdfHeaderTheme: \"auto\",\n };\n}\n\n/**\n * Determine whether to use a dark or light header band for this brand.\n *\n * - Explicit override: respect pdfHeaderTheme if set to 'dark' or 'light'.\n * - Auto (default): use dark theme only when a logoDarkUrl (white-on-dark\n * variant) is available, OR when the logo filename hints at a white/dark\n * variant. Otherwise default to LIGHT (safe for multi-color logos).\n */\nexport function resolveBandThemeMode(brand: BrandInput): \"dark\" | \"light\" {\n if (brand.pdfHeaderTheme === \"dark\") return \"dark\";\n if (brand.pdfHeaderTheme === \"light\") return \"light\";\n if (brand.logoDarkUrl) return \"dark\";\n const candidate = (brand.logoWordmarkUrl ?? brand.logoUrl ?? brand.logoMarkUrl ?? \"\").toLowerCase();\n if (candidate.includes(\"white\") || candidate.includes(\"-on-dark\") || candidate.includes(\"_dark\")) return \"dark\";\n return \"light\";\n}\n\n/**\n * Pick the best logo URL for the resolved theme mode.\n * Returns null if no logo available.\n */\nexport function resolveBestLogoUrl(brand: BrandInput, mode: \"dark\" | \"light\"): string | null {\n if (mode === \"dark\") {\n return brand.logoDarkUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? null;\n }\n return (\n brand.logoLightUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? brand.logoDarkUrl ?? null\n );\n}\n\n/**\n * Darken a hex color by a 0-1 amount. Used to derive a dark band color from\n * a brand primary when no explicit dark variant exists.\n */\nexport function darkenHex(hex: string, amount: number): string {\n const m = /^#?([0-9a-f]{6})$/i.exec(hex);\n if (!m) return hex;\n const n = Number.parseInt(m[1], 16);\n let r = (n >> 16) & 0xff;\n let g = (n >> 8) & 0xff;\n let b = n & 0xff;\n r = Math.max(0, Math.round(r * (1 - amount)));\n g = Math.max(0, Math.round(g * (1 - amount)));\n b = Math.max(0, Math.round(b * (1 - amount)));\n return `#${[r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/**\n * Convert hex to {r,g,b} normalized 0-1. Used by both React-PDF (color objects)\n * and pdf-lib (rgb() arguments).\n */\nexport function hexToRgb01(hex: string): [number, number, number] {\n const h = hex.replace(\"#\", \"\").trim();\n const r = Number.parseInt(h.slice(0, 2), 16) / 255;\n const g = Number.parseInt(h.slice(2, 4), 16) / 255;\n const b = Number.parseInt(h.slice(4, 6), 16) / 255;\n return [Number.isFinite(r) ? r : 0.12, Number.isFinite(g) ? g : 0.23, Number.isFinite(b) ? b : 0.37];\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\n/**\n * Draw a branded cover page. Logo centered on the band, doc type label,\n * project name, location, doc number, recipient, prepared-by signature block,\n * bottom metadata line.\n *\n * Designed for 612x792 (US Letter) page size. Caller is responsible for\n * adding the page to the document.\n */\nexport function drawBrandedCover(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold, helvOblique } = fonts;\n const { bandColor, accentColor, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const CX = W / 2;\n const agencyName = ctx.brand.agencyName;\n\n // Top branded band\n const COVER_HEADER_H = 130;\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H, width: W, height: COVER_HEADER_H, color: bandColor });\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H - 4, width: W, height: 4, color: accentColor });\n\n // Logo or name inside the band\n const COVER_LOGO_H = 70;\n const COVER_LOGO_W = Math.round(COVER_LOGO_H * (logoAspect || 1));\n if (logoImage) {\n page.drawImage(logoImage, {\n x: CX - COVER_LOGO_W / 2,\n y: H - COVER_HEADER_H + (COVER_HEADER_H - COVER_LOGO_H) / 2,\n width: COVER_LOGO_W,\n height: COVER_LOGO_H,\n });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n const nameW = helvBold.widthOfTextAtSize(agencyName, 28);\n page.drawText(agencyName, {\n x: CX - nameW / 2,\n y: H - COVER_HEADER_H / 2 - 10,\n size: 28,\n font: helvBold,\n color: nameColor,\n });\n }\n\n // Project / document block (centered, upper-middle)\n let cursorY = H - COVER_HEADER_H - 70;\n\n const docTypeUpper = ctx.docType.toUpperCase();\n const tagW = helvBold.widthOfTextAtSize(docTypeUpper, 11);\n page.drawText(docTypeUpper, {\n x: CX - tagW / 2,\n y: cursorY,\n size: 11,\n font: helvBold,\n color: rgb(0.45, 0.45, 0.45),\n });\n cursorY -= 30;\n\n if (ctx.projectName) {\n const projW = helvBold.widthOfTextAtSize(ctx.projectName, 22);\n page.drawText(ctx.projectName, {\n x: CX - projW / 2,\n y: cursorY,\n size: 22,\n font: helvBold,\n color: rgb(0.08, 0.08, 0.08),\n });\n cursorY -= 24;\n }\n\n if (ctx.location) {\n const locW = helv.widthOfTextAtSize(ctx.location, 12);\n page.drawText(ctx.location, {\n x: CX - locW / 2,\n y: cursorY,\n size: 12,\n font: helv,\n color: rgb(0.4, 0.4, 0.4),\n });\n cursorY -= 36;\n } else {\n cursorY -= 24;\n }\n\n if (ctx.docNumber) {\n const numLabel = ctx.docNumber;\n const numW = helvBold.widthOfTextAtSize(numLabel, 12);\n page.drawText(numLabel, {\n x: CX - numW / 2,\n y: cursorY,\n size: 12,\n font: helvBold,\n color: rgb(0.25, 0.25, 0.25),\n });\n cursorY -= 28;\n }\n\n const recipientName = ctx.recipientName ?? ctx.distributorName ?? null;\n const recipientLabel = ctx.recipientLabel ?? (ctx.distributorName ? \"DISTRIBUTOR\" : null);\n if (recipientName && recipientLabel) {\n const labelW = helvBold.widthOfTextAtSize(recipientLabel, 11);\n const gap = 14;\n const valueW = helvBold.widthOfTextAtSize(recipientName, 13);\n const totalW = labelW + gap + valueW;\n const rowX = CX - totalW / 2;\n page.drawText(recipientLabel, { x: rowX, y: cursorY, size: 11, font: helvBold, color: rgb(0.45, 0.45, 0.45) });\n page.drawText(recipientName, { x: rowX + labelW + gap, y: cursorY, size: 13, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n }\n\n // Bottom signature block\n const SIG_BLOCK_TOP = 140;\n let sigY = SIG_BLOCK_TOP;\n\n page.drawRectangle({ x: CX - 100, y: sigY, width: 200, height: 0.5, color: rgb(0.7, 0.7, 0.7) });\n sigY -= 18;\n\n const prepLabel = \"- Prepared By -\";\n const prepLabelW = helvOblique.widthOfTextAtSize(prepLabel, 10);\n page.drawText(prepLabel, { x: CX - prepLabelW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.5, 0.5, 0.5) });\n sigY -= 18;\n\n if (ctx.repName) {\n const repNameW = helvBold.widthOfTextAtSize(ctx.repName, 12);\n page.drawText(ctx.repName, { x: CX - repNameW / 2, y: sigY, size: 12, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n sigY -= 16;\n }\n if (ctx.repEmail) {\n const repEmailW = helv.widthOfTextAtSize(ctx.repEmail, 10);\n page.drawText(ctx.repEmail, { x: CX - repEmailW / 2, y: sigY, size: 10, font: helv, color: rgb(0.16, 0.42, 0.68) });\n sigY -= 14;\n }\n\n const tnW = helvOblique.widthOfTextAtSize(agencyName, 10);\n page.drawText(agencyName, { x: CX - tnW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.4, 0.4, 0.4) });\n\n // Bottom-most: doc number + date\n const footerLine = [ctx.docNumber, ctx.dateStamp].filter(Boolean).join(\" | \");\n if (footerLine) {\n const flW = helv.widthOfTextAtSize(footerLine, 9);\n page.drawText(footerLine, { x: CX - flW / 2, y: 36, size: 9, font: helv, color: rgb(0.55, 0.55, 0.55) });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_HEADER_HEIGHT = 110; // dark band + stripe + cream info zone\nexport const BRANDED_HEADER_MINIMAL_H = 56; // dark band + stripe + thin cream strip\n\nfunction clip(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, max - 1)}…`;\n}\n\n/**\n * Draw the per-page branded header.\n *\n * showFixtureCard=true (datasheet pages): dark band 38pt + stripe 3pt + cream\n * info zone with white card (Job Name / Manufacturer / Model Number) + type\n * chip on right. Use for submittal datasheet pages.\n *\n * showFixtureCard=false (default): dark band 38pt + stripe 3pt + thin cream\n * strip with project name + doc number. Use for non-datasheet pages.\n */\nexport function drawBrandedHeader(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n opts?: { showFixtureCard?: boolean },\n): void {\n const showFixtureCard = opts?.showFixtureCard ?? false;\n const { helv, helvBold } = fonts;\n const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const TOP_BANNER_H = showFixtureCard ? BRANDED_HEADER_HEIGHT : BRANDED_HEADER_MINIMAL_H;\n\n const PAD = 12;\n const DARK_H = 38;\n const STRIPE_H = 3;\n const CREAM_H = TOP_BANNER_H - DARK_H - STRIPE_H;\n\n const darkBandY = H - DARK_H;\n const stripeY = darkBandY - STRIPE_H;\n const headerTop = H - TOP_BANNER_H;\n\n page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });\n page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });\n page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: headerTop },\n end: { x: W, y: headerTop },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n if (logoImage) {\n const bandLogoH = 26;\n const logoW = Math.round(bandLogoH * logoAspect);\n page.drawImage(logoImage, { x: PAD, y: darkBandY + (DARK_H - bandLogoH) / 2, width: logoW, height: bandLogoH });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n page.drawText(ctx.brand.agencyName, { x: PAD, y: darkBandY + 12, size: 12, font: helvBold, color: nameColor });\n }\n\n if (showFixtureCard) {\n drawFixtureCard(page, ctx, fonts, W, TOP_BANNER_H, PAD, CREAM_H, headerTop);\n } else {\n drawSimpleInfoStrip(page, ctx, fonts, W, CREAM_H, headerTop, PAD);\n }\n}\n\nfunction drawFixtureCard(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n _TOP_BANNER_H: number,\n PAD: number,\n CREAM_H: number,\n headerTop: number,\n): void {\n const { helv, helvBold } = fonts;\n const { cardColors } = ctx;\n\n const CHIP_W_RESERVE = 60;\n const CARD_X = PAD * 2;\n const CARD_W = W - CARD_X - CHIP_W_RESERVE - PAD;\n const CARD_H = Math.min(CREAM_H - 8, 60);\n const CARD_Y = headerTop + (CREAM_H - CARD_H) / 2;\n\n page.drawRectangle({\n x: CARD_X,\n y: CARD_Y,\n width: CARD_W,\n height: CARD_H,\n borderColor: cardColors.cardBorder,\n borderWidth: 0.75,\n color: rgb(1, 1, 1),\n });\n\n const LABEL_X = CARD_X + 14;\n const VALUE_X = CARD_X + 100;\n const ROW1_Y = CARD_Y + CARD_H - 17;\n const ROW2_Y = ROW1_Y - 18;\n const ROW3_Y = ROW2_Y - 18;\n\n const jobName = (ctx.projectName || \"—\").toUpperCase();\n const rows: Array<[string, string]> = [\n [\"Job Name:\", clip(jobName, 48)],\n [\"Manufacturer:\", clip(ctx.fixtureMfr ?? \"—\", 44)],\n [\"Model Number:\", clip(ctx.fixturePartNumber ?? \"—\", 44)],\n ];\n const rowYs = [ROW1_Y, ROW2_Y, ROW3_Y];\n for (let i = 0; i < rows.length; i++) {\n const [label, value] = rows[i];\n const y = rowYs[i];\n page.drawText(label, { x: LABEL_X, y, size: 9, font: helvBold, color: rgb(0.27, 0.27, 0.27) });\n page.drawText(value, { x: VALUE_X, y, size: 10.5, font: helvBold, color: rgb(0.08, 0.08, 0.08) });\n }\n\n // Type chip\n const CHIP_W = 50;\n const CHIP_H = Math.min(CREAM_H - 8, 50);\n const CHIP_X = W - PAD - CHIP_W;\n const CHIP_Y = headerTop + (CREAM_H - CHIP_H) / 2;\n const typeCode = clip(ctx.fixtureType ?? \"—\", 4);\n\n page.drawRectangle({ x: CHIP_X, y: CHIP_Y, width: CHIP_W, height: CHIP_H, color: cardColors.chipBg });\n\n const typeLabelW = helv.widthOfTextAtSize(\"Type:\", 7);\n page.drawText(\"Type:\", {\n x: CHIP_X + (CHIP_W - typeLabelW) / 2,\n y: CHIP_Y + CHIP_H - 13,\n size: 7,\n font: helv,\n color: rgb(0.5, 0.5, 0.5),\n });\n\n const codeSize = typeCode.length <= 2 ? 22 : typeCode.length <= 3 ? 17 : 13;\n const codeW = helvBold.widthOfTextAtSize(typeCode, codeSize);\n page.drawText(typeCode, {\n x: CHIP_X + (CHIP_W - codeW) / 2,\n y: CHIP_Y + (CHIP_H - 13) / 2 - codeSize * 0.35,\n size: codeSize,\n font: helvBold,\n color: rgb(0.133, 0.133, 0.133),\n });\n}\n\nfunction drawSimpleInfoStrip(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n CREAM_H: number,\n headerTop: number,\n PAD: number,\n): void {\n const { helv, helvBold } = fonts;\n const stripY = headerTop + (CREAM_H - 9) / 2;\n\n if (ctx.projectName) {\n page.drawText(clip(ctx.projectName, 60), {\n x: PAD * 2,\n y: stripY,\n size: 9,\n font: helvBold,\n color: rgb(0.2, 0.2, 0.2),\n });\n }\n if (ctx.docNumber) {\n const numW = helv.widthOfTextAtSize(ctx.docNumber, 8);\n page.drawText(ctx.docNumber, { x: W - PAD - numW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n } else if (ctx.docType) {\n const dtW = helv.widthOfTextAtSize(ctx.docType, 8);\n page.drawText(ctx.docType, { x: W - PAD - dtW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { PDFArray, PDFName, PDFNumber, PDFRef, rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_FOOTER_HEIGHT = 38;\n\n/**\n * Per-page branded footer with prepared-by line, date, page numbers, and a\n * clickable \"Index\" link that jumps to page 1.\n */\nexport function drawBrandedFooter(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold } = fonts;\n const W = page.getWidth();\n const BOT_BANNER_H = BRANDED_FOOTER_HEIGHT;\n const PAD = 10;\n const darkGrey = rgb(0.25, 0.25, 0.25);\n const midGrey = rgb(0.55, 0.55, 0.55);\n\n page.drawRectangle({ x: 0, y: 0, width: W, height: BOT_BANNER_H, color: ctx.cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: BOT_BANNER_H },\n end: { x: W, y: BOT_BANNER_H },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n // TOP ROW\n const topRowY = BOT_BANNER_H - 13;\n page.drawText(\"Prepared By: \", { x: PAD, y: topRowY, size: 9, font: helv, color: darkGrey });\n const prepByW = helv.widthOfTextAtSize(\"Prepared By: \", 9);\n page.drawText(ctx.brand.agencyName, { x: PAD + prepByW, y: topRowY, size: 9, font: helvBold, color: darkGrey });\n\n const repParts: string[] = [];\n if (ctx.repName) repParts.push(ctx.repName);\n if (ctx.repEmail) repParts.push(ctx.repEmail);\n if (repParts.length > 0) {\n const repStr = repParts.join(\" | \");\n const repStrW = helv.widthOfTextAtSize(repStr, 9);\n page.drawText(repStr, { x: W - PAD - repStrW, y: topRowY, size: 9, font: helv, color: darkGrey });\n }\n\n // Thin divider\n const divY = Math.round(BOT_BANNER_H / 2) - 1;\n page.drawLine({\n start: { x: PAD, y: divY },\n end: { x: W - PAD, y: divY },\n thickness: 0.4,\n color: rgb(0.82, 0.82, 0.8),\n });\n\n // BOTTOM ROW\n const botRowY = 5;\n page.drawText(ctx.dateStamp, { x: PAD, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const pageStr = ctx.pageCount > 0 ? `${ctx.pageNumber} of ${ctx.pageCount}` : String(ctx.pageNumber);\n const pageStrW = helv.widthOfTextAtSize(pageStr, 8);\n page.drawText(pageStr, { x: (W - pageStrW) / 2, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const indexLabel = \"Index\";\n const indexW = helv.widthOfTextAtSize(indexLabel, 8);\n const indexX = W - PAD - indexW;\n page.drawText(indexLabel, { x: indexX, y: botRowY, size: 8, font: helv, color: ctx.accentColor });\n\n if (ctx.pageNumber > 1) {\n addGoToFirstPageLink(page, {\n x: indexX - 1,\n y: botRowY - 1,\n width: indexW + 2,\n height: 10,\n });\n }\n}\n\nfunction addGoToFirstPageLink(\n page: PDFPage,\n rect: { x: number; y: number; width: number; height: number },\n): void {\n const doc = page.doc;\n const pages = doc.getPages();\n if (pages.length === 0) return;\n const firstPageRef = doc.context.getObjectRef(pages[0].node);\n if (!firstPageRef) return;\n\n const linkDict = doc.context.obj({\n Type: \"Annot\",\n Subtype: \"Link\",\n Rect: [rect.x, rect.y, rect.x + rect.width, rect.y + rect.height],\n Border: [0, 0, 0],\n A: {\n Type: \"Action\",\n S: \"GoTo\",\n D: [firstPageRef, PDFName.of(\"Fit\")],\n },\n });\n const linkRef = doc.context.register(linkDict);\n\n const existing = page.node.Annots();\n if (existing instanceof PDFArray) {\n existing.push(linkRef);\n } else {\n page.node.set(PDFName.of(\"Annots\"), doc.context.obj([linkRef]));\n }\n\n void PDFRef;\n void PDFNumber;\n}\n"],"mappings":"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","names":["import_pdf_lib","import_pdf_lib","import_pdf_lib","import_pdf_lib"]}
1
+ {"version":3,"sources":["../src/pdf-lib/index.ts","../src/pdf-lib/fonts.ts","../src/pdf-lib/assets.ts","../src/brand.ts","../src/pdf-lib/cover.ts","../src/pdf-lib/header.ts","../src/pdf-lib/footer.ts"],"sourcesContent":["export { loadBookendFonts } from \"./fonts\";\nexport type { BookendFonts } from \"./fonts\";\n\nexport { loadBookendAssets } from \"./assets\";\nexport type { LoadBookendAssetsOptions, ResolvedBookendAssets } from \"./assets\";\n\nexport type { BookendContext } from \"./context\";\n\nexport { drawBrandedCover } from \"./cover\";\n\nexport {\n BRANDED_HEADER_HEIGHT,\n BRANDED_HEADER_MINIMAL_H,\n BRANDED_HEADER_PROMINENT_H,\n drawBrandedHeader,\n} from \"./header\";\n\nexport { BRANDED_FOOTER_HEIGHT, drawBrandedFooter } from \"./footer\";\n","import type { PDFDocument, PDFFont } from \"pdf-lib\";\nimport { StandardFonts } from \"pdf-lib\";\n\nexport interface BookendFonts {\n helv: PDFFont;\n helvBold: PDFFont;\n helvOblique: PDFFont;\n}\n\nexport async function loadBookendFonts(doc: PDFDocument): Promise<BookendFonts> {\n const [helv, helvBold, helvOblique] = await Promise.all([\n doc.embedFont(StandardFonts.Helvetica),\n doc.embedFont(StandardFonts.HelveticaBold),\n doc.embedFont(StandardFonts.HelveticaOblique),\n ]);\n return { helv, helvBold, helvOblique };\n}\n","import type { PDFDocument, PDFImage, RGB } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport { darkenHex, hexToRgb01, resolveBandThemeMode, resolveBestLogoUrl } from \"../brand\";\nimport type { BrandInput } from \"../types\";\n\nexport interface ResolvedBookendAssets {\n logoImage: PDFImage | null;\n logoAspect: number;\n bandColor: RGB;\n accentColor: RGB;\n cardColors: {\n cream: RGB;\n cardBorder: RGB;\n chipBg: RGB;\n };\n themeMode: \"dark\" | \"light\";\n}\n\nexport interface LoadBookendAssetsOptions {\n brand: BrandInput;\n doc: PDFDocument;\n /**\n * Optional URL resolver. Use when your app needs to convert relative paths\n * (e.g. /demo-files/logo.png) into absolute URLs for server-side fetching.\n * Defaults to identity (returns the URL unchanged).\n */\n resolveUrl?: (url: string) => string;\n /** Optional fetch timeout in ms. Default 8s. */\n fetchTimeoutMs?: number;\n}\n\n/**\n * Resolve brand colors + load logo PDFImage from URL.\n * Mirrors RepFirm's loadTenantBookendAssets behavior.\n */\nexport async function loadBookendAssets(opts: LoadBookendAssetsOptions): Promise<ResolvedBookendAssets> {\n const { brand, doc, resolveUrl = (u) => u, fetchTimeoutMs = 8_000 } = opts;\n const themeMode = resolveBandThemeMode(brand);\n const themeVars = brand.themeVars ?? {};\n\n // Band color\n let bandColorHex: string;\n if (themeMode === \"dark\") {\n bandColorHex =\n themeVars[\"--sidebar-bg\"] ??\n themeVars[\"--header-bg\"] ??\n (brand.primaryColor ? darkenHex(brand.primaryColor, 0.45) : \"#1e3a5f\");\n } else {\n bandColorHex = \"#fbfaf6\";\n }\n\n const accentHex = brand.primaryColor ?? themeVars[\"--accent\"] ?? \"#1e3a5f\";\n\n const [br, bg2, bb] = hexToRgb01(bandColorHex);\n const [ar, ag, ab] = hexToRgb01(accentHex);\n\n const cardColors = {\n cream: rgb(0.984, 0.98, 0.965),\n cardBorder: rgb(0.78, 0.78, 0.78),\n chipBg: rgb(0.937, 0.937, 0.933),\n };\n\n // Logo URL resolution\n const candidate = resolveBestLogoUrl(brand, themeMode);\n const logoUrl = candidate ? resolveUrl(candidate) : null;\n\n let logoImage: PDFImage | null = null;\n let logoAspect = 1;\n\n if (logoUrl && !logoUrl.toLowerCase().endsWith(\".svg\")) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), fetchTimeoutMs);\n let res: Response;\n try {\n res = await fetch(logoUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n if (res.ok) {\n const buf = new Uint8Array(await res.arrayBuffer());\n const lower = logoUrl.toLowerCase();\n if (lower.includes(\".png\") || (res.headers.get(\"content-type\") ?? \"\").includes(\"png\")) {\n logoImage = await doc.embedPng(buf);\n } else {\n logoImage = await doc.embedJpg(buf);\n }\n const dims = logoImage.scale(1);\n logoAspect = dims.width / dims.height;\n }\n } catch {\n // fall back to text-only header\n }\n }\n\n return {\n logoImage,\n logoAspect,\n bandColor: rgb(br, bg2, bb),\n accentColor: rgb(ar, ag, ab),\n cardColors,\n themeMode,\n };\n}\n","import type { BrandInput } from \"./types\";\n\n/**\n * Adapter: RepFirm TenantBranding shape -> BrandInput.\n *\n * RepFirm tenants store branding on the `tenants.branding` JSONB column with\n * keys like primary_color, logo_url, logo_dark_url, etc. (snake_case). Pair\n * with the tenant name from the parent row when calling.\n */\nexport interface TenantBrandingShape {\n primary_color?: string;\n logo_url?: string | null;\n logo_wordmark_url?: string | null;\n logo_mark_url?: string | null;\n logo_dark_url?: string | null;\n logo_light_url?: string | null;\n pdf_header_theme?: \"dark\" | \"light\" | \"auto\";\n theme_vars?: Record<string, string>;\n tagline?: string | null;\n company_address?: string | null;\n company_phone?: string | null;\n company_email?: string | null;\n}\n\nexport function brandFromTenantBranding(\n tenantName: string,\n branding: TenantBrandingShape | null | undefined,\n fallbackPrimary = \"#1e3a5f\",\n): BrandInput {\n const b = branding ?? {};\n return {\n agencyName: tenantName,\n primaryColor: b.primary_color ?? fallbackPrimary,\n logoUrl: b.logo_url ?? null,\n logoDarkUrl: b.logo_dark_url ?? null,\n logoLightUrl: b.logo_light_url ?? null,\n logoWordmarkUrl: b.logo_wordmark_url ?? null,\n logoMarkUrl: b.logo_mark_url ?? null,\n pdfHeaderTheme: b.pdf_header_theme ?? \"auto\",\n themeVars: b.theme_vars ?? {},\n tagline: b.tagline ?? null,\n address: b.company_address ?? null,\n phone: b.company_phone ?? null,\n email: b.company_email ?? null,\n };\n}\n\n/**\n * Adapter: dygarn-dashboard prospect brand shape -> BrandInput.\n *\n * Dygarn-dashboard stores per-prospect brand kits in outreach_prospect_brands\n * with simpler shape (camelCase or snake — be liberal in what we accept).\n */\nexport interface ProspectBrandShape {\n agency_name?: string;\n agencyName?: string;\n legal_name?: string | null;\n legalName?: string | null;\n logo_url?: string | null;\n logoUrl?: string | null;\n primary_color?: string;\n primaryColor?: string;\n secondary_color?: string;\n secondaryColor?: string;\n address?: string | null;\n phone?: string | null;\n website?: string | null;\n}\n\nexport function brandFromProspectBrand(p: ProspectBrandShape): BrandInput {\n const agency = p.agencyName ?? p.agency_name ?? \"Agency\";\n return {\n agencyName: agency,\n legalName: p.legalName ?? p.legal_name ?? null,\n logoUrl: p.logoUrl ?? p.logo_url ?? null,\n primaryColor: p.primaryColor ?? p.primary_color ?? \"#1a73e8\",\n address: p.address ?? null,\n phone: p.phone ?? null,\n website: p.website ?? null,\n pdfHeaderTheme: \"auto\",\n };\n}\n\n/**\n * Determine whether to use a dark or light header band for this brand.\n *\n * - Explicit override: respect pdfHeaderTheme if set to 'dark' or 'light'.\n * - Auto (default): use dark theme only when a logoDarkUrl (white-on-dark\n * variant) is available, OR when the logo filename hints at a white/dark\n * variant. Otherwise default to LIGHT (safe for multi-color logos).\n */\nexport function resolveBandThemeMode(brand: BrandInput): \"dark\" | \"light\" {\n if (brand.pdfHeaderTheme === \"dark\") return \"dark\";\n if (brand.pdfHeaderTheme === \"light\") return \"light\";\n if (brand.logoDarkUrl) return \"dark\";\n const candidate = (brand.logoWordmarkUrl ?? brand.logoUrl ?? brand.logoMarkUrl ?? \"\").toLowerCase();\n if (candidate.includes(\"white\") || candidate.includes(\"-on-dark\") || candidate.includes(\"_dark\")) return \"dark\";\n return \"light\";\n}\n\n/**\n * Pick the best logo URL for the resolved theme mode.\n * Returns null if no logo available.\n */\nexport function resolveBestLogoUrl(brand: BrandInput, mode: \"dark\" | \"light\"): string | null {\n if (mode === \"dark\") {\n return brand.logoDarkUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? null;\n }\n return (\n brand.logoLightUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? brand.logoDarkUrl ?? null\n );\n}\n\n/**\n * Darken a hex color by a 0-1 amount. Used to derive a dark band color from\n * a brand primary when no explicit dark variant exists.\n */\nexport function darkenHex(hex: string, amount: number): string {\n const m = /^#?([0-9a-f]{6})$/i.exec(hex);\n if (!m) return hex;\n const n = Number.parseInt(m[1], 16);\n let r = (n >> 16) & 0xff;\n let g = (n >> 8) & 0xff;\n let b = n & 0xff;\n r = Math.max(0, Math.round(r * (1 - amount)));\n g = Math.max(0, Math.round(g * (1 - amount)));\n b = Math.max(0, Math.round(b * (1 - amount)));\n return `#${[r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/**\n * Convert hex to {r,g,b} normalized 0-1. Used by both React-PDF (color objects)\n * and pdf-lib (rgb() arguments).\n */\nexport function hexToRgb01(hex: string): [number, number, number] {\n const h = hex.replace(\"#\", \"\").trim();\n const r = Number.parseInt(h.slice(0, 2), 16) / 255;\n const g = Number.parseInt(h.slice(2, 4), 16) / 255;\n const b = Number.parseInt(h.slice(4, 6), 16) / 255;\n return [Number.isFinite(r) ? r : 0.12, Number.isFinite(g) ? g : 0.23, Number.isFinite(b) ? b : 0.37];\n}\n","import type { PDFFont, PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\n/**\n * Greedy word-wrap by measured width. Words longer than maxWidth are emitted\n * on their own line (they will visually overflow but at least won't crash the\n * layout). Returns at least one line.\n */\nfunction wrapByWidth(text: string, font: PDFFont, size: number, maxWidth: number): string[] {\n const out: string[] = [];\n const words = text.split(/\\s+/).filter(Boolean);\n let line = \"\";\n for (const w of words) {\n const candidate = line ? `${line} ${w}` : w;\n if (font.widthOfTextAtSize(candidate, size) <= maxWidth) {\n line = candidate;\n } else {\n if (line) out.push(line);\n line = w;\n }\n }\n if (line) out.push(line);\n return out.length > 0 ? out : [text];\n}\n\n/**\n * Draw a branded cover page. Logo centered on the band, doc type label,\n * project name, location, doc number, recipient, prepared-by signature block,\n * bottom metadata line.\n *\n * Designed for 612x792 (US Letter) page size. Caller is responsible for\n * adding the page to the document.\n */\nexport function drawBrandedCover(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold, helvOblique } = fonts;\n const { bandColor, accentColor, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const CX = W / 2;\n const agencyName = ctx.brand.agencyName;\n\n // Top branded band\n const COVER_HEADER_H = 130;\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H, width: W, height: COVER_HEADER_H, color: bandColor });\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H - 4, width: W, height: 4, color: accentColor });\n\n // Logo or name inside the band\n const COVER_LOGO_H = 70;\n const COVER_LOGO_W = Math.round(COVER_LOGO_H * (logoAspect || 1));\n if (logoImage) {\n page.drawImage(logoImage, {\n x: CX - COVER_LOGO_W / 2,\n y: H - COVER_HEADER_H + (COVER_HEADER_H - COVER_LOGO_H) / 2,\n width: COVER_LOGO_W,\n height: COVER_LOGO_H,\n });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n const nameW = helvBold.widthOfTextAtSize(agencyName, 28);\n page.drawText(agencyName, {\n x: CX - nameW / 2,\n y: H - COVER_HEADER_H / 2 - 10,\n size: 28,\n font: helvBold,\n color: nameColor,\n });\n }\n\n // Project / document block (centered, upper-middle)\n let cursorY = H - COVER_HEADER_H - 70;\n\n const docTypeUpper = ctx.docType.toUpperCase();\n const tagW = helvBold.widthOfTextAtSize(docTypeUpper, 11);\n page.drawText(docTypeUpper, {\n x: CX - tagW / 2,\n y: cursorY,\n size: 11,\n font: helvBold,\n color: rgb(0.45, 0.45, 0.45),\n });\n cursorY -= 30;\n\n if (ctx.projectName) {\n // Available column for the project name title. Leave a small margin off\n // each edge so the text never butts against the page border.\n const PROJ_MARGIN = 36;\n const PROJ_MAX_W = W - PROJ_MARGIN * 2;\n const PROJ_MAX_SIZE = 22;\n const PROJ_MIN_SIZE = 14;\n\n // 1) Try shrinking from MAX down to MIN, single line, until it fits.\n let projSize = PROJ_MAX_SIZE;\n while (projSize > PROJ_MIN_SIZE && helvBold.widthOfTextAtSize(ctx.projectName, projSize) > PROJ_MAX_W) {\n projSize -= 1;\n }\n\n if (helvBold.widthOfTextAtSize(ctx.projectName, projSize) <= PROJ_MAX_W) {\n // Single line fits at this size.\n const projW = helvBold.widthOfTextAtSize(ctx.projectName, projSize);\n page.drawText(ctx.projectName, {\n x: CX - projW / 2,\n y: cursorY,\n size: projSize,\n font: helvBold,\n color: rgb(0.08, 0.08, 0.08),\n });\n cursorY -= projSize + 2;\n } else {\n // 2) Still too long at min size. Word-wrap to 2+ lines at min size.\n const lines = wrapByWidth(ctx.projectName, helvBold, PROJ_MIN_SIZE, PROJ_MAX_W);\n for (const line of lines) {\n const lineW = helvBold.widthOfTextAtSize(line, PROJ_MIN_SIZE);\n page.drawText(line, {\n x: CX - lineW / 2,\n y: cursorY,\n size: PROJ_MIN_SIZE,\n font: helvBold,\n color: rgb(0.08, 0.08, 0.08),\n });\n cursorY -= PROJ_MIN_SIZE + 2;\n }\n // Add the trailing gap the original layout expected.\n cursorY -= 2;\n }\n }\n\n if (ctx.location) {\n const locW = helv.widthOfTextAtSize(ctx.location, 12);\n page.drawText(ctx.location, {\n x: CX - locW / 2,\n y: cursorY,\n size: 12,\n font: helv,\n color: rgb(0.4, 0.4, 0.4),\n });\n cursorY -= 36;\n } else {\n cursorY -= 24;\n }\n\n if (ctx.docNumber) {\n const numLabel = ctx.docNumber;\n const numW = helvBold.widthOfTextAtSize(numLabel, 12);\n page.drawText(numLabel, {\n x: CX - numW / 2,\n y: cursorY,\n size: 12,\n font: helvBold,\n color: rgb(0.25, 0.25, 0.25),\n });\n cursorY -= 28;\n }\n\n const recipientName = ctx.recipientName ?? ctx.distributorName ?? null;\n const recipientLabel = ctx.recipientLabel ?? (ctx.distributorName ? \"DISTRIBUTOR\" : null);\n if (recipientName && recipientLabel) {\n const labelW = helvBold.widthOfTextAtSize(recipientLabel, 11);\n const gap = 14;\n const valueW = helvBold.widthOfTextAtSize(recipientName, 13);\n const totalW = labelW + gap + valueW;\n const rowX = CX - totalW / 2;\n page.drawText(recipientLabel, { x: rowX, y: cursorY, size: 11, font: helvBold, color: rgb(0.45, 0.45, 0.45) });\n page.drawText(recipientName, { x: rowX + labelW + gap, y: cursorY, size: 13, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n }\n\n // Bottom signature block\n const SIG_BLOCK_TOP = 140;\n let sigY = SIG_BLOCK_TOP;\n\n page.drawRectangle({ x: CX - 100, y: sigY, width: 200, height: 0.5, color: rgb(0.7, 0.7, 0.7) });\n sigY -= 18;\n\n const prepLabel = \"- Prepared By -\";\n const prepLabelW = helvOblique.widthOfTextAtSize(prepLabel, 10);\n page.drawText(prepLabel, { x: CX - prepLabelW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.5, 0.5, 0.5) });\n sigY -= 18;\n\n if (ctx.repName) {\n const repNameW = helvBold.widthOfTextAtSize(ctx.repName, 12);\n page.drawText(ctx.repName, { x: CX - repNameW / 2, y: sigY, size: 12, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n sigY -= 16;\n }\n if (ctx.repEmail) {\n const repEmailW = helv.widthOfTextAtSize(ctx.repEmail, 10);\n page.drawText(ctx.repEmail, { x: CX - repEmailW / 2, y: sigY, size: 10, font: helv, color: rgb(0.16, 0.42, 0.68) });\n sigY -= 14;\n }\n\n const tnW = helvOblique.widthOfTextAtSize(agencyName, 10);\n page.drawText(agencyName, { x: CX - tnW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.4, 0.4, 0.4) });\n\n // Bottom-most: doc number + date\n const footerLine = [ctx.docNumber, ctx.dateStamp].filter(Boolean).join(\" | \");\n if (footerLine) {\n const flW = helv.widthOfTextAtSize(footerLine, 9);\n page.drawText(footerLine, { x: CX - flW / 2, y: 36, size: 9, font: helv, color: rgb(0.55, 0.55, 0.55) });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_HEADER_HEIGHT = 110; // dark band + stripe + cream info zone\nexport const BRANDED_HEADER_MINIMAL_H = 56; // dark band + stripe + thin cream strip\nexport const BRANDED_HEADER_PROMINENT_H = 180; // tall band for quote-style covers (bigger logo)\n\nfunction clip(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, max - 1)}…`;\n}\n\n/**\n * Draw the per-page branded header.\n *\n * showFixtureCard=true (datasheet pages): dark band 38pt + stripe 3pt + cream\n * info zone with white card (Job Name / Manufacturer / Model Number) + type\n * chip on right. Use for submittal datasheet pages.\n *\n * showFixtureCard=false (default): dark band 38pt + stripe 3pt + thin cream\n * strip with project name + doc number. Use for non-datasheet pages.\n */\nexport function drawBrandedHeader(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n opts?: { showFixtureCard?: boolean; prominent?: boolean },\n): void {\n const showFixtureCard = opts?.showFixtureCard ?? false;\n const prominent = opts?.prominent ?? false;\n if (prominent) {\n drawProminentHeader(page, ctx, fonts);\n return;\n }\n const { helv, helvBold } = fonts;\n const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const TOP_BANNER_H = showFixtureCard ? BRANDED_HEADER_HEIGHT : BRANDED_HEADER_MINIMAL_H;\n\n const PAD = 12;\n const DARK_H = 38;\n const STRIPE_H = 3;\n const CREAM_H = TOP_BANNER_H - DARK_H - STRIPE_H;\n\n const darkBandY = H - DARK_H;\n const stripeY = darkBandY - STRIPE_H;\n const headerTop = H - TOP_BANNER_H;\n\n page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });\n page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });\n page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: headerTop },\n end: { x: W, y: headerTop },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n if (logoImage) {\n const bandLogoH = 26;\n const logoW = Math.round(bandLogoH * logoAspect);\n page.drawImage(logoImage, { x: PAD, y: darkBandY + (DARK_H - bandLogoH) / 2, width: logoW, height: bandLogoH });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n page.drawText(ctx.brand.agencyName, { x: PAD, y: darkBandY + 12, size: 12, font: helvBold, color: nameColor });\n }\n\n if (showFixtureCard) {\n drawFixtureCard(page, ctx, fonts, W, TOP_BANNER_H, PAD, CREAM_H, headerTop);\n } else {\n drawSimpleInfoStrip(page, ctx, fonts, W, CREAM_H, headerTop, PAD);\n }\n}\n\nfunction drawFixtureCard(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n _TOP_BANNER_H: number,\n PAD: number,\n CREAM_H: number,\n headerTop: number,\n): void {\n const { helv, helvBold } = fonts;\n const { cardColors } = ctx;\n\n const CHIP_W_RESERVE = 60;\n const CARD_X = PAD * 2;\n const CARD_W = W - CARD_X - CHIP_W_RESERVE - PAD;\n const CARD_H = Math.min(CREAM_H - 8, 60);\n const CARD_Y = headerTop + (CREAM_H - CARD_H) / 2;\n\n page.drawRectangle({\n x: CARD_X,\n y: CARD_Y,\n width: CARD_W,\n height: CARD_H,\n borderColor: cardColors.cardBorder,\n borderWidth: 0.75,\n color: rgb(1, 1, 1),\n });\n\n const LABEL_X = CARD_X + 14;\n const VALUE_X = CARD_X + 100;\n const ROW1_Y = CARD_Y + CARD_H - 17;\n const ROW2_Y = ROW1_Y - 18;\n const ROW3_Y = ROW2_Y - 18;\n\n const jobName = (ctx.projectName || \"—\").toUpperCase();\n const rows: Array<[string, string]> = [\n [\"Job Name:\", clip(jobName, 48)],\n [\"Manufacturer:\", clip(ctx.fixtureMfr ?? \"—\", 44)],\n [\"Model Number:\", clip(ctx.fixturePartNumber ?? \"—\", 44)],\n ];\n const rowYs = [ROW1_Y, ROW2_Y, ROW3_Y];\n for (let i = 0; i < rows.length; i++) {\n const [label, value] = rows[i];\n const y = rowYs[i];\n page.drawText(label, { x: LABEL_X, y, size: 9, font: helvBold, color: rgb(0.27, 0.27, 0.27) });\n page.drawText(value, { x: VALUE_X, y, size: 10.5, font: helvBold, color: rgb(0.08, 0.08, 0.08) });\n }\n\n // Type chip\n const CHIP_W = 50;\n const CHIP_H = Math.min(CREAM_H - 8, 50);\n const CHIP_X = W - PAD - CHIP_W;\n const CHIP_Y = headerTop + (CREAM_H - CHIP_H) / 2;\n const typeCode = clip(ctx.fixtureType ?? \"—\", 4);\n\n page.drawRectangle({ x: CHIP_X, y: CHIP_Y, width: CHIP_W, height: CHIP_H, color: cardColors.chipBg });\n\n const typeLabelW = helv.widthOfTextAtSize(\"Type:\", 7);\n page.drawText(\"Type:\", {\n x: CHIP_X + (CHIP_W - typeLabelW) / 2,\n y: CHIP_Y + CHIP_H - 13,\n size: 7,\n font: helv,\n color: rgb(0.5, 0.5, 0.5),\n });\n\n const codeSize = typeCode.length <= 2 ? 22 : typeCode.length <= 3 ? 17 : 13;\n const codeW = helvBold.widthOfTextAtSize(typeCode, codeSize);\n page.drawText(typeCode, {\n x: CHIP_X + (CHIP_W - codeW) / 2,\n y: CHIP_Y + (CHIP_H - 13) / 2 - codeSize * 0.35,\n size: codeSize,\n font: helvBold,\n color: rgb(0.133, 0.133, 0.133),\n });\n}\n\nfunction drawSimpleInfoStrip(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n CREAM_H: number,\n headerTop: number,\n PAD: number,\n): void {\n const { helv, helvBold } = fonts;\n const stripY = headerTop + (CREAM_H - 9) / 2;\n\n if (ctx.projectName) {\n page.drawText(clip(ctx.projectName, 60), {\n x: PAD * 2,\n y: stripY,\n size: 9,\n font: helvBold,\n color: rgb(0.2, 0.2, 0.2),\n });\n }\n if (ctx.docNumber) {\n const numW = helv.widthOfTextAtSize(ctx.docNumber, 8);\n page.drawText(ctx.docNumber, { x: W - PAD - numW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n } else if (ctx.docType) {\n const dtW = helv.widthOfTextAtSize(ctx.docType, 8);\n page.drawText(ctx.docType, { x: W - PAD - dtW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n }\n}\n\n\nfunction drawProminentHeader(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n): void {\n const { helv, helvBold } = fonts;\n const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const TOP_BANNER_H = BRANDED_HEADER_PROMINENT_H;\n\n const PAD = 20;\n const STRIPE_H = 3;\n const CREAM_H = 28; // thin cream strip at bottom of band for meta info\n const DARK_H = TOP_BANNER_H - STRIPE_H - CREAM_H;\n\n const darkBandY = H - DARK_H;\n const stripeY = darkBandY - STRIPE_H;\n const headerTop = H - TOP_BANNER_H;\n\n page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });\n page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });\n page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: headerTop },\n end: { x: W, y: headerTop },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n if (logoImage) {\n const LOGO_MAX_H = 120;\n const LOGO_MAX_W = Math.min(W - PAD * 2, 320);\n let logoH = LOGO_MAX_H;\n let logoW = logoH * logoAspect;\n if (logoW > LOGO_MAX_W) {\n logoW = LOGO_MAX_W;\n logoH = logoW / logoAspect;\n }\n const logoX = PAD;\n const logoY = darkBandY + (DARK_H - logoH) / 2;\n page.drawImage(logoImage, { x: logoX, y: logoY, width: logoW, height: logoH });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n const nameSize = 28;\n page.drawText(ctx.brand.agencyName, {\n x: PAD,\n y: darkBandY + (DARK_H - nameSize) / 2,\n size: nameSize,\n font: helvBold,\n color: nameColor,\n });\n }\n\n // Doc number bottom-right of dark band\n if (ctx.docNumber) {\n const docColor = themeMode === \"dark\" ? rgb(0.9, 0.9, 0.9) : rgb(0.15, 0.15, 0.15);\n const docSize = 12;\n const docW = helvBold.widthOfTextAtSize(ctx.docNumber, docSize);\n page.drawText(ctx.docNumber, {\n x: W - PAD - docW,\n y: darkBandY + 14,\n size: docSize,\n font: helvBold,\n color: docColor,\n });\n if (ctx.dateStamp) {\n const dateColor = themeMode === \"dark\" ? rgb(0.7, 0.7, 0.7) : rgb(0.45, 0.45, 0.45);\n const dateSize = 9;\n const dateW = helv.widthOfTextAtSize(ctx.dateStamp, dateSize);\n page.drawText(ctx.dateStamp, {\n x: W - PAD - dateW,\n y: darkBandY + 2,\n size: dateSize,\n font: helv,\n color: dateColor,\n });\n }\n }\n\n // Project name small + grey in cream strip\n const stripY = headerTop + (CREAM_H - 9) / 2;\n if (ctx.projectName) {\n page.drawText(clip(ctx.projectName, 70), {\n x: PAD,\n y: stripY,\n size: 9,\n font: helvBold,\n color: rgb(0.4, 0.4, 0.4),\n });\n }\n if (ctx.docType) {\n const dtW = helv.widthOfTextAtSize(ctx.docType, 8);\n page.drawText(ctx.docType, {\n x: W - PAD - dtW,\n y: stripY + 0.5,\n size: 8,\n font: helv,\n color: rgb(0.45, 0.45, 0.45),\n });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { PDFArray, PDFName, PDFNumber, PDFRef, rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_FOOTER_HEIGHT = 38;\n\n/**\n * Per-page branded footer with prepared-by line, date, page numbers, and a\n * clickable \"Index\" link that jumps to page 1.\n */\nexport function drawBrandedFooter(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold } = fonts;\n const W = page.getWidth();\n const BOT_BANNER_H = BRANDED_FOOTER_HEIGHT;\n const PAD = 10;\n const darkGrey = rgb(0.25, 0.25, 0.25);\n const midGrey = rgb(0.55, 0.55, 0.55);\n\n page.drawRectangle({ x: 0, y: 0, width: W, height: BOT_BANNER_H, color: ctx.cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: BOT_BANNER_H },\n end: { x: W, y: BOT_BANNER_H },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n // TOP ROW\n const topRowY = BOT_BANNER_H - 13;\n page.drawText(\"Prepared By: \", { x: PAD, y: topRowY, size: 9, font: helv, color: darkGrey });\n const prepByW = helv.widthOfTextAtSize(\"Prepared By: \", 9);\n page.drawText(ctx.brand.agencyName, { x: PAD + prepByW, y: topRowY, size: 9, font: helvBold, color: darkGrey });\n\n const repParts: string[] = [];\n if (ctx.repName) repParts.push(ctx.repName);\n if (ctx.repEmail) repParts.push(ctx.repEmail);\n if (repParts.length > 0) {\n const repStr = repParts.join(\" | \");\n const repStrW = helv.widthOfTextAtSize(repStr, 9);\n page.drawText(repStr, { x: W - PAD - repStrW, y: topRowY, size: 9, font: helv, color: darkGrey });\n }\n\n // Thin divider\n const divY = Math.round(BOT_BANNER_H / 2) - 1;\n page.drawLine({\n start: { x: PAD, y: divY },\n end: { x: W - PAD, y: divY },\n thickness: 0.4,\n color: rgb(0.82, 0.82, 0.8),\n });\n\n // BOTTOM ROW\n const botRowY = 5;\n page.drawText(ctx.dateStamp, { x: PAD, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const pageStr = ctx.pageCount > 0 ? `${ctx.pageNumber} of ${ctx.pageCount}` : String(ctx.pageNumber);\n const pageStrW = helv.widthOfTextAtSize(pageStr, 8);\n page.drawText(pageStr, { x: (W - pageStrW) / 2, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const indexLabel = \"Index\";\n const indexW = helv.widthOfTextAtSize(indexLabel, 8);\n const indexX = W - PAD - indexW;\n page.drawText(indexLabel, { x: indexX, y: botRowY, size: 8, font: helv, color: ctx.accentColor });\n\n if (ctx.pageNumber > 1) {\n addGoToFirstPageLink(page, {\n x: indexX - 1,\n y: botRowY - 1,\n width: indexW + 2,\n height: 10,\n });\n }\n}\n\nfunction addGoToFirstPageLink(\n page: PDFPage,\n rect: { x: number; y: number; width: number; height: number },\n): void {\n const doc = page.doc;\n const pages = doc.getPages();\n if (pages.length === 0) return;\n const firstPageRef = doc.context.getObjectRef(pages[0].node);\n if (!firstPageRef) return;\n\n const linkDict = doc.context.obj({\n Type: \"Annot\",\n Subtype: \"Link\",\n Rect: [rect.x, rect.y, rect.x + rect.width, rect.y + rect.height],\n Border: [0, 0, 0],\n A: {\n Type: \"Action\",\n S: \"GoTo\",\n D: [firstPageRef, PDFName.of(\"Fit\")],\n },\n });\n const linkRef = doc.context.register(linkDict);\n\n const existing = page.node.Annots();\n if (existing instanceof PDFArray) {\n existing.push(linkRef);\n } else {\n page.node.set(PDFName.of(\"Annots\"), doc.context.obj([linkRef]));\n }\n\n void PDFRef;\n void PDFNumber;\n}\n"],"mappings":"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","names":["import_pdf_lib","import_pdf_lib","import_pdf_lib","import_pdf_lib"]}
@@ -95,6 +95,7 @@ declare function drawBrandedCover(page: PDFPage, ctx: BookendContext, fonts: Boo
95
95
 
96
96
  declare const BRANDED_HEADER_HEIGHT = 110;
97
97
  declare const BRANDED_HEADER_MINIMAL_H = 56;
98
+ declare const BRANDED_HEADER_PROMINENT_H = 180;
98
99
  /**
99
100
  * Draw the per-page branded header.
100
101
  *
@@ -107,6 +108,7 @@ declare const BRANDED_HEADER_MINIMAL_H = 56;
107
108
  */
108
109
  declare function drawBrandedHeader(page: PDFPage, ctx: BookendContext, fonts: BookendFonts, opts?: {
109
110
  showFixtureCard?: boolean;
111
+ prominent?: boolean;
110
112
  }): void;
111
113
 
112
114
  declare const BRANDED_FOOTER_HEIGHT = 38;
@@ -116,4 +118,4 @@ declare const BRANDED_FOOTER_HEIGHT = 38;
116
118
  */
117
119
  declare function drawBrandedFooter(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void;
118
120
 
119
- export { BRANDED_FOOTER_HEIGHT, BRANDED_HEADER_HEIGHT, BRANDED_HEADER_MINIMAL_H, type BookendContext, type BookendFonts, type LoadBookendAssetsOptions, type ResolvedBookendAssets, drawBrandedCover, drawBrandedFooter, drawBrandedHeader, loadBookendAssets, loadBookendFonts };
121
+ export { BRANDED_FOOTER_HEIGHT, BRANDED_HEADER_HEIGHT, BRANDED_HEADER_MINIMAL_H, BRANDED_HEADER_PROMINENT_H, type BookendContext, type BookendFonts, type LoadBookendAssetsOptions, type ResolvedBookendAssets, drawBrandedCover, drawBrandedFooter, drawBrandedHeader, loadBookendAssets, loadBookendFonts };
package/dist/pdf-lib.d.ts CHANGED
@@ -95,6 +95,7 @@ declare function drawBrandedCover(page: PDFPage, ctx: BookendContext, fonts: Boo
95
95
 
96
96
  declare const BRANDED_HEADER_HEIGHT = 110;
97
97
  declare const BRANDED_HEADER_MINIMAL_H = 56;
98
+ declare const BRANDED_HEADER_PROMINENT_H = 180;
98
99
  /**
99
100
  * Draw the per-page branded header.
100
101
  *
@@ -107,6 +108,7 @@ declare const BRANDED_HEADER_MINIMAL_H = 56;
107
108
  */
108
109
  declare function drawBrandedHeader(page: PDFPage, ctx: BookendContext, fonts: BookendFonts, opts?: {
109
110
  showFixtureCard?: boolean;
111
+ prominent?: boolean;
110
112
  }): void;
111
113
 
112
114
  declare const BRANDED_FOOTER_HEIGHT = 38;
@@ -116,4 +118,4 @@ declare const BRANDED_FOOTER_HEIGHT = 38;
116
118
  */
117
119
  declare function drawBrandedFooter(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void;
118
120
 
119
- export { BRANDED_FOOTER_HEIGHT, BRANDED_HEADER_HEIGHT, BRANDED_HEADER_MINIMAL_H, type BookendContext, type BookendFonts, type LoadBookendAssetsOptions, type ResolvedBookendAssets, drawBrandedCover, drawBrandedFooter, drawBrandedHeader, loadBookendAssets, loadBookendFonts };
121
+ export { BRANDED_FOOTER_HEIGHT, BRANDED_HEADER_HEIGHT, BRANDED_HEADER_MINIMAL_H, BRANDED_HEADER_PROMINENT_H, type BookendContext, type BookendFonts, type LoadBookendAssetsOptions, type ResolvedBookendAssets, drawBrandedCover, drawBrandedFooter, drawBrandedHeader, loadBookendAssets, loadBookendFonts };
package/dist/pdf-lib.js CHANGED
@@ -106,6 +106,22 @@ async function loadBookendAssets(opts) {
106
106
 
107
107
  // src/pdf-lib/cover.ts
108
108
  import { rgb as rgb2 } from "pdf-lib";
109
+ function wrapByWidth(text, font, size, maxWidth) {
110
+ const out = [];
111
+ const words = text.split(/\s+/).filter(Boolean);
112
+ let line = "";
113
+ for (const w of words) {
114
+ const candidate = line ? `${line} ${w}` : w;
115
+ if (font.widthOfTextAtSize(candidate, size) <= maxWidth) {
116
+ line = candidate;
117
+ } else {
118
+ if (line) out.push(line);
119
+ line = w;
120
+ }
121
+ }
122
+ if (line) out.push(line);
123
+ return out.length > 0 ? out : [text];
124
+ }
109
125
  function drawBrandedCover(page, ctx, fonts) {
110
126
  const { helv, helvBold, helvOblique } = fonts;
111
127
  const { bandColor, accentColor, logoImage, logoAspect, themeMode } = ctx;
@@ -148,15 +164,39 @@ function drawBrandedCover(page, ctx, fonts) {
148
164
  });
149
165
  cursorY -= 30;
150
166
  if (ctx.projectName) {
151
- const projW = helvBold.widthOfTextAtSize(ctx.projectName, 22);
152
- page.drawText(ctx.projectName, {
153
- x: CX - projW / 2,
154
- y: cursorY,
155
- size: 22,
156
- font: helvBold,
157
- color: rgb2(0.08, 0.08, 0.08)
158
- });
159
- cursorY -= 24;
167
+ const PROJ_MARGIN = 36;
168
+ const PROJ_MAX_W = W - PROJ_MARGIN * 2;
169
+ const PROJ_MAX_SIZE = 22;
170
+ const PROJ_MIN_SIZE = 14;
171
+ let projSize = PROJ_MAX_SIZE;
172
+ while (projSize > PROJ_MIN_SIZE && helvBold.widthOfTextAtSize(ctx.projectName, projSize) > PROJ_MAX_W) {
173
+ projSize -= 1;
174
+ }
175
+ if (helvBold.widthOfTextAtSize(ctx.projectName, projSize) <= PROJ_MAX_W) {
176
+ const projW = helvBold.widthOfTextAtSize(ctx.projectName, projSize);
177
+ page.drawText(ctx.projectName, {
178
+ x: CX - projW / 2,
179
+ y: cursorY,
180
+ size: projSize,
181
+ font: helvBold,
182
+ color: rgb2(0.08, 0.08, 0.08)
183
+ });
184
+ cursorY -= projSize + 2;
185
+ } else {
186
+ const lines = wrapByWidth(ctx.projectName, helvBold, PROJ_MIN_SIZE, PROJ_MAX_W);
187
+ for (const line of lines) {
188
+ const lineW = helvBold.widthOfTextAtSize(line, PROJ_MIN_SIZE);
189
+ page.drawText(line, {
190
+ x: CX - lineW / 2,
191
+ y: cursorY,
192
+ size: PROJ_MIN_SIZE,
193
+ font: helvBold,
194
+ color: rgb2(0.08, 0.08, 0.08)
195
+ });
196
+ cursorY -= PROJ_MIN_SIZE + 2;
197
+ }
198
+ cursorY -= 2;
199
+ }
160
200
  }
161
201
  if (ctx.location) {
162
202
  const locW = helv.widthOfTextAtSize(ctx.location, 12);
@@ -225,12 +265,18 @@ function drawBrandedCover(page, ctx, fonts) {
225
265
  import { rgb as rgb3 } from "pdf-lib";
226
266
  var BRANDED_HEADER_HEIGHT = 110;
227
267
  var BRANDED_HEADER_MINIMAL_H = 56;
268
+ var BRANDED_HEADER_PROMINENT_H = 180;
228
269
  function clip(s, max) {
229
270
  if (s.length <= max) return s;
230
271
  return `${s.slice(0, max - 1)}\u2026`;
231
272
  }
232
273
  function drawBrandedHeader(page, ctx, fonts, opts) {
233
274
  const showFixtureCard = opts?.showFixtureCard ?? false;
275
+ const prominent = opts?.prominent ?? false;
276
+ if (prominent) {
277
+ drawProminentHeader(page, ctx, fonts);
278
+ return;
279
+ }
234
280
  const { helv, helvBold } = fonts;
235
281
  const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;
236
282
  const W = page.getWidth();
@@ -345,6 +391,96 @@ function drawSimpleInfoStrip(page, ctx, fonts, W, CREAM_H, headerTop, PAD) {
345
391
  page.drawText(ctx.docType, { x: W - PAD - dtW, y: stripY, size: 8, font: helv, color: rgb3(0.4, 0.4, 0.4) });
346
392
  }
347
393
  }
394
+ function drawProminentHeader(page, ctx, fonts) {
395
+ const { helv, helvBold } = fonts;
396
+ const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;
397
+ const W = page.getWidth();
398
+ const H = page.getHeight();
399
+ const TOP_BANNER_H = BRANDED_HEADER_PROMINENT_H;
400
+ const PAD = 20;
401
+ const STRIPE_H = 3;
402
+ const CREAM_H = 28;
403
+ const DARK_H = TOP_BANNER_H - STRIPE_H - CREAM_H;
404
+ const darkBandY = H - DARK_H;
405
+ const stripeY = darkBandY - STRIPE_H;
406
+ const headerTop = H - TOP_BANNER_H;
407
+ page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });
408
+ page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });
409
+ page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });
410
+ page.drawLine({
411
+ start: { x: 0, y: headerTop },
412
+ end: { x: W, y: headerTop },
413
+ thickness: 0.5,
414
+ color: rgb3(0.8, 0.8, 0.78)
415
+ });
416
+ if (logoImage) {
417
+ const LOGO_MAX_H = 120;
418
+ const LOGO_MAX_W = Math.min(W - PAD * 2, 320);
419
+ let logoH = LOGO_MAX_H;
420
+ let logoW = logoH * logoAspect;
421
+ if (logoW > LOGO_MAX_W) {
422
+ logoW = LOGO_MAX_W;
423
+ logoH = logoW / logoAspect;
424
+ }
425
+ const logoX = PAD;
426
+ const logoY = darkBandY + (DARK_H - logoH) / 2;
427
+ page.drawImage(logoImage, { x: logoX, y: logoY, width: logoW, height: logoH });
428
+ } else {
429
+ const nameColor = themeMode === "dark" ? rgb3(1, 1, 1) : rgb3(0.08, 0.08, 0.08);
430
+ const nameSize = 28;
431
+ page.drawText(ctx.brand.agencyName, {
432
+ x: PAD,
433
+ y: darkBandY + (DARK_H - nameSize) / 2,
434
+ size: nameSize,
435
+ font: helvBold,
436
+ color: nameColor
437
+ });
438
+ }
439
+ if (ctx.docNumber) {
440
+ const docColor = themeMode === "dark" ? rgb3(0.9, 0.9, 0.9) : rgb3(0.15, 0.15, 0.15);
441
+ const docSize = 12;
442
+ const docW = helvBold.widthOfTextAtSize(ctx.docNumber, docSize);
443
+ page.drawText(ctx.docNumber, {
444
+ x: W - PAD - docW,
445
+ y: darkBandY + 14,
446
+ size: docSize,
447
+ font: helvBold,
448
+ color: docColor
449
+ });
450
+ if (ctx.dateStamp) {
451
+ const dateColor = themeMode === "dark" ? rgb3(0.7, 0.7, 0.7) : rgb3(0.45, 0.45, 0.45);
452
+ const dateSize = 9;
453
+ const dateW = helv.widthOfTextAtSize(ctx.dateStamp, dateSize);
454
+ page.drawText(ctx.dateStamp, {
455
+ x: W - PAD - dateW,
456
+ y: darkBandY + 2,
457
+ size: dateSize,
458
+ font: helv,
459
+ color: dateColor
460
+ });
461
+ }
462
+ }
463
+ const stripY = headerTop + (CREAM_H - 9) / 2;
464
+ if (ctx.projectName) {
465
+ page.drawText(clip(ctx.projectName, 70), {
466
+ x: PAD,
467
+ y: stripY,
468
+ size: 9,
469
+ font: helvBold,
470
+ color: rgb3(0.4, 0.4, 0.4)
471
+ });
472
+ }
473
+ if (ctx.docType) {
474
+ const dtW = helv.widthOfTextAtSize(ctx.docType, 8);
475
+ page.drawText(ctx.docType, {
476
+ x: W - PAD - dtW,
477
+ y: stripY + 0.5,
478
+ size: 8,
479
+ font: helv,
480
+ color: rgb3(0.45, 0.45, 0.45)
481
+ });
482
+ }
483
+ }
348
484
 
349
485
  // src/pdf-lib/footer.ts
350
486
  import { PDFArray, PDFName, PDFNumber, PDFRef, rgb as rgb4 } from "pdf-lib";
@@ -431,6 +567,7 @@ export {
431
567
  BRANDED_FOOTER_HEIGHT,
432
568
  BRANDED_HEADER_HEIGHT,
433
569
  BRANDED_HEADER_MINIMAL_H,
570
+ BRANDED_HEADER_PROMINENT_H,
434
571
  drawBrandedCover,
435
572
  drawBrandedFooter,
436
573
  drawBrandedHeader,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pdf-lib/fonts.ts","../src/pdf-lib/assets.ts","../src/brand.ts","../src/pdf-lib/cover.ts","../src/pdf-lib/header.ts","../src/pdf-lib/footer.ts"],"sourcesContent":["import type { PDFDocument, PDFFont } from \"pdf-lib\";\nimport { StandardFonts } from \"pdf-lib\";\n\nexport interface BookendFonts {\n helv: PDFFont;\n helvBold: PDFFont;\n helvOblique: PDFFont;\n}\n\nexport async function loadBookendFonts(doc: PDFDocument): Promise<BookendFonts> {\n const [helv, helvBold, helvOblique] = await Promise.all([\n doc.embedFont(StandardFonts.Helvetica),\n doc.embedFont(StandardFonts.HelveticaBold),\n doc.embedFont(StandardFonts.HelveticaOblique),\n ]);\n return { helv, helvBold, helvOblique };\n}\n","import type { PDFDocument, PDFImage, RGB } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport { darkenHex, hexToRgb01, resolveBandThemeMode, resolveBestLogoUrl } from \"../brand\";\nimport type { BrandInput } from \"../types\";\n\nexport interface ResolvedBookendAssets {\n logoImage: PDFImage | null;\n logoAspect: number;\n bandColor: RGB;\n accentColor: RGB;\n cardColors: {\n cream: RGB;\n cardBorder: RGB;\n chipBg: RGB;\n };\n themeMode: \"dark\" | \"light\";\n}\n\nexport interface LoadBookendAssetsOptions {\n brand: BrandInput;\n doc: PDFDocument;\n /**\n * Optional URL resolver. Use when your app needs to convert relative paths\n * (e.g. /demo-files/logo.png) into absolute URLs for server-side fetching.\n * Defaults to identity (returns the URL unchanged).\n */\n resolveUrl?: (url: string) => string;\n /** Optional fetch timeout in ms. Default 8s. */\n fetchTimeoutMs?: number;\n}\n\n/**\n * Resolve brand colors + load logo PDFImage from URL.\n * Mirrors RepFirm's loadTenantBookendAssets behavior.\n */\nexport async function loadBookendAssets(opts: LoadBookendAssetsOptions): Promise<ResolvedBookendAssets> {\n const { brand, doc, resolveUrl = (u) => u, fetchTimeoutMs = 8_000 } = opts;\n const themeMode = resolveBandThemeMode(brand);\n const themeVars = brand.themeVars ?? {};\n\n // Band color\n let bandColorHex: string;\n if (themeMode === \"dark\") {\n bandColorHex =\n themeVars[\"--sidebar-bg\"] ??\n themeVars[\"--header-bg\"] ??\n (brand.primaryColor ? darkenHex(brand.primaryColor, 0.45) : \"#1e3a5f\");\n } else {\n bandColorHex = \"#fbfaf6\";\n }\n\n const accentHex = brand.primaryColor ?? themeVars[\"--accent\"] ?? \"#1e3a5f\";\n\n const [br, bg2, bb] = hexToRgb01(bandColorHex);\n const [ar, ag, ab] = hexToRgb01(accentHex);\n\n const cardColors = {\n cream: rgb(0.984, 0.98, 0.965),\n cardBorder: rgb(0.78, 0.78, 0.78),\n chipBg: rgb(0.937, 0.937, 0.933),\n };\n\n // Logo URL resolution\n const candidate = resolveBestLogoUrl(brand, themeMode);\n const logoUrl = candidate ? resolveUrl(candidate) : null;\n\n let logoImage: PDFImage | null = null;\n let logoAspect = 1;\n\n if (logoUrl && !logoUrl.toLowerCase().endsWith(\".svg\")) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), fetchTimeoutMs);\n let res: Response;\n try {\n res = await fetch(logoUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n if (res.ok) {\n const buf = new Uint8Array(await res.arrayBuffer());\n const lower = logoUrl.toLowerCase();\n if (lower.includes(\".png\") || (res.headers.get(\"content-type\") ?? \"\").includes(\"png\")) {\n logoImage = await doc.embedPng(buf);\n } else {\n logoImage = await doc.embedJpg(buf);\n }\n const dims = logoImage.scale(1);\n logoAspect = dims.width / dims.height;\n }\n } catch {\n // fall back to text-only header\n }\n }\n\n return {\n logoImage,\n logoAspect,\n bandColor: rgb(br, bg2, bb),\n accentColor: rgb(ar, ag, ab),\n cardColors,\n themeMode,\n };\n}\n","import type { BrandInput } from \"./types\";\n\n/**\n * Adapter: RepFirm TenantBranding shape -> BrandInput.\n *\n * RepFirm tenants store branding on the `tenants.branding` JSONB column with\n * keys like primary_color, logo_url, logo_dark_url, etc. (snake_case). Pair\n * with the tenant name from the parent row when calling.\n */\nexport interface TenantBrandingShape {\n primary_color?: string;\n logo_url?: string | null;\n logo_wordmark_url?: string | null;\n logo_mark_url?: string | null;\n logo_dark_url?: string | null;\n logo_light_url?: string | null;\n pdf_header_theme?: \"dark\" | \"light\" | \"auto\";\n theme_vars?: Record<string, string>;\n tagline?: string | null;\n company_address?: string | null;\n company_phone?: string | null;\n company_email?: string | null;\n}\n\nexport function brandFromTenantBranding(\n tenantName: string,\n branding: TenantBrandingShape | null | undefined,\n fallbackPrimary = \"#1e3a5f\",\n): BrandInput {\n const b = branding ?? {};\n return {\n agencyName: tenantName,\n primaryColor: b.primary_color ?? fallbackPrimary,\n logoUrl: b.logo_url ?? null,\n logoDarkUrl: b.logo_dark_url ?? null,\n logoLightUrl: b.logo_light_url ?? null,\n logoWordmarkUrl: b.logo_wordmark_url ?? null,\n logoMarkUrl: b.logo_mark_url ?? null,\n pdfHeaderTheme: b.pdf_header_theme ?? \"auto\",\n themeVars: b.theme_vars ?? {},\n tagline: b.tagline ?? null,\n address: b.company_address ?? null,\n phone: b.company_phone ?? null,\n email: b.company_email ?? null,\n };\n}\n\n/**\n * Adapter: dygarn-dashboard prospect brand shape -> BrandInput.\n *\n * Dygarn-dashboard stores per-prospect brand kits in outreach_prospect_brands\n * with simpler shape (camelCase or snake — be liberal in what we accept).\n */\nexport interface ProspectBrandShape {\n agency_name?: string;\n agencyName?: string;\n legal_name?: string | null;\n legalName?: string | null;\n logo_url?: string | null;\n logoUrl?: string | null;\n primary_color?: string;\n primaryColor?: string;\n secondary_color?: string;\n secondaryColor?: string;\n address?: string | null;\n phone?: string | null;\n website?: string | null;\n}\n\nexport function brandFromProspectBrand(p: ProspectBrandShape): BrandInput {\n const agency = p.agencyName ?? p.agency_name ?? \"Agency\";\n return {\n agencyName: agency,\n legalName: p.legalName ?? p.legal_name ?? null,\n logoUrl: p.logoUrl ?? p.logo_url ?? null,\n primaryColor: p.primaryColor ?? p.primary_color ?? \"#1a73e8\",\n address: p.address ?? null,\n phone: p.phone ?? null,\n website: p.website ?? null,\n pdfHeaderTheme: \"auto\",\n };\n}\n\n/**\n * Determine whether to use a dark or light header band for this brand.\n *\n * - Explicit override: respect pdfHeaderTheme if set to 'dark' or 'light'.\n * - Auto (default): use dark theme only when a logoDarkUrl (white-on-dark\n * variant) is available, OR when the logo filename hints at a white/dark\n * variant. Otherwise default to LIGHT (safe for multi-color logos).\n */\nexport function resolveBandThemeMode(brand: BrandInput): \"dark\" | \"light\" {\n if (brand.pdfHeaderTheme === \"dark\") return \"dark\";\n if (brand.pdfHeaderTheme === \"light\") return \"light\";\n if (brand.logoDarkUrl) return \"dark\";\n const candidate = (brand.logoWordmarkUrl ?? brand.logoUrl ?? brand.logoMarkUrl ?? \"\").toLowerCase();\n if (candidate.includes(\"white\") || candidate.includes(\"-on-dark\") || candidate.includes(\"_dark\")) return \"dark\";\n return \"light\";\n}\n\n/**\n * Pick the best logo URL for the resolved theme mode.\n * Returns null if no logo available.\n */\nexport function resolveBestLogoUrl(brand: BrandInput, mode: \"dark\" | \"light\"): string | null {\n if (mode === \"dark\") {\n return brand.logoDarkUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? null;\n }\n return (\n brand.logoLightUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? brand.logoDarkUrl ?? null\n );\n}\n\n/**\n * Darken a hex color by a 0-1 amount. Used to derive a dark band color from\n * a brand primary when no explicit dark variant exists.\n */\nexport function darkenHex(hex: string, amount: number): string {\n const m = /^#?([0-9a-f]{6})$/i.exec(hex);\n if (!m) return hex;\n const n = Number.parseInt(m[1], 16);\n let r = (n >> 16) & 0xff;\n let g = (n >> 8) & 0xff;\n let b = n & 0xff;\n r = Math.max(0, Math.round(r * (1 - amount)));\n g = Math.max(0, Math.round(g * (1 - amount)));\n b = Math.max(0, Math.round(b * (1 - amount)));\n return `#${[r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/**\n * Convert hex to {r,g,b} normalized 0-1. Used by both React-PDF (color objects)\n * and pdf-lib (rgb() arguments).\n */\nexport function hexToRgb01(hex: string): [number, number, number] {\n const h = hex.replace(\"#\", \"\").trim();\n const r = Number.parseInt(h.slice(0, 2), 16) / 255;\n const g = Number.parseInt(h.slice(2, 4), 16) / 255;\n const b = Number.parseInt(h.slice(4, 6), 16) / 255;\n return [Number.isFinite(r) ? r : 0.12, Number.isFinite(g) ? g : 0.23, Number.isFinite(b) ? b : 0.37];\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\n/**\n * Draw a branded cover page. Logo centered on the band, doc type label,\n * project name, location, doc number, recipient, prepared-by signature block,\n * bottom metadata line.\n *\n * Designed for 612x792 (US Letter) page size. Caller is responsible for\n * adding the page to the document.\n */\nexport function drawBrandedCover(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold, helvOblique } = fonts;\n const { bandColor, accentColor, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const CX = W / 2;\n const agencyName = ctx.brand.agencyName;\n\n // Top branded band\n const COVER_HEADER_H = 130;\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H, width: W, height: COVER_HEADER_H, color: bandColor });\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H - 4, width: W, height: 4, color: accentColor });\n\n // Logo or name inside the band\n const COVER_LOGO_H = 70;\n const COVER_LOGO_W = Math.round(COVER_LOGO_H * (logoAspect || 1));\n if (logoImage) {\n page.drawImage(logoImage, {\n x: CX - COVER_LOGO_W / 2,\n y: H - COVER_HEADER_H + (COVER_HEADER_H - COVER_LOGO_H) / 2,\n width: COVER_LOGO_W,\n height: COVER_LOGO_H,\n });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n const nameW = helvBold.widthOfTextAtSize(agencyName, 28);\n page.drawText(agencyName, {\n x: CX - nameW / 2,\n y: H - COVER_HEADER_H / 2 - 10,\n size: 28,\n font: helvBold,\n color: nameColor,\n });\n }\n\n // Project / document block (centered, upper-middle)\n let cursorY = H - COVER_HEADER_H - 70;\n\n const docTypeUpper = ctx.docType.toUpperCase();\n const tagW = helvBold.widthOfTextAtSize(docTypeUpper, 11);\n page.drawText(docTypeUpper, {\n x: CX - tagW / 2,\n y: cursorY,\n size: 11,\n font: helvBold,\n color: rgb(0.45, 0.45, 0.45),\n });\n cursorY -= 30;\n\n if (ctx.projectName) {\n const projW = helvBold.widthOfTextAtSize(ctx.projectName, 22);\n page.drawText(ctx.projectName, {\n x: CX - projW / 2,\n y: cursorY,\n size: 22,\n font: helvBold,\n color: rgb(0.08, 0.08, 0.08),\n });\n cursorY -= 24;\n }\n\n if (ctx.location) {\n const locW = helv.widthOfTextAtSize(ctx.location, 12);\n page.drawText(ctx.location, {\n x: CX - locW / 2,\n y: cursorY,\n size: 12,\n font: helv,\n color: rgb(0.4, 0.4, 0.4),\n });\n cursorY -= 36;\n } else {\n cursorY -= 24;\n }\n\n if (ctx.docNumber) {\n const numLabel = ctx.docNumber;\n const numW = helvBold.widthOfTextAtSize(numLabel, 12);\n page.drawText(numLabel, {\n x: CX - numW / 2,\n y: cursorY,\n size: 12,\n font: helvBold,\n color: rgb(0.25, 0.25, 0.25),\n });\n cursorY -= 28;\n }\n\n const recipientName = ctx.recipientName ?? ctx.distributorName ?? null;\n const recipientLabel = ctx.recipientLabel ?? (ctx.distributorName ? \"DISTRIBUTOR\" : null);\n if (recipientName && recipientLabel) {\n const labelW = helvBold.widthOfTextAtSize(recipientLabel, 11);\n const gap = 14;\n const valueW = helvBold.widthOfTextAtSize(recipientName, 13);\n const totalW = labelW + gap + valueW;\n const rowX = CX - totalW / 2;\n page.drawText(recipientLabel, { x: rowX, y: cursorY, size: 11, font: helvBold, color: rgb(0.45, 0.45, 0.45) });\n page.drawText(recipientName, { x: rowX + labelW + gap, y: cursorY, size: 13, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n }\n\n // Bottom signature block\n const SIG_BLOCK_TOP = 140;\n let sigY = SIG_BLOCK_TOP;\n\n page.drawRectangle({ x: CX - 100, y: sigY, width: 200, height: 0.5, color: rgb(0.7, 0.7, 0.7) });\n sigY -= 18;\n\n const prepLabel = \"- Prepared By -\";\n const prepLabelW = helvOblique.widthOfTextAtSize(prepLabel, 10);\n page.drawText(prepLabel, { x: CX - prepLabelW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.5, 0.5, 0.5) });\n sigY -= 18;\n\n if (ctx.repName) {\n const repNameW = helvBold.widthOfTextAtSize(ctx.repName, 12);\n page.drawText(ctx.repName, { x: CX - repNameW / 2, y: sigY, size: 12, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n sigY -= 16;\n }\n if (ctx.repEmail) {\n const repEmailW = helv.widthOfTextAtSize(ctx.repEmail, 10);\n page.drawText(ctx.repEmail, { x: CX - repEmailW / 2, y: sigY, size: 10, font: helv, color: rgb(0.16, 0.42, 0.68) });\n sigY -= 14;\n }\n\n const tnW = helvOblique.widthOfTextAtSize(agencyName, 10);\n page.drawText(agencyName, { x: CX - tnW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.4, 0.4, 0.4) });\n\n // Bottom-most: doc number + date\n const footerLine = [ctx.docNumber, ctx.dateStamp].filter(Boolean).join(\" | \");\n if (footerLine) {\n const flW = helv.widthOfTextAtSize(footerLine, 9);\n page.drawText(footerLine, { x: CX - flW / 2, y: 36, size: 9, font: helv, color: rgb(0.55, 0.55, 0.55) });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_HEADER_HEIGHT = 110; // dark band + stripe + cream info zone\nexport const BRANDED_HEADER_MINIMAL_H = 56; // dark band + stripe + thin cream strip\n\nfunction clip(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, max - 1)}…`;\n}\n\n/**\n * Draw the per-page branded header.\n *\n * showFixtureCard=true (datasheet pages): dark band 38pt + stripe 3pt + cream\n * info zone with white card (Job Name / Manufacturer / Model Number) + type\n * chip on right. Use for submittal datasheet pages.\n *\n * showFixtureCard=false (default): dark band 38pt + stripe 3pt + thin cream\n * strip with project name + doc number. Use for non-datasheet pages.\n */\nexport function drawBrandedHeader(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n opts?: { showFixtureCard?: boolean },\n): void {\n const showFixtureCard = opts?.showFixtureCard ?? false;\n const { helv, helvBold } = fonts;\n const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const TOP_BANNER_H = showFixtureCard ? BRANDED_HEADER_HEIGHT : BRANDED_HEADER_MINIMAL_H;\n\n const PAD = 12;\n const DARK_H = 38;\n const STRIPE_H = 3;\n const CREAM_H = TOP_BANNER_H - DARK_H - STRIPE_H;\n\n const darkBandY = H - DARK_H;\n const stripeY = darkBandY - STRIPE_H;\n const headerTop = H - TOP_BANNER_H;\n\n page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });\n page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });\n page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: headerTop },\n end: { x: W, y: headerTop },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n if (logoImage) {\n const bandLogoH = 26;\n const logoW = Math.round(bandLogoH * logoAspect);\n page.drawImage(logoImage, { x: PAD, y: darkBandY + (DARK_H - bandLogoH) / 2, width: logoW, height: bandLogoH });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n page.drawText(ctx.brand.agencyName, { x: PAD, y: darkBandY + 12, size: 12, font: helvBold, color: nameColor });\n }\n\n if (showFixtureCard) {\n drawFixtureCard(page, ctx, fonts, W, TOP_BANNER_H, PAD, CREAM_H, headerTop);\n } else {\n drawSimpleInfoStrip(page, ctx, fonts, W, CREAM_H, headerTop, PAD);\n }\n}\n\nfunction drawFixtureCard(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n _TOP_BANNER_H: number,\n PAD: number,\n CREAM_H: number,\n headerTop: number,\n): void {\n const { helv, helvBold } = fonts;\n const { cardColors } = ctx;\n\n const CHIP_W_RESERVE = 60;\n const CARD_X = PAD * 2;\n const CARD_W = W - CARD_X - CHIP_W_RESERVE - PAD;\n const CARD_H = Math.min(CREAM_H - 8, 60);\n const CARD_Y = headerTop + (CREAM_H - CARD_H) / 2;\n\n page.drawRectangle({\n x: CARD_X,\n y: CARD_Y,\n width: CARD_W,\n height: CARD_H,\n borderColor: cardColors.cardBorder,\n borderWidth: 0.75,\n color: rgb(1, 1, 1),\n });\n\n const LABEL_X = CARD_X + 14;\n const VALUE_X = CARD_X + 100;\n const ROW1_Y = CARD_Y + CARD_H - 17;\n const ROW2_Y = ROW1_Y - 18;\n const ROW3_Y = ROW2_Y - 18;\n\n const jobName = (ctx.projectName || \"—\").toUpperCase();\n const rows: Array<[string, string]> = [\n [\"Job Name:\", clip(jobName, 48)],\n [\"Manufacturer:\", clip(ctx.fixtureMfr ?? \"—\", 44)],\n [\"Model Number:\", clip(ctx.fixturePartNumber ?? \"—\", 44)],\n ];\n const rowYs = [ROW1_Y, ROW2_Y, ROW3_Y];\n for (let i = 0; i < rows.length; i++) {\n const [label, value] = rows[i];\n const y = rowYs[i];\n page.drawText(label, { x: LABEL_X, y, size: 9, font: helvBold, color: rgb(0.27, 0.27, 0.27) });\n page.drawText(value, { x: VALUE_X, y, size: 10.5, font: helvBold, color: rgb(0.08, 0.08, 0.08) });\n }\n\n // Type chip\n const CHIP_W = 50;\n const CHIP_H = Math.min(CREAM_H - 8, 50);\n const CHIP_X = W - PAD - CHIP_W;\n const CHIP_Y = headerTop + (CREAM_H - CHIP_H) / 2;\n const typeCode = clip(ctx.fixtureType ?? \"—\", 4);\n\n page.drawRectangle({ x: CHIP_X, y: CHIP_Y, width: CHIP_W, height: CHIP_H, color: cardColors.chipBg });\n\n const typeLabelW = helv.widthOfTextAtSize(\"Type:\", 7);\n page.drawText(\"Type:\", {\n x: CHIP_X + (CHIP_W - typeLabelW) / 2,\n y: CHIP_Y + CHIP_H - 13,\n size: 7,\n font: helv,\n color: rgb(0.5, 0.5, 0.5),\n });\n\n const codeSize = typeCode.length <= 2 ? 22 : typeCode.length <= 3 ? 17 : 13;\n const codeW = helvBold.widthOfTextAtSize(typeCode, codeSize);\n page.drawText(typeCode, {\n x: CHIP_X + (CHIP_W - codeW) / 2,\n y: CHIP_Y + (CHIP_H - 13) / 2 - codeSize * 0.35,\n size: codeSize,\n font: helvBold,\n color: rgb(0.133, 0.133, 0.133),\n });\n}\n\nfunction drawSimpleInfoStrip(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n CREAM_H: number,\n headerTop: number,\n PAD: number,\n): void {\n const { helv, helvBold } = fonts;\n const stripY = headerTop + (CREAM_H - 9) / 2;\n\n if (ctx.projectName) {\n page.drawText(clip(ctx.projectName, 60), {\n x: PAD * 2,\n y: stripY,\n size: 9,\n font: helvBold,\n color: rgb(0.2, 0.2, 0.2),\n });\n }\n if (ctx.docNumber) {\n const numW = helv.widthOfTextAtSize(ctx.docNumber, 8);\n page.drawText(ctx.docNumber, { x: W - PAD - numW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n } else if (ctx.docType) {\n const dtW = helv.widthOfTextAtSize(ctx.docType, 8);\n page.drawText(ctx.docType, { x: W - PAD - dtW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { PDFArray, PDFName, PDFNumber, PDFRef, rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_FOOTER_HEIGHT = 38;\n\n/**\n * Per-page branded footer with prepared-by line, date, page numbers, and a\n * clickable \"Index\" link that jumps to page 1.\n */\nexport function drawBrandedFooter(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold } = fonts;\n const W = page.getWidth();\n const BOT_BANNER_H = BRANDED_FOOTER_HEIGHT;\n const PAD = 10;\n const darkGrey = rgb(0.25, 0.25, 0.25);\n const midGrey = rgb(0.55, 0.55, 0.55);\n\n page.drawRectangle({ x: 0, y: 0, width: W, height: BOT_BANNER_H, color: ctx.cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: BOT_BANNER_H },\n end: { x: W, y: BOT_BANNER_H },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n // TOP ROW\n const topRowY = BOT_BANNER_H - 13;\n page.drawText(\"Prepared By: \", { x: PAD, y: topRowY, size: 9, font: helv, color: darkGrey });\n const prepByW = helv.widthOfTextAtSize(\"Prepared By: \", 9);\n page.drawText(ctx.brand.agencyName, { x: PAD + prepByW, y: topRowY, size: 9, font: helvBold, color: darkGrey });\n\n const repParts: string[] = [];\n if (ctx.repName) repParts.push(ctx.repName);\n if (ctx.repEmail) repParts.push(ctx.repEmail);\n if (repParts.length > 0) {\n const repStr = repParts.join(\" | \");\n const repStrW = helv.widthOfTextAtSize(repStr, 9);\n page.drawText(repStr, { x: W - PAD - repStrW, y: topRowY, size: 9, font: helv, color: darkGrey });\n }\n\n // Thin divider\n const divY = Math.round(BOT_BANNER_H / 2) - 1;\n page.drawLine({\n start: { x: PAD, y: divY },\n end: { x: W - PAD, y: divY },\n thickness: 0.4,\n color: rgb(0.82, 0.82, 0.8),\n });\n\n // BOTTOM ROW\n const botRowY = 5;\n page.drawText(ctx.dateStamp, { x: PAD, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const pageStr = ctx.pageCount > 0 ? `${ctx.pageNumber} of ${ctx.pageCount}` : String(ctx.pageNumber);\n const pageStrW = helv.widthOfTextAtSize(pageStr, 8);\n page.drawText(pageStr, { x: (W - pageStrW) / 2, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const indexLabel = \"Index\";\n const indexW = helv.widthOfTextAtSize(indexLabel, 8);\n const indexX = W - PAD - indexW;\n page.drawText(indexLabel, { x: indexX, y: botRowY, size: 8, font: helv, color: ctx.accentColor });\n\n if (ctx.pageNumber > 1) {\n addGoToFirstPageLink(page, {\n x: indexX - 1,\n y: botRowY - 1,\n width: indexW + 2,\n height: 10,\n });\n }\n}\n\nfunction addGoToFirstPageLink(\n page: PDFPage,\n rect: { x: number; y: number; width: number; height: number },\n): void {\n const doc = page.doc;\n const pages = doc.getPages();\n if (pages.length === 0) return;\n const firstPageRef = doc.context.getObjectRef(pages[0].node);\n if (!firstPageRef) return;\n\n const linkDict = doc.context.obj({\n Type: \"Annot\",\n Subtype: \"Link\",\n Rect: [rect.x, rect.y, rect.x + rect.width, rect.y + rect.height],\n Border: [0, 0, 0],\n A: {\n Type: \"Action\",\n S: \"GoTo\",\n D: [firstPageRef, PDFName.of(\"Fit\")],\n },\n });\n const linkRef = doc.context.register(linkDict);\n\n const existing = page.node.Annots();\n if (existing instanceof PDFArray) {\n existing.push(linkRef);\n } else {\n page.node.set(PDFName.of(\"Annots\"), doc.context.obj([linkRef]));\n }\n\n void PDFRef;\n void PDFNumber;\n}\n"],"mappings":"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","names":["rgb","rgb","rgb"]}
1
+ {"version":3,"sources":["../src/pdf-lib/fonts.ts","../src/pdf-lib/assets.ts","../src/brand.ts","../src/pdf-lib/cover.ts","../src/pdf-lib/header.ts","../src/pdf-lib/footer.ts"],"sourcesContent":["import type { PDFDocument, PDFFont } from \"pdf-lib\";\nimport { StandardFonts } from \"pdf-lib\";\n\nexport interface BookendFonts {\n helv: PDFFont;\n helvBold: PDFFont;\n helvOblique: PDFFont;\n}\n\nexport async function loadBookendFonts(doc: PDFDocument): Promise<BookendFonts> {\n const [helv, helvBold, helvOblique] = await Promise.all([\n doc.embedFont(StandardFonts.Helvetica),\n doc.embedFont(StandardFonts.HelveticaBold),\n doc.embedFont(StandardFonts.HelveticaOblique),\n ]);\n return { helv, helvBold, helvOblique };\n}\n","import type { PDFDocument, PDFImage, RGB } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport { darkenHex, hexToRgb01, resolveBandThemeMode, resolveBestLogoUrl } from \"../brand\";\nimport type { BrandInput } from \"../types\";\n\nexport interface ResolvedBookendAssets {\n logoImage: PDFImage | null;\n logoAspect: number;\n bandColor: RGB;\n accentColor: RGB;\n cardColors: {\n cream: RGB;\n cardBorder: RGB;\n chipBg: RGB;\n };\n themeMode: \"dark\" | \"light\";\n}\n\nexport interface LoadBookendAssetsOptions {\n brand: BrandInput;\n doc: PDFDocument;\n /**\n * Optional URL resolver. Use when your app needs to convert relative paths\n * (e.g. /demo-files/logo.png) into absolute URLs for server-side fetching.\n * Defaults to identity (returns the URL unchanged).\n */\n resolveUrl?: (url: string) => string;\n /** Optional fetch timeout in ms. Default 8s. */\n fetchTimeoutMs?: number;\n}\n\n/**\n * Resolve brand colors + load logo PDFImage from URL.\n * Mirrors RepFirm's loadTenantBookendAssets behavior.\n */\nexport async function loadBookendAssets(opts: LoadBookendAssetsOptions): Promise<ResolvedBookendAssets> {\n const { brand, doc, resolveUrl = (u) => u, fetchTimeoutMs = 8_000 } = opts;\n const themeMode = resolveBandThemeMode(brand);\n const themeVars = brand.themeVars ?? {};\n\n // Band color\n let bandColorHex: string;\n if (themeMode === \"dark\") {\n bandColorHex =\n themeVars[\"--sidebar-bg\"] ??\n themeVars[\"--header-bg\"] ??\n (brand.primaryColor ? darkenHex(brand.primaryColor, 0.45) : \"#1e3a5f\");\n } else {\n bandColorHex = \"#fbfaf6\";\n }\n\n const accentHex = brand.primaryColor ?? themeVars[\"--accent\"] ?? \"#1e3a5f\";\n\n const [br, bg2, bb] = hexToRgb01(bandColorHex);\n const [ar, ag, ab] = hexToRgb01(accentHex);\n\n const cardColors = {\n cream: rgb(0.984, 0.98, 0.965),\n cardBorder: rgb(0.78, 0.78, 0.78),\n chipBg: rgb(0.937, 0.937, 0.933),\n };\n\n // Logo URL resolution\n const candidate = resolveBestLogoUrl(brand, themeMode);\n const logoUrl = candidate ? resolveUrl(candidate) : null;\n\n let logoImage: PDFImage | null = null;\n let logoAspect = 1;\n\n if (logoUrl && !logoUrl.toLowerCase().endsWith(\".svg\")) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), fetchTimeoutMs);\n let res: Response;\n try {\n res = await fetch(logoUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n if (res.ok) {\n const buf = new Uint8Array(await res.arrayBuffer());\n const lower = logoUrl.toLowerCase();\n if (lower.includes(\".png\") || (res.headers.get(\"content-type\") ?? \"\").includes(\"png\")) {\n logoImage = await doc.embedPng(buf);\n } else {\n logoImage = await doc.embedJpg(buf);\n }\n const dims = logoImage.scale(1);\n logoAspect = dims.width / dims.height;\n }\n } catch {\n // fall back to text-only header\n }\n }\n\n return {\n logoImage,\n logoAspect,\n bandColor: rgb(br, bg2, bb),\n accentColor: rgb(ar, ag, ab),\n cardColors,\n themeMode,\n };\n}\n","import type { BrandInput } from \"./types\";\n\n/**\n * Adapter: RepFirm TenantBranding shape -> BrandInput.\n *\n * RepFirm tenants store branding on the `tenants.branding` JSONB column with\n * keys like primary_color, logo_url, logo_dark_url, etc. (snake_case). Pair\n * with the tenant name from the parent row when calling.\n */\nexport interface TenantBrandingShape {\n primary_color?: string;\n logo_url?: string | null;\n logo_wordmark_url?: string | null;\n logo_mark_url?: string | null;\n logo_dark_url?: string | null;\n logo_light_url?: string | null;\n pdf_header_theme?: \"dark\" | \"light\" | \"auto\";\n theme_vars?: Record<string, string>;\n tagline?: string | null;\n company_address?: string | null;\n company_phone?: string | null;\n company_email?: string | null;\n}\n\nexport function brandFromTenantBranding(\n tenantName: string,\n branding: TenantBrandingShape | null | undefined,\n fallbackPrimary = \"#1e3a5f\",\n): BrandInput {\n const b = branding ?? {};\n return {\n agencyName: tenantName,\n primaryColor: b.primary_color ?? fallbackPrimary,\n logoUrl: b.logo_url ?? null,\n logoDarkUrl: b.logo_dark_url ?? null,\n logoLightUrl: b.logo_light_url ?? null,\n logoWordmarkUrl: b.logo_wordmark_url ?? null,\n logoMarkUrl: b.logo_mark_url ?? null,\n pdfHeaderTheme: b.pdf_header_theme ?? \"auto\",\n themeVars: b.theme_vars ?? {},\n tagline: b.tagline ?? null,\n address: b.company_address ?? null,\n phone: b.company_phone ?? null,\n email: b.company_email ?? null,\n };\n}\n\n/**\n * Adapter: dygarn-dashboard prospect brand shape -> BrandInput.\n *\n * Dygarn-dashboard stores per-prospect brand kits in outreach_prospect_brands\n * with simpler shape (camelCase or snake — be liberal in what we accept).\n */\nexport interface ProspectBrandShape {\n agency_name?: string;\n agencyName?: string;\n legal_name?: string | null;\n legalName?: string | null;\n logo_url?: string | null;\n logoUrl?: string | null;\n primary_color?: string;\n primaryColor?: string;\n secondary_color?: string;\n secondaryColor?: string;\n address?: string | null;\n phone?: string | null;\n website?: string | null;\n}\n\nexport function brandFromProspectBrand(p: ProspectBrandShape): BrandInput {\n const agency = p.agencyName ?? p.agency_name ?? \"Agency\";\n return {\n agencyName: agency,\n legalName: p.legalName ?? p.legal_name ?? null,\n logoUrl: p.logoUrl ?? p.logo_url ?? null,\n primaryColor: p.primaryColor ?? p.primary_color ?? \"#1a73e8\",\n address: p.address ?? null,\n phone: p.phone ?? null,\n website: p.website ?? null,\n pdfHeaderTheme: \"auto\",\n };\n}\n\n/**\n * Determine whether to use a dark or light header band for this brand.\n *\n * - Explicit override: respect pdfHeaderTheme if set to 'dark' or 'light'.\n * - Auto (default): use dark theme only when a logoDarkUrl (white-on-dark\n * variant) is available, OR when the logo filename hints at a white/dark\n * variant. Otherwise default to LIGHT (safe for multi-color logos).\n */\nexport function resolveBandThemeMode(brand: BrandInput): \"dark\" | \"light\" {\n if (brand.pdfHeaderTheme === \"dark\") return \"dark\";\n if (brand.pdfHeaderTheme === \"light\") return \"light\";\n if (brand.logoDarkUrl) return \"dark\";\n const candidate = (brand.logoWordmarkUrl ?? brand.logoUrl ?? brand.logoMarkUrl ?? \"\").toLowerCase();\n if (candidate.includes(\"white\") || candidate.includes(\"-on-dark\") || candidate.includes(\"_dark\")) return \"dark\";\n return \"light\";\n}\n\n/**\n * Pick the best logo URL for the resolved theme mode.\n * Returns null if no logo available.\n */\nexport function resolveBestLogoUrl(brand: BrandInput, mode: \"dark\" | \"light\"): string | null {\n if (mode === \"dark\") {\n return brand.logoDarkUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? null;\n }\n return (\n brand.logoLightUrl ?? brand.logoUrl ?? brand.logoWordmarkUrl ?? brand.logoMarkUrl ?? brand.logoDarkUrl ?? null\n );\n}\n\n/**\n * Darken a hex color by a 0-1 amount. Used to derive a dark band color from\n * a brand primary when no explicit dark variant exists.\n */\nexport function darkenHex(hex: string, amount: number): string {\n const m = /^#?([0-9a-f]{6})$/i.exec(hex);\n if (!m) return hex;\n const n = Number.parseInt(m[1], 16);\n let r = (n >> 16) & 0xff;\n let g = (n >> 8) & 0xff;\n let b = n & 0xff;\n r = Math.max(0, Math.round(r * (1 - amount)));\n g = Math.max(0, Math.round(g * (1 - amount)));\n b = Math.max(0, Math.round(b * (1 - amount)));\n return `#${[r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/**\n * Convert hex to {r,g,b} normalized 0-1. Used by both React-PDF (color objects)\n * and pdf-lib (rgb() arguments).\n */\nexport function hexToRgb01(hex: string): [number, number, number] {\n const h = hex.replace(\"#\", \"\").trim();\n const r = Number.parseInt(h.slice(0, 2), 16) / 255;\n const g = Number.parseInt(h.slice(2, 4), 16) / 255;\n const b = Number.parseInt(h.slice(4, 6), 16) / 255;\n return [Number.isFinite(r) ? r : 0.12, Number.isFinite(g) ? g : 0.23, Number.isFinite(b) ? b : 0.37];\n}\n","import type { PDFFont, PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\n/**\n * Greedy word-wrap by measured width. Words longer than maxWidth are emitted\n * on their own line (they will visually overflow but at least won't crash the\n * layout). Returns at least one line.\n */\nfunction wrapByWidth(text: string, font: PDFFont, size: number, maxWidth: number): string[] {\n const out: string[] = [];\n const words = text.split(/\\s+/).filter(Boolean);\n let line = \"\";\n for (const w of words) {\n const candidate = line ? `${line} ${w}` : w;\n if (font.widthOfTextAtSize(candidate, size) <= maxWidth) {\n line = candidate;\n } else {\n if (line) out.push(line);\n line = w;\n }\n }\n if (line) out.push(line);\n return out.length > 0 ? out : [text];\n}\n\n/**\n * Draw a branded cover page. Logo centered on the band, doc type label,\n * project name, location, doc number, recipient, prepared-by signature block,\n * bottom metadata line.\n *\n * Designed for 612x792 (US Letter) page size. Caller is responsible for\n * adding the page to the document.\n */\nexport function drawBrandedCover(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold, helvOblique } = fonts;\n const { bandColor, accentColor, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const CX = W / 2;\n const agencyName = ctx.brand.agencyName;\n\n // Top branded band\n const COVER_HEADER_H = 130;\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H, width: W, height: COVER_HEADER_H, color: bandColor });\n page.drawRectangle({ x: 0, y: H - COVER_HEADER_H - 4, width: W, height: 4, color: accentColor });\n\n // Logo or name inside the band\n const COVER_LOGO_H = 70;\n const COVER_LOGO_W = Math.round(COVER_LOGO_H * (logoAspect || 1));\n if (logoImage) {\n page.drawImage(logoImage, {\n x: CX - COVER_LOGO_W / 2,\n y: H - COVER_HEADER_H + (COVER_HEADER_H - COVER_LOGO_H) / 2,\n width: COVER_LOGO_W,\n height: COVER_LOGO_H,\n });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n const nameW = helvBold.widthOfTextAtSize(agencyName, 28);\n page.drawText(agencyName, {\n x: CX - nameW / 2,\n y: H - COVER_HEADER_H / 2 - 10,\n size: 28,\n font: helvBold,\n color: nameColor,\n });\n }\n\n // Project / document block (centered, upper-middle)\n let cursorY = H - COVER_HEADER_H - 70;\n\n const docTypeUpper = ctx.docType.toUpperCase();\n const tagW = helvBold.widthOfTextAtSize(docTypeUpper, 11);\n page.drawText(docTypeUpper, {\n x: CX - tagW / 2,\n y: cursorY,\n size: 11,\n font: helvBold,\n color: rgb(0.45, 0.45, 0.45),\n });\n cursorY -= 30;\n\n if (ctx.projectName) {\n // Available column for the project name title. Leave a small margin off\n // each edge so the text never butts against the page border.\n const PROJ_MARGIN = 36;\n const PROJ_MAX_W = W - PROJ_MARGIN * 2;\n const PROJ_MAX_SIZE = 22;\n const PROJ_MIN_SIZE = 14;\n\n // 1) Try shrinking from MAX down to MIN, single line, until it fits.\n let projSize = PROJ_MAX_SIZE;\n while (projSize > PROJ_MIN_SIZE && helvBold.widthOfTextAtSize(ctx.projectName, projSize) > PROJ_MAX_W) {\n projSize -= 1;\n }\n\n if (helvBold.widthOfTextAtSize(ctx.projectName, projSize) <= PROJ_MAX_W) {\n // Single line fits at this size.\n const projW = helvBold.widthOfTextAtSize(ctx.projectName, projSize);\n page.drawText(ctx.projectName, {\n x: CX - projW / 2,\n y: cursorY,\n size: projSize,\n font: helvBold,\n color: rgb(0.08, 0.08, 0.08),\n });\n cursorY -= projSize + 2;\n } else {\n // 2) Still too long at min size. Word-wrap to 2+ lines at min size.\n const lines = wrapByWidth(ctx.projectName, helvBold, PROJ_MIN_SIZE, PROJ_MAX_W);\n for (const line of lines) {\n const lineW = helvBold.widthOfTextAtSize(line, PROJ_MIN_SIZE);\n page.drawText(line, {\n x: CX - lineW / 2,\n y: cursorY,\n size: PROJ_MIN_SIZE,\n font: helvBold,\n color: rgb(0.08, 0.08, 0.08),\n });\n cursorY -= PROJ_MIN_SIZE + 2;\n }\n // Add the trailing gap the original layout expected.\n cursorY -= 2;\n }\n }\n\n if (ctx.location) {\n const locW = helv.widthOfTextAtSize(ctx.location, 12);\n page.drawText(ctx.location, {\n x: CX - locW / 2,\n y: cursorY,\n size: 12,\n font: helv,\n color: rgb(0.4, 0.4, 0.4),\n });\n cursorY -= 36;\n } else {\n cursorY -= 24;\n }\n\n if (ctx.docNumber) {\n const numLabel = ctx.docNumber;\n const numW = helvBold.widthOfTextAtSize(numLabel, 12);\n page.drawText(numLabel, {\n x: CX - numW / 2,\n y: cursorY,\n size: 12,\n font: helvBold,\n color: rgb(0.25, 0.25, 0.25),\n });\n cursorY -= 28;\n }\n\n const recipientName = ctx.recipientName ?? ctx.distributorName ?? null;\n const recipientLabel = ctx.recipientLabel ?? (ctx.distributorName ? \"DISTRIBUTOR\" : null);\n if (recipientName && recipientLabel) {\n const labelW = helvBold.widthOfTextAtSize(recipientLabel, 11);\n const gap = 14;\n const valueW = helvBold.widthOfTextAtSize(recipientName, 13);\n const totalW = labelW + gap + valueW;\n const rowX = CX - totalW / 2;\n page.drawText(recipientLabel, { x: rowX, y: cursorY, size: 11, font: helvBold, color: rgb(0.45, 0.45, 0.45) });\n page.drawText(recipientName, { x: rowX + labelW + gap, y: cursorY, size: 13, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n }\n\n // Bottom signature block\n const SIG_BLOCK_TOP = 140;\n let sigY = SIG_BLOCK_TOP;\n\n page.drawRectangle({ x: CX - 100, y: sigY, width: 200, height: 0.5, color: rgb(0.7, 0.7, 0.7) });\n sigY -= 18;\n\n const prepLabel = \"- Prepared By -\";\n const prepLabelW = helvOblique.widthOfTextAtSize(prepLabel, 10);\n page.drawText(prepLabel, { x: CX - prepLabelW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.5, 0.5, 0.5) });\n sigY -= 18;\n\n if (ctx.repName) {\n const repNameW = helvBold.widthOfTextAtSize(ctx.repName, 12);\n page.drawText(ctx.repName, { x: CX - repNameW / 2, y: sigY, size: 12, font: helvBold, color: rgb(0.1, 0.1, 0.1) });\n sigY -= 16;\n }\n if (ctx.repEmail) {\n const repEmailW = helv.widthOfTextAtSize(ctx.repEmail, 10);\n page.drawText(ctx.repEmail, { x: CX - repEmailW / 2, y: sigY, size: 10, font: helv, color: rgb(0.16, 0.42, 0.68) });\n sigY -= 14;\n }\n\n const tnW = helvOblique.widthOfTextAtSize(agencyName, 10);\n page.drawText(agencyName, { x: CX - tnW / 2, y: sigY, size: 10, font: helvOblique, color: rgb(0.4, 0.4, 0.4) });\n\n // Bottom-most: doc number + date\n const footerLine = [ctx.docNumber, ctx.dateStamp].filter(Boolean).join(\" | \");\n if (footerLine) {\n const flW = helv.widthOfTextAtSize(footerLine, 9);\n page.drawText(footerLine, { x: CX - flW / 2, y: 36, size: 9, font: helv, color: rgb(0.55, 0.55, 0.55) });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_HEADER_HEIGHT = 110; // dark band + stripe + cream info zone\nexport const BRANDED_HEADER_MINIMAL_H = 56; // dark band + stripe + thin cream strip\nexport const BRANDED_HEADER_PROMINENT_H = 180; // tall band for quote-style covers (bigger logo)\n\nfunction clip(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, max - 1)}…`;\n}\n\n/**\n * Draw the per-page branded header.\n *\n * showFixtureCard=true (datasheet pages): dark band 38pt + stripe 3pt + cream\n * info zone with white card (Job Name / Manufacturer / Model Number) + type\n * chip on right. Use for submittal datasheet pages.\n *\n * showFixtureCard=false (default): dark band 38pt + stripe 3pt + thin cream\n * strip with project name + doc number. Use for non-datasheet pages.\n */\nexport function drawBrandedHeader(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n opts?: { showFixtureCard?: boolean; prominent?: boolean },\n): void {\n const showFixtureCard = opts?.showFixtureCard ?? false;\n const prominent = opts?.prominent ?? false;\n if (prominent) {\n drawProminentHeader(page, ctx, fonts);\n return;\n }\n const { helv, helvBold } = fonts;\n const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const TOP_BANNER_H = showFixtureCard ? BRANDED_HEADER_HEIGHT : BRANDED_HEADER_MINIMAL_H;\n\n const PAD = 12;\n const DARK_H = 38;\n const STRIPE_H = 3;\n const CREAM_H = TOP_BANNER_H - DARK_H - STRIPE_H;\n\n const darkBandY = H - DARK_H;\n const stripeY = darkBandY - STRIPE_H;\n const headerTop = H - TOP_BANNER_H;\n\n page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });\n page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });\n page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: headerTop },\n end: { x: W, y: headerTop },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n if (logoImage) {\n const bandLogoH = 26;\n const logoW = Math.round(bandLogoH * logoAspect);\n page.drawImage(logoImage, { x: PAD, y: darkBandY + (DARK_H - bandLogoH) / 2, width: logoW, height: bandLogoH });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n page.drawText(ctx.brand.agencyName, { x: PAD, y: darkBandY + 12, size: 12, font: helvBold, color: nameColor });\n }\n\n if (showFixtureCard) {\n drawFixtureCard(page, ctx, fonts, W, TOP_BANNER_H, PAD, CREAM_H, headerTop);\n } else {\n drawSimpleInfoStrip(page, ctx, fonts, W, CREAM_H, headerTop, PAD);\n }\n}\n\nfunction drawFixtureCard(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n _TOP_BANNER_H: number,\n PAD: number,\n CREAM_H: number,\n headerTop: number,\n): void {\n const { helv, helvBold } = fonts;\n const { cardColors } = ctx;\n\n const CHIP_W_RESERVE = 60;\n const CARD_X = PAD * 2;\n const CARD_W = W - CARD_X - CHIP_W_RESERVE - PAD;\n const CARD_H = Math.min(CREAM_H - 8, 60);\n const CARD_Y = headerTop + (CREAM_H - CARD_H) / 2;\n\n page.drawRectangle({\n x: CARD_X,\n y: CARD_Y,\n width: CARD_W,\n height: CARD_H,\n borderColor: cardColors.cardBorder,\n borderWidth: 0.75,\n color: rgb(1, 1, 1),\n });\n\n const LABEL_X = CARD_X + 14;\n const VALUE_X = CARD_X + 100;\n const ROW1_Y = CARD_Y + CARD_H - 17;\n const ROW2_Y = ROW1_Y - 18;\n const ROW3_Y = ROW2_Y - 18;\n\n const jobName = (ctx.projectName || \"—\").toUpperCase();\n const rows: Array<[string, string]> = [\n [\"Job Name:\", clip(jobName, 48)],\n [\"Manufacturer:\", clip(ctx.fixtureMfr ?? \"—\", 44)],\n [\"Model Number:\", clip(ctx.fixturePartNumber ?? \"—\", 44)],\n ];\n const rowYs = [ROW1_Y, ROW2_Y, ROW3_Y];\n for (let i = 0; i < rows.length; i++) {\n const [label, value] = rows[i];\n const y = rowYs[i];\n page.drawText(label, { x: LABEL_X, y, size: 9, font: helvBold, color: rgb(0.27, 0.27, 0.27) });\n page.drawText(value, { x: VALUE_X, y, size: 10.5, font: helvBold, color: rgb(0.08, 0.08, 0.08) });\n }\n\n // Type chip\n const CHIP_W = 50;\n const CHIP_H = Math.min(CREAM_H - 8, 50);\n const CHIP_X = W - PAD - CHIP_W;\n const CHIP_Y = headerTop + (CREAM_H - CHIP_H) / 2;\n const typeCode = clip(ctx.fixtureType ?? \"—\", 4);\n\n page.drawRectangle({ x: CHIP_X, y: CHIP_Y, width: CHIP_W, height: CHIP_H, color: cardColors.chipBg });\n\n const typeLabelW = helv.widthOfTextAtSize(\"Type:\", 7);\n page.drawText(\"Type:\", {\n x: CHIP_X + (CHIP_W - typeLabelW) / 2,\n y: CHIP_Y + CHIP_H - 13,\n size: 7,\n font: helv,\n color: rgb(0.5, 0.5, 0.5),\n });\n\n const codeSize = typeCode.length <= 2 ? 22 : typeCode.length <= 3 ? 17 : 13;\n const codeW = helvBold.widthOfTextAtSize(typeCode, codeSize);\n page.drawText(typeCode, {\n x: CHIP_X + (CHIP_W - codeW) / 2,\n y: CHIP_Y + (CHIP_H - 13) / 2 - codeSize * 0.35,\n size: codeSize,\n font: helvBold,\n color: rgb(0.133, 0.133, 0.133),\n });\n}\n\nfunction drawSimpleInfoStrip(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n W: number,\n CREAM_H: number,\n headerTop: number,\n PAD: number,\n): void {\n const { helv, helvBold } = fonts;\n const stripY = headerTop + (CREAM_H - 9) / 2;\n\n if (ctx.projectName) {\n page.drawText(clip(ctx.projectName, 60), {\n x: PAD * 2,\n y: stripY,\n size: 9,\n font: helvBold,\n color: rgb(0.2, 0.2, 0.2),\n });\n }\n if (ctx.docNumber) {\n const numW = helv.widthOfTextAtSize(ctx.docNumber, 8);\n page.drawText(ctx.docNumber, { x: W - PAD - numW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n } else if (ctx.docType) {\n const dtW = helv.widthOfTextAtSize(ctx.docType, 8);\n page.drawText(ctx.docType, { x: W - PAD - dtW, y: stripY, size: 8, font: helv, color: rgb(0.4, 0.4, 0.4) });\n }\n}\n\n\nfunction drawProminentHeader(\n page: PDFPage,\n ctx: BookendContext,\n fonts: BookendFonts,\n): void {\n const { helv, helvBold } = fonts;\n const { bandColor, accentColor, cardColors, logoImage, logoAspect, themeMode } = ctx;\n const W = page.getWidth();\n const H = page.getHeight();\n const TOP_BANNER_H = BRANDED_HEADER_PROMINENT_H;\n\n const PAD = 20;\n const STRIPE_H = 3;\n const CREAM_H = 28; // thin cream strip at bottom of band for meta info\n const DARK_H = TOP_BANNER_H - STRIPE_H - CREAM_H;\n\n const darkBandY = H - DARK_H;\n const stripeY = darkBandY - STRIPE_H;\n const headerTop = H - TOP_BANNER_H;\n\n page.drawRectangle({ x: 0, y: darkBandY, width: W, height: DARK_H, color: bandColor });\n page.drawRectangle({ x: 0, y: stripeY, width: W, height: STRIPE_H, color: accentColor });\n page.drawRectangle({ x: 0, y: headerTop, width: W, height: CREAM_H, color: cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: headerTop },\n end: { x: W, y: headerTop },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n if (logoImage) {\n const LOGO_MAX_H = 120;\n const LOGO_MAX_W = Math.min(W - PAD * 2, 320);\n let logoH = LOGO_MAX_H;\n let logoW = logoH * logoAspect;\n if (logoW > LOGO_MAX_W) {\n logoW = LOGO_MAX_W;\n logoH = logoW / logoAspect;\n }\n const logoX = PAD;\n const logoY = darkBandY + (DARK_H - logoH) / 2;\n page.drawImage(logoImage, { x: logoX, y: logoY, width: logoW, height: logoH });\n } else {\n const nameColor = themeMode === \"dark\" ? rgb(1, 1, 1) : rgb(0.08, 0.08, 0.08);\n const nameSize = 28;\n page.drawText(ctx.brand.agencyName, {\n x: PAD,\n y: darkBandY + (DARK_H - nameSize) / 2,\n size: nameSize,\n font: helvBold,\n color: nameColor,\n });\n }\n\n // Doc number bottom-right of dark band\n if (ctx.docNumber) {\n const docColor = themeMode === \"dark\" ? rgb(0.9, 0.9, 0.9) : rgb(0.15, 0.15, 0.15);\n const docSize = 12;\n const docW = helvBold.widthOfTextAtSize(ctx.docNumber, docSize);\n page.drawText(ctx.docNumber, {\n x: W - PAD - docW,\n y: darkBandY + 14,\n size: docSize,\n font: helvBold,\n color: docColor,\n });\n if (ctx.dateStamp) {\n const dateColor = themeMode === \"dark\" ? rgb(0.7, 0.7, 0.7) : rgb(0.45, 0.45, 0.45);\n const dateSize = 9;\n const dateW = helv.widthOfTextAtSize(ctx.dateStamp, dateSize);\n page.drawText(ctx.dateStamp, {\n x: W - PAD - dateW,\n y: darkBandY + 2,\n size: dateSize,\n font: helv,\n color: dateColor,\n });\n }\n }\n\n // Project name small + grey in cream strip\n const stripY = headerTop + (CREAM_H - 9) / 2;\n if (ctx.projectName) {\n page.drawText(clip(ctx.projectName, 70), {\n x: PAD,\n y: stripY,\n size: 9,\n font: helvBold,\n color: rgb(0.4, 0.4, 0.4),\n });\n }\n if (ctx.docType) {\n const dtW = helv.widthOfTextAtSize(ctx.docType, 8);\n page.drawText(ctx.docType, {\n x: W - PAD - dtW,\n y: stripY + 0.5,\n size: 8,\n font: helv,\n color: rgb(0.45, 0.45, 0.45),\n });\n }\n}\n","import type { PDFPage } from \"pdf-lib\";\nimport { PDFArray, PDFName, PDFNumber, PDFRef, rgb } from \"pdf-lib\";\n\nimport type { BookendContext } from \"./context\";\nimport type { BookendFonts } from \"./fonts\";\n\nexport const BRANDED_FOOTER_HEIGHT = 38;\n\n/**\n * Per-page branded footer with prepared-by line, date, page numbers, and a\n * clickable \"Index\" link that jumps to page 1.\n */\nexport function drawBrandedFooter(page: PDFPage, ctx: BookendContext, fonts: BookendFonts): void {\n const { helv, helvBold } = fonts;\n const W = page.getWidth();\n const BOT_BANNER_H = BRANDED_FOOTER_HEIGHT;\n const PAD = 10;\n const darkGrey = rgb(0.25, 0.25, 0.25);\n const midGrey = rgb(0.55, 0.55, 0.55);\n\n page.drawRectangle({ x: 0, y: 0, width: W, height: BOT_BANNER_H, color: ctx.cardColors.cream });\n\n page.drawLine({\n start: { x: 0, y: BOT_BANNER_H },\n end: { x: W, y: BOT_BANNER_H },\n thickness: 0.5,\n color: rgb(0.8, 0.8, 0.78),\n });\n\n // TOP ROW\n const topRowY = BOT_BANNER_H - 13;\n page.drawText(\"Prepared By: \", { x: PAD, y: topRowY, size: 9, font: helv, color: darkGrey });\n const prepByW = helv.widthOfTextAtSize(\"Prepared By: \", 9);\n page.drawText(ctx.brand.agencyName, { x: PAD + prepByW, y: topRowY, size: 9, font: helvBold, color: darkGrey });\n\n const repParts: string[] = [];\n if (ctx.repName) repParts.push(ctx.repName);\n if (ctx.repEmail) repParts.push(ctx.repEmail);\n if (repParts.length > 0) {\n const repStr = repParts.join(\" | \");\n const repStrW = helv.widthOfTextAtSize(repStr, 9);\n page.drawText(repStr, { x: W - PAD - repStrW, y: topRowY, size: 9, font: helv, color: darkGrey });\n }\n\n // Thin divider\n const divY = Math.round(BOT_BANNER_H / 2) - 1;\n page.drawLine({\n start: { x: PAD, y: divY },\n end: { x: W - PAD, y: divY },\n thickness: 0.4,\n color: rgb(0.82, 0.82, 0.8),\n });\n\n // BOTTOM ROW\n const botRowY = 5;\n page.drawText(ctx.dateStamp, { x: PAD, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const pageStr = ctx.pageCount > 0 ? `${ctx.pageNumber} of ${ctx.pageCount}` : String(ctx.pageNumber);\n const pageStrW = helv.widthOfTextAtSize(pageStr, 8);\n page.drawText(pageStr, { x: (W - pageStrW) / 2, y: botRowY, size: 8, font: helv, color: midGrey });\n\n const indexLabel = \"Index\";\n const indexW = helv.widthOfTextAtSize(indexLabel, 8);\n const indexX = W - PAD - indexW;\n page.drawText(indexLabel, { x: indexX, y: botRowY, size: 8, font: helv, color: ctx.accentColor });\n\n if (ctx.pageNumber > 1) {\n addGoToFirstPageLink(page, {\n x: indexX - 1,\n y: botRowY - 1,\n width: indexW + 2,\n height: 10,\n });\n }\n}\n\nfunction addGoToFirstPageLink(\n page: PDFPage,\n rect: { x: number; y: number; width: number; height: number },\n): void {\n const doc = page.doc;\n const pages = doc.getPages();\n if (pages.length === 0) return;\n const firstPageRef = doc.context.getObjectRef(pages[0].node);\n if (!firstPageRef) return;\n\n const linkDict = doc.context.obj({\n Type: \"Annot\",\n Subtype: \"Link\",\n Rect: [rect.x, rect.y, rect.x + rect.width, rect.y + rect.height],\n Border: [0, 0, 0],\n A: {\n Type: \"Action\",\n S: \"GoTo\",\n D: [firstPageRef, PDFName.of(\"Fit\")],\n },\n });\n const linkRef = doc.context.register(linkDict);\n\n const existing = page.node.Annots();\n if (existing instanceof PDFArray) {\n existing.push(linkRef);\n } else {\n page.node.set(PDFName.of(\"Annots\"), doc.context.obj([linkRef]));\n }\n\n void PDFRef;\n void PDFNumber;\n}\n"],"mappings":"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","names":["rgb","rgb","rgb"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmoney2000/dygarn-pdf-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Branded PDF design system for DyGarn ecosystem apps (RepFirm, dygarn-dashboard). Shared visual primitives + style themes for quote, submittal, PO, and outreach kits.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,