import { o as __toESM } from "./chunk-ErZ26oRB.js";

//#region src/renderers/pdf.ts
/**
* PDF renderer — lazy-loads pdfmake on first use.
* pdfmake handles pagination, tables, text wrapping, and font embedding.
*
* @example
* ```ts
* import { render, Document, Page, Heading } from '@pyreon/document'
*
* const doc = Document({
*   title: 'Report',
*   children: Page({ children: Heading({ children: 'Hello' }) }),
* })
* const pdf = await render(doc, 'pdf') // → Uint8Array
* ```
*/
function resolveColumn(col) {
	return typeof col === "string" ? { header: col } : col;
}
function getTextContent(children) {
	return children.map((c) => typeof c === "string" ? c : getTextContent(c.children)).join("");
}
/** pdfmake expects page sizes as `{ width, height }` objects. */
const PAGE_SIZES = {
	A3: {
		width: 841.89,
		height: 1190.55
	},
	A4: {
		width: 595.28,
		height: 841.89
	},
	A5: {
		width: 419.53,
		height: 595.28
	},
	letter: {
		width: 612,
		height: 792
	},
	legal: {
		width: 612,
		height: 1008
	},
	tabloid: {
		width: 792,
		height: 1224
	}
};
/**
* Resolve an image `src` for pdfmake.
*
* - `data:` URIs are passed through directly (pdfmake supports base64).
* - `http(s)://` URLs cannot be resolved at render time in the browser.
*   A placeholder text node is returned instead.
* - Relative / absolute paths (e.g. `/logo.png`) cannot be resolved without
*   a server-side fetch, so they are skipped with a placeholder.
*/
function resolveImageSrc(src) {
	if (src.startsWith("data:")) return { image: src };
	if (src.startsWith("http://") || src.startsWith("https://")) return {
		text: `[Image: ${src}]`,
		italics: true,
		color: "#999999"
	};
	return {
		text: `[Image: ${src}]`,
		italics: true,
		color: "#999999"
	};
}
function nodeToContent(node) {
	const p = node.props;
	switch (node.type) {
		case "document":
		case "page": return node.children.map((c) => typeof c === "string" ? c : nodeToContent(c)).filter((c) => c != null);
		case "section": {
			const content = node.children.map((c) => typeof c === "string" ? c : nodeToContent(c)).filter((c) => c != null).flat();
			if (p.direction === "row") return {
				columns: node.children.filter((c) => typeof c !== "string").map((child) => ({
					stack: [nodeToContent(child)].flat().filter(Boolean),
					width: child.props.width === "*" || !child.props.width ? "*" : child.props.width
				})),
				columnGap: p.gap ?? 0
			};
			return content;
		}
		case "row": return {
			columns: node.children.filter((c) => typeof c !== "string").map((child) => ({
				stack: [nodeToContent(child)].flat().filter(Boolean),
				width: child.props.width ?? "*"
			})),
			columnGap: p.gap ?? 0
		};
		case "column": return node.children.map((c) => typeof c === "string" ? c : nodeToContent(c)).filter((c) => c != null).flat();
		case "heading": {
			const level = p.level ?? 1;
			return {
				text: getTextContent(node.children),
				fontSize: {
					1: 24,
					2: 20,
					3: 18,
					4: 16,
					5: 14,
					6: 12
				}[level] ?? 18,
				bold: true,
				color: p.color ?? "#000000",
				alignment: p.align ?? "left",
				margin: [
					0,
					level === 1 ? 0 : 8,
					0,
					8
				]
			};
		}
		case "text": return {
			text: getTextContent(node.children),
			fontSize: p.size ?? 12,
			color: p.color ?? "#333333",
			bold: p.bold ?? false,
			italics: p.italic ?? false,
			decoration: p.underline ? "underline" : p.strikethrough ? "lineThrough" : void 0,
			alignment: p.align ?? "left",
			lineHeight: p.lineHeight ?? 1.4,
			margin: [
				0,
				0,
				0,
				8
			]
		};
		case "link": return {
			text: getTextContent(node.children),
			link: p.href,
			color: p.color ?? "#4f46e5",
			decoration: "underline"
		};
		case "image": {
			const src = p.src;
			const resolved = resolveImageSrc(src);
			if ("image" in resolved) {
				const result = {
					image: resolved.image,
					fit: [p.width ?? 500, p.height ?? 400],
					margin: [
						0,
						0,
						0,
						8
					]
				};
				if (p.align === "center") result.alignment = "center";
				if (p.align === "right") result.alignment = "right";
				return result;
			}
			return {
				...resolved,
				margin: [
					0,
					0,
					0,
					8
				]
			};
		}
		case "table": {
			const columns = (p.columns ?? []).map(resolveColumn);
			const rows = p.rows ?? [];
			const hs = p.headerStyle;
			const headerRow = columns.map((col) => ({
				text: col.header,
				bold: true,
				fillColor: hs?.background ?? "#f5f5f5",
				color: hs?.color ?? "#000000",
				alignment: col.align ?? "left"
			}));
			const dataRows = rows.map((row, rowIdx) => columns.map((col, colIdx) => ({
				text: String(row[colIdx] ?? ""),
				alignment: col.align ?? "left",
				fillColor: p.striped && rowIdx % 2 === 1 ? "#f9f9f9" : void 0
			})));
			return {
				table: {
					headerRows: 1,
					widths: columns.map((col) => {
						if (!col.width) return "*";
						if (typeof col.width === "string" && col.width.endsWith("%")) return col.width;
						return col.width;
					}),
					body: [headerRow, ...dataRows]
				},
				layout: p.bordered ? void 0 : "lightHorizontalLines",
				unbreakable: p.keepTogether ?? false,
				margin: [
					0,
					0,
					0,
					12
				]
			};
		}
		case "list": {
			const items = node.children.filter((c) => typeof c !== "string").map((item) => getTextContent(item.children));
			return p.ordered ? {
				ol: items,
				margin: [
					0,
					0,
					0,
					8
				]
			} : {
				ul: items,
				margin: [
					0,
					0,
					0,
					8
				]
			};
		}
		case "list-item": return getTextContent(node.children);
		case "code": return {
			text: getTextContent(node.children),
			font: "Courier",
			fontSize: 10,
			background: "#f5f5f5",
			margin: [
				0,
				0,
				0,
				8
			]
		};
		case "page-break": return {
			text: "",
			pageBreak: "after"
		};
		case "divider": return {
			canvas: [{
				type: "line",
				x1: 0,
				y1: 0,
				x2: 515,
				y2: 0,
				lineWidth: p.thickness ?? 1,
				lineColor: p.color ?? "#dddddd"
			}],
			margin: [
				0,
				8,
				0,
				8
			]
		};
		case "spacer": return {
			text: "",
			margin: [
				0,
				p.height ?? 12,
				0,
				0
			]
		};
		case "button": return {
			text: getTextContent(node.children),
			link: p.href,
			bold: true,
			color: p.color ?? "#ffffff",
			background: p.background ?? "#4f46e5",
			margin: [
				0,
				8,
				0,
				8
			]
		};
		case "quote": return {
			table: {
				widths: [4, "*"],
				body: [[{
					text: "",
					fillColor: p.borderColor ?? "#dddddd"
				}, {
					text: getTextContent(node.children),
					italics: true,
					color: "#555555",
					margin: [
						8,
						4,
						0,
						4
					]
				}]]
			},
			layout: "noBorders",
			margin: [
				0,
				4,
				0,
				8
			]
		};
		default: return null;
	}
}
function resolveMargin(margin) {
	if (margin == null) return [
		40,
		40,
		40,
		40
	];
	if (typeof margin === "number") return [
		margin,
		margin,
		margin,
		margin
	];
	if (margin.length === 2) return [
		margin[1],
		margin[0],
		margin[1],
		margin[0]
	];
	return margin;
}
/**
* Render header/footer DocNodes into pdfmake content for page headers/footers.
*
* pdfmake header/footer functions receive `(currentPage, pageCount, pageSize)`
* and must return a content object. We flatten the DocNode into static content.
*/
function renderHeaderFooter(node) {
	if (!node) return void 0;
	const content = nodeToContent(node);
	if (content == null) return void 0;
	if (Array.isArray(content)) return {
		stack: content,
		margin: [
			40,
			10,
			40,
			0
		]
	};
	if (typeof content === "object") return {
		...content,
		margin: [
			40,
			10,
			40,
			0
		]
	};
	return {
		text: content,
		margin: [
			40,
			10,
			40,
			0
		]
	};
}
const pdfRenderer = { async render(node, _options) {
	let pdfMakeModule;
	let pdfFontsModule;
	try {
		pdfMakeModule = await import("./pdfmake-CKMX5URW.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
		pdfFontsModule = await import("./vfs_fonts-Cap07Jg3.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
	} catch {
		throw new Error("[@pyreon/document] PDF renderer requires \"pdfmake\" package. Install it: bun add pdfmake");
	}
	let pdfMake = pdfMakeModule.default ?? pdfMakeModule;
	if (pdfMake.default && typeof pdfMake.default.createPdf === "function") pdfMake = pdfMake.default;
	const pdfFonts = pdfFontsModule.default ?? pdfFontsModule;
	if (pdfMake.vfs == null) pdfMake.vfs = pdfFonts.pdfMake?.vfs ?? pdfFonts.vfs;
	const pageNode = node.children.find((c) => typeof c !== "string" && c.type === "page");
	const pageSize = pageNode?.props.size ?? "A4";
	const pageOrientation = pageNode?.props.orientation ?? "portrait";
	const pageMargin = resolveMargin(pageNode?.props.margin);
	const content = [nodeToContent(node)].flat().filter(Boolean);
	const headerFn = renderHeaderFooter(pageNode?.props.header);
	const footerFn = renderHeaderFooter(pageNode?.props.footer);
	const docDefinition = {
		pageSize: PAGE_SIZES[pageSize] ?? PAGE_SIZES.A4,
		pageOrientation,
		pageMargins: pageMargin,
		info: {
			title: node.props.title ?? "",
			author: node.props.author ?? "",
			subject: node.props.subject ?? "",
			keywords: node.props.keywords?.join(", ") ?? ""
		},
		content,
		defaultStyle: {
			fontSize: 12,
			lineHeight: 1.4
		},
		pageBreakBefore: (currentNode, helpers) => {
			if (currentNode.headlineLevel && helpers.getFollowingNodesOnPage) {
				if (helpers.getFollowingNodesOnPage().length === 0) return true;
			}
			return false;
		}
	};
	if (headerFn) docDefinition.header = headerFn;
	if (footerFn) docDefinition.footer = footerFn;
	try {
		const buffer = await pdfMake.createPdf(docDefinition).getBuffer();
		return new Uint8Array(buffer);
	} catch (err) {
		throw new Error(`[@pyreon/document] PDF generation failed: ${err}`);
	}
} };

//#endregion
export { pdfRenderer };
//# sourceMappingURL=pdf-IuBgTb3T.js.map