@gmoney2000/dygarn-pdf-kit 0.1.0 → 0.1.1
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 +49 -9
- package/dist/pdf-lib.cjs.map +1 -1
- package/dist/pdf-lib.js +49 -9
- package/dist/pdf-lib.js.map +1 -1
- package/package.json +1 -1
package/dist/pdf-lib.cjs
CHANGED
|
@@ -139,6 +139,22 @@ async function loadBookendAssets(opts) {
|
|
|
139
139
|
|
|
140
140
|
// src/pdf-lib/cover.ts
|
|
141
141
|
var import_pdf_lib3 = require("pdf-lib");
|
|
142
|
+
function wrapByWidth(text, font, size, maxWidth) {
|
|
143
|
+
const out = [];
|
|
144
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
145
|
+
let line = "";
|
|
146
|
+
for (const w of words) {
|
|
147
|
+
const candidate = line ? `${line} ${w}` : w;
|
|
148
|
+
if (font.widthOfTextAtSize(candidate, size) <= maxWidth) {
|
|
149
|
+
line = candidate;
|
|
150
|
+
} else {
|
|
151
|
+
if (line) out.push(line);
|
|
152
|
+
line = w;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (line) out.push(line);
|
|
156
|
+
return out.length > 0 ? out : [text];
|
|
157
|
+
}
|
|
142
158
|
function drawBrandedCover(page, ctx, fonts) {
|
|
143
159
|
const { helv, helvBold, helvOblique } = fonts;
|
|
144
160
|
const { bandColor, accentColor, logoImage, logoAspect, themeMode } = ctx;
|
|
@@ -181,15 +197,39 @@ function drawBrandedCover(page, ctx, fonts) {
|
|
|
181
197
|
});
|
|
182
198
|
cursorY -= 30;
|
|
183
199
|
if (ctx.projectName) {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
200
|
+
const PROJ_MARGIN = 36;
|
|
201
|
+
const PROJ_MAX_W = W - PROJ_MARGIN * 2;
|
|
202
|
+
const PROJ_MAX_SIZE = 22;
|
|
203
|
+
const PROJ_MIN_SIZE = 14;
|
|
204
|
+
let projSize = PROJ_MAX_SIZE;
|
|
205
|
+
while (projSize > PROJ_MIN_SIZE && helvBold.widthOfTextAtSize(ctx.projectName, projSize) > PROJ_MAX_W) {
|
|
206
|
+
projSize -= 1;
|
|
207
|
+
}
|
|
208
|
+
if (helvBold.widthOfTextAtSize(ctx.projectName, projSize) <= PROJ_MAX_W) {
|
|
209
|
+
const projW = helvBold.widthOfTextAtSize(ctx.projectName, projSize);
|
|
210
|
+
page.drawText(ctx.projectName, {
|
|
211
|
+
x: CX - projW / 2,
|
|
212
|
+
y: cursorY,
|
|
213
|
+
size: projSize,
|
|
214
|
+
font: helvBold,
|
|
215
|
+
color: (0, import_pdf_lib3.rgb)(0.08, 0.08, 0.08)
|
|
216
|
+
});
|
|
217
|
+
cursorY -= projSize + 2;
|
|
218
|
+
} else {
|
|
219
|
+
const lines = wrapByWidth(ctx.projectName, helvBold, PROJ_MIN_SIZE, PROJ_MAX_W);
|
|
220
|
+
for (const line of lines) {
|
|
221
|
+
const lineW = helvBold.widthOfTextAtSize(line, PROJ_MIN_SIZE);
|
|
222
|
+
page.drawText(line, {
|
|
223
|
+
x: CX - lineW / 2,
|
|
224
|
+
y: cursorY,
|
|
225
|
+
size: PROJ_MIN_SIZE,
|
|
226
|
+
font: helvBold,
|
|
227
|
+
color: (0, import_pdf_lib3.rgb)(0.08, 0.08, 0.08)
|
|
228
|
+
});
|
|
229
|
+
cursorY -= PROJ_MIN_SIZE + 2;
|
|
230
|
+
}
|
|
231
|
+
cursorY -= 2;
|
|
232
|
+
}
|
|
193
233
|
}
|
|
194
234
|
if (ctx.location) {
|
|
195
235
|
const locW = helv.widthOfTextAtSize(ctx.location, 12);
|
package/dist/pdf-lib.cjs.map
CHANGED
|
@@ -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 { 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 { 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\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"]}
|
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
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
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);
|
package/dist/pdf-lib.js.map
CHANGED
|
@@ -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\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmoney2000/dygarn-pdf-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
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,
|