117 lines
4.5 KiB
TypeScript
117 lines
4.5 KiB
TypeScript
import * as txml from "#lib/helpers/txml.js"
|
|
import { PDFDocument, PDFFont, rgb, StandardFonts } from "pdf-lib"
|
|
import { Checkbox, Flex, Text } from "./index.js"
|
|
import { Layout } from "./Layout.js"
|
|
import { Image } from "./Image.js"
|
|
import { PDFElement } from "./PDFElement.js"
|
|
|
|
export function xml2pdf(xml: string, fonts: Record<string, PDFFont> & { "default": PDFFont }) {
|
|
const tree = txml.parse(xml)
|
|
|
|
const iterateChildren = (children: ReturnType<typeof txml.parse>, parent: PDFElement) => {
|
|
for (const child of children) {
|
|
if (typeof child === "string") {
|
|
// Simple text
|
|
parent.addChild(new Text(child, { font: fonts["default"] }))
|
|
} else if (child.tagName === "flex") {
|
|
const flexbox = new Flex({
|
|
height: parseFloat(child.attributes.height) || "auto",
|
|
width: parseFloat(child.attributes.width) || "auto",
|
|
align: child.attributes.align,
|
|
direction: child.attributes.direction,
|
|
gap: parseFloat(child.attributes.gap) || 0,
|
|
justify: child.attributes.justify,
|
|
margin: {
|
|
bottom: parseFloat(child.attributes.marginBottom) || 0,
|
|
left: parseFloat(child.attributes.marginLeft) || 0,
|
|
right: parseFloat(child.attributes.marginRight) || 0,
|
|
top: parseFloat(child.attributes.marginTop) || 0,
|
|
},
|
|
padding: {
|
|
bottom: parseFloat(child.attributes.paddingBottom) || 0,
|
|
left: parseFloat(child.attributes.paddingLeft) || 0,
|
|
right: parseFloat(child.attributes.paddingRight) || 0,
|
|
top: parseFloat(child.attributes.paddingTop) || 0,
|
|
}
|
|
})
|
|
|
|
iterateChildren(child.children, flexbox)
|
|
parent.addChild(flexbox)
|
|
} else if (child.tagName === "text") {
|
|
|
|
let color = rgb(0,0,0)
|
|
|
|
if (child.attributes.hasOwnProperty("color")) {
|
|
const colorValue = child.attributes.color.split(",")
|
|
|
|
if (colorValue.length !== 3) {
|
|
throw new Error("Invalid color, please provide 'r,g,b' as a value.")
|
|
}
|
|
|
|
color = rgb(...colorValue.map((x) => parseInt(x) / 255) as [number, number, number]);
|
|
}
|
|
|
|
const text = new Text(child.children[0] as string || "", { font: child.attributes.hasOwnProperty("font") ? fonts[child.attributes["font"]] : fonts["default"], lineHeight: parseFloat(child.attributes.lineHeight), fontSize: parseFloat(child.attributes.size) || 10, color, margin: {
|
|
bottom: parseFloat(child.attributes.marginBottom) || 0,
|
|
left: parseFloat(child.attributes.marginLeft) || 0,
|
|
right: parseFloat(child.attributes.marginRight) || 0,
|
|
top: parseFloat(child.attributes.marginTop) || 0,
|
|
},
|
|
padding: {
|
|
bottom: parseFloat(child.attributes.paddingBottom) || 0,
|
|
left: parseFloat(child.attributes.paddingLeft) || 0,
|
|
right: parseFloat(child.attributes.paddingRight) || 0,
|
|
top: parseFloat(child.attributes.paddingTop) || 0,
|
|
} })
|
|
|
|
parent.addChild(text)
|
|
} else if (child.tagName === "checkbox") {
|
|
if (typeof child.attributes.width === "undefined" || typeof child.attributes.height === "undefined") {
|
|
throw new Error("Missing height or width property in Checkbox creation.")
|
|
}
|
|
|
|
const checkbox = new Checkbox(parseFloat(child.attributes.width), parseFloat(child.attributes.height), (child.attributes.hasOwnProperty("checked") && child.attributes.checked !== "false") || false)
|
|
|
|
parent.addChild(checkbox);
|
|
} else if (child.tagName === "layout") {
|
|
const layout = new Layout([], {
|
|
margin: {
|
|
bottom: parseFloat(child.attributes.marginBottom) || 0,
|
|
left: parseFloat(child.attributes.marginLeft) || 0,
|
|
right: parseFloat(child.attributes.marginRight) || 0,
|
|
top: parseFloat(child.attributes.marginTop) || 0,
|
|
},
|
|
padding: {
|
|
bottom: parseFloat(child.attributes.paddingBottom) || 0,
|
|
left: parseFloat(child.attributes.paddingLeft) || 0,
|
|
right: parseFloat(child.attributes.paddingRight) || 0,
|
|
top: parseFloat(child.attributes.paddingTop) || 0,
|
|
}
|
|
})
|
|
|
|
iterateChildren(child.children, layout)
|
|
parent.addChild(layout)
|
|
} else if (child.tagName === "img") {
|
|
const image = new Image({
|
|
width: parseFloat(child.attributes.width),
|
|
height: parseFloat(child.attributes.height),
|
|
margin: {
|
|
bottom: parseFloat(child.attributes.marginBottom) || 0,
|
|
left: parseFloat(child.attributes.marginLeft) || 0,
|
|
right: parseFloat(child.attributes.marginRight) || 0,
|
|
top: parseFloat(child.attributes.marginTop) || 0,
|
|
},
|
|
src: child.attributes.src,
|
|
data: child.attributes.data
|
|
})
|
|
|
|
parent.addChild(image)
|
|
}
|
|
}
|
|
}
|
|
|
|
const layout = new Layout([]);
|
|
iterateChildren(tree, layout)
|
|
|
|
return layout
|
|
} |