UNPKG

pdfmkr

Version:

Generate PDF documents from JavaScript objects

1,454 lines (1,426 loc) 112 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/api/colors.ts var colors_exports = {}; __export(colors_exports, { namedColors: () => namedColors }); var namedColors = { black: rgb(0, 0, 0), gray: rgb(0.5, 0.5, 0.5), white: rgb(1, 1, 1), red: rgb(1, 0, 0), blue: rgb(0, 0, 1), green: rgb(0, 0.5, 0), cyan: rgb(0, 1, 1), magenta: rgb(1, 0, 1), yellow: rgb(1, 1, 0), lightgray: rgb(0.83, 0.83, 0.83), darkgray: rgb(0.66, 0.66, 0.66) }; function rgb(red, green, blue) { return [red, green, blue]; } // src/api/document.ts var document_exports = {}; // src/api/graphics.ts var graphics_exports = {}; __export(graphics_exports, { circle: () => circle, line: () => line, path: () => path, rect: () => rect }); function line(x1, y1, x2, y2, props) { return { ...props, type: "line", x1, y1, x2, y2 }; } function rect(x, y, width, height, props) { return { ...props, type: "rect", x, y, width, height }; } function circle(cx, cy, r, props) { return { ...props, type: "circle", cx, cy, r }; } function path(d, props) { return { ...props, type: "path", d }; } // src/api/layout.ts var layout_exports = {}; __export(layout_exports, { columns: () => columns, image: () => image, rows: () => rows, text: () => text }); function text(text2, props) { return { ...props, text: text2 }; } function image(image2, props) { return { ...props, image: image2 }; } function columns(columns2, props) { return { ...props, columns: columns2 }; } function rows(rows2, props) { return { ...props, rows: rows2 }; } // src/api/PdfMaker.ts var PdfMaker_exports = {}; __export(PdfMaker_exports, { PdfMaker: () => PdfMaker }); // src/font-store.ts import { PDFEmbeddedFont } from "@ralfstx/pdf-core"; // src/util/print-value.ts function printValue(value, refs) { if (typeof value === "string") return `'${value}'`; if (Array.isArray(value)) return printArray(value, refs); if (value instanceof Date) { try { return `Date ${value.toISOString()}`; } catch { return `Invalid Date`; } } if (value instanceof Function) { return value.name ? `function ${value.name}` : "anonymous function"; } if (value instanceof ArrayBuffer) return `ArrayBuffer ${printArray([...new Uint8Array(value)])}`; if (ArrayBuffer.isView(value)) { return `${value.constructor.name} ${printArray([...new Uint8Array(value.buffer)])}`; } const str = String(value); if (str === "[object Object]") return printObject(value, refs); return str; } function printArray(array, refs) { if (refs?.includes(array)) return "recursive ref"; const maxElements = 8; const content = array.slice(0, maxElements).map((v) => printValue(v, [...refs ?? [], array])).join(", "); const tail = array.length > maxElements ? ", \u2026" : ""; return `[${content}${tail}]`; } function printObject(object, refs) { if (refs?.includes(object)) return "recursive ref"; const maxEntries = 8; const entries = Object.entries(object); const tail = entries.length > maxEntries ? ", \u2026" : ""; const main = entries.slice(0, maxEntries).map(([key, value]) => `${key}: ${printValue(value, [...refs ?? [], object])}`).join(", "); return `{${main}${tail}}`; } // src/fonts.ts function weightToNumber(weight) { if (weight === "normal") { return 400; } if (weight === "bold") { return 700; } if (typeof weight !== "number" || !isFinite(weight) || weight < 1 || weight > 1e3) { throw new Error(`Invalid font weight: ${printValue(weight)}`); } return weight; } // src/font-store.ts var FontStore = class { #fontDefs; #fontCache = {}; constructor() { this.#fontDefs = []; } registerFont(data, config) { const pdfFont = new PDFEmbeddedFont(data); const family = config?.family ?? pdfFont.familyName; const style = config?.style ?? pdfFont.style; const weight = weightToNumber(config?.weight ?? pdfFont.weight); this.#fontDefs.push({ family, style, weight, data, pdfFont }); this.#fontCache = {}; } async selectFont(selector) { const cacheKey = [ selector.fontFamily ?? "any", selector.fontStyle ?? "normal", selector.fontWeight ?? "normal" ].join(":"); try { return await (this.#fontCache[cacheKey] ??= this._loadFont(selector)); } catch (error) { const { fontFamily: family, fontStyle: style, fontWeight: weight } = selector; const selectorStr = `'${family}', style=${style ?? "normal"}, weight=${weight ?? "normal"}`; throw new Error(`Could not load font for ${selectorStr}`, { cause: error }); } } _loadFont(selector) { const selectedFontDef = selectFontDef(this.#fontDefs, selector); return Promise.resolve(selectedFontDef.pdfFont ?? new PDFEmbeddedFont(selectedFontDef.data)); } }; function selectFontDef(fontDefs, selector) { if (!fontDefs.length) { throw new Error("No fonts defined"); } const fontsWithMatchingFamily = selector.fontFamily ? fontDefs.filter((def) => def.family === selector.fontFamily) : fontDefs; if (!fontsWithMatchingFamily.length) { const uniqueFamilies = [...new Set(fontDefs.map((f) => f.family))]; throw new Error( `No matching font found for family '${selector.fontFamily}'. Registered families are: '${uniqueFamilies.join("', '")}'.` ); } let fontsWithMatchingStyle = fontsWithMatchingFamily.filter( (def) => def.style === (selector.fontStyle ?? "normal") ); if (!fontsWithMatchingStyle.length) { fontsWithMatchingStyle = fontsWithMatchingFamily.filter( (def) => def.style === "italic" && selector.fontStyle === "oblique" || def.style === "oblique" && selector.fontStyle === "italic" ); } if (!fontsWithMatchingStyle.length) { const { fontFamily: family, fontStyle: style } = selector; const selectorStr = `'${family}', style=${style ?? "normal"}`; throw new Error(`No matching font found for ${selectorStr}`); } const selected = selectFontForWeight(fontsWithMatchingStyle, selector.fontWeight ?? "normal"); if (!selected) { const { fontFamily: family, fontStyle: style, fontWeight: weight } = selector; const selectorStr = `'${family}', style=${style ?? "normal"}, weight=${weight ?? "normal"}`; throw new Error(`No matching font found for ${selectorStr}`); } return selected; } function selectFontForWeight(fonts, weight) { const weightNum = weightToNumber(weight); const font = fonts.find((font2) => font2.weight === weightNum); if (font) return font; const ascending = fonts.slice().sort((a, b) => a.weight - b.weight); const descending = ascending.slice().reverse(); if (weightNum >= 400 && weightNum <= 500) { const font2 = ascending.find((font3) => font3.weight > weightNum && font3.weight <= 500) ?? descending.find((font3) => font3.weight < weightNum) ?? ascending.find((font3) => font3.weight > 500); if (font2) return font2; } if (weightNum < 400) { const font2 = descending.find((font3) => font3.weight < weightNum) ?? ascending.find((font3) => font3.weight > weightNum); if (font2) return font2; } if (weightNum > 500) { const font2 = ascending.find((font3) => font3.weight > weightNum) ?? descending.find((font3) => font3.weight < weightNum); if (font2) return font2; } throw new Error(`Could not find font for weight ${weight}`); } // src/image-loader.ts import { PDFImage } from "@ralfstx/pdf-core"; // src/util/base64.ts var base64Lookup = createBase64LookupTable(); function decodeBase64(base64) { if (base64.length % 4 !== 0) { throw new Error("Invalid base64 string: length must be a multiple of 4"); } const len = base64.length; const padding = base64[len - 1] === "=" ? base64[len - 2] === "=" ? 2 : 1 : 0; const bufferLength = len * 3 / 4 - padding; const bytes = new Uint8Array(bufferLength); let byteIndex = 0; for (let i = 0; i < len; i += 4) { const encoded1 = lookup(base64, i); const encoded2 = lookup(base64, i + 1); const encoded3 = lookup(base64, i + 2); const encoded4 = lookup(base64, i + 3); bytes[byteIndex++] = encoded1 << 2 | encoded2 >> 4; if (base64[i + 2] !== "=") bytes[byteIndex++] = (encoded2 & 15) << 4 | encoded3 >> 2; if (base64[i + 3] !== "=") bytes[byteIndex++] = (encoded3 & 3) << 6 | encoded4; } return bytes; } function lookup(string, pos) { const code = string.charCodeAt(pos); if (code === 61) return 0; if (code < base64Lookup.length) { const value = base64Lookup[code]; if (value !== 255) { return value; } } throw new Error(`Invalid Base64 character '${string[pos]}' at position ${pos}`); } function createBase64LookupTable() { const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const table = new Uint8Array(256).fill(255); for (let i = 0; i < base64Chars.length; i++) { table[base64Chars.charCodeAt(i)] = i; } return table; } // src/util/fs.ts var readRelativeFile = async (rootDir, relPath) => { let fs; let path2; try { fs = await import("node:fs/promises"); path2 = await import("node:path"); } catch { throw new Error("File system is not available in this environment"); } if (path2.isAbsolute(relPath)) { throw new Error(`Path is not relative: '${relPath}'`); } const resolvedPath = path2.resolve(rootDir, relPath); const realPath = await fs.realpath(resolvedPath); try { return await fs.readFile(realPath); } catch (error) { throw new Error(`Failed to load file '${realPath}'`, { cause: error }); } }; // src/data-loader.ts function createDataLoader(config) { const loaders = { http: loadHttp, https: loadHttp, data: loadData, file: loadFile }; return async function(url) { const schema = getUrlSchema(url).slice(0, -1); const loader = loaders[schema]; if (!loader) { throw new Error(`URL not supported: '${url}'`); } return await loader(url, config); }; } function getUrlSchema(url) { try { return new URL(url).protocol; } catch { throw new Error(`Invalid URL: '${url}'`); } } function loadData(url) { if (!url.startsWith("data:")) { throw new Error(`Not a data URL: '${url}'`); } const endOfHeader = url.indexOf(","); if (endOfHeader === -1) { throw new Error(`Invalid data URL: '${url}'`); } const header = url.slice(5, endOfHeader); if (!header.endsWith(";base64")) { throw new Error(`Unsupported encoding in data URL: '${url}'`); } const dataPart = url.slice(endOfHeader + 1); const data = new Uint8Array(decodeBase64(dataPart)); return { data }; } async function loadHttp(url) { if (!url.startsWith("http:") && !url.startsWith("https:")) { throw new Error(`Not a http(s) URL: '${url}'`); } const response = await fetch(url); if (!response.ok) { throw new Error(`Received ${response.status} ${response.statusText}`); } const data = new Uint8Array(await response.arrayBuffer()); return { data }; } async function loadFile(url, config) { if (!url.startsWith("file:")) { throw new Error(`Not a file URL: '${url}'`); } if (!config?.resourceRoot) { throw new Error("No resource root defined"); } const urlPath = decodeURIComponent(new URL(url).pathname); const relPath = urlPath.replace(/^\//g, ""); const data = new Uint8Array(await readRelativeFile(config.resourceRoot, relPath)); return { data }; } // src/image-loader.ts function createImageLoader(resourceRoot) { const dataLoader = createDataLoader(resourceRoot ? { resourceRoot } : void 0); const cache = {}; return (url) => cache[url] ??= loadImage(url, dataLoader); } async function loadImage(url, dataLoader) { const { data } = await dataLoader(url); const format = determineImageFormat(data); return format === "jpeg" ? PDFImage.fromJpeg(data) : PDFImage.fromPng(data); } function determineImageFormat(data) { if (isPng(data)) return "png"; if (isJpeg(data)) return "jpeg"; throw new Error("Unknown image format"); } function isJpeg(data) { return data[0] === 255 && data[1] === 216 && data[2] === 255; } function isPng(data) { return hasBytes(data, 0, [137, 80, 78, 71, 13, 10, 26, 10]); } function hasBytes(data, offset, bytes) { for (let i = 0; i < bytes.length; i++) { if (data[offset + i] !== bytes[i]) return false; } return true; } // src/layout/layout.ts import { PDFPage } from "@ralfstx/pdf-core"; // src/api/sizes.ts var sizes_exports = {}; __export(sizes_exports, { paperSizes: () => paperSizes }); var paperSizes = { "4A0": { width: 4767.87, height: 6740.79 }, "2A0": { width: 3370.39, height: 4767.87 }, A0: { width: 2383.94, height: 3370.39 }, A1: { width: 1683.78, height: 2383.94 }, A2: { width: 1190.55, height: 1683.78 }, A3: { width: 841.89, height: 1190.55 }, A4: { width: 595.28, height: 841.89 }, A5: { width: 419.53, height: 595.28 }, A6: { width: 297.64, height: 419.53 }, A7: { width: 209.76, height: 297.64 }, A8: { width: 147.4, height: 209.76 }, A9: { width: 104.88, height: 147.4 }, A10: { width: 73.7, height: 104.88 }, B0: { width: 2834.65, height: 4008.19 }, B1: { width: 2004.09, height: 2834.65 }, B2: { width: 1417.32, height: 2004.09 }, B3: { width: 1000.63, height: 1417.32 }, B4: { width: 708.66, height: 1000.63 }, B5: { width: 498.9, height: 708.66 }, B6: { width: 354.33, height: 498.9 }, B7: { width: 249.45, height: 354.33 }, B8: { width: 175.75, height: 249.45 }, B9: { width: 124.72, height: 175.75 }, B10: { width: 87.87, height: 124.72 }, C0: { width: 2599.37, height: 3676.54 }, C1: { width: 1836.85, height: 2599.37 }, C2: { width: 1298.27, height: 1836.85 }, C3: { width: 918.43, height: 1298.27 }, C4: { width: 649.13, height: 918.43 }, C5: { width: 459.21, height: 649.13 }, C6: { width: 323.15, height: 459.21 }, C7: { width: 229.61, height: 323.15 }, C8: { width: 161.57, height: 229.61 }, C9: { width: 113.39, height: 161.57 }, C10: { width: 79.37, height: 113.39 }, RA0: { width: 2437.8, height: 3458.27 }, RA1: { width: 1729.13, height: 2437.8 }, RA2: { width: 1218.9, height: 1729.13 }, RA3: { width: 864.57, height: 1218.9 }, RA4: { width: 609.45, height: 864.57 }, SRA0: { width: 2551.18, height: 3628.35 }, SRA1: { width: 1814.17, height: 2551.18 }, SRA2: { width: 1275.59, height: 1814.17 }, SRA3: { width: 907.09, height: 1275.59 }, SRA4: { width: 637.8, height: 907.09 }, Executive: { width: 521.86, height: 756 }, Folio: { width: 612, height: 936 }, Legal: { width: 612, height: 1008 }, Letter: { width: 612, height: 792 }, Tabloid: { width: 792, height: 1224 } }; // src/util/types.ts function pickDefined(obj) { const result = {}; for (const key in obj) { if (typeof obj[key] !== "undefined") { result[key] = obj[key]; } } return result; } function readFrom(object, name, type) { return readAs(object[name], name, type); } function readAs(value, name, type) { try { return asType(value, type); } catch (error) { if (error instanceof Error && error.message === "Missing value") { throw new TypeError(`Missing value for "${name}"`); } if (error instanceof Error && error.message?.startsWith('Invalid value for "')) { const tail = error.message.replace(/^Invalid value for "/, ""); throw new TypeError(`Invalid value for "${name}/${tail}`); } const errorStr = error instanceof Error ? error.message : String(error); throw new TypeError(`Invalid value for "${name}": ${errorStr}`); } } var types = { boolean: () => readBoolean, date: () => readDate, string: (options) => (value) => readString(value, options), number: (options) => (value) => readNumber(value, options), array: (items, options) => (value) => readArray(value, items, options), object: (properties, options) => (value) => readObject(value, properties, options) }; function optional(type) { return (value) => { if (value === void 0) return void 0; return type ? asType(value, type) : value; }; } function required(type) { return (value) => { if (value === void 0) throw new TypeError(`Missing value`); return type ? asType(value, type) : value; }; } function dynamic(type, name) { return (value) => { if (typeof value !== "function") { const result = asType(value, type); return () => result; } const subject = name ? `Supplied function for "${name}"` : "Supplied function"; return (...args) => { const result = safeCall(value, args, subject); try { return asType(result, type); } catch (error) { throw new Error(`${subject} returned invalid value`, { cause: error }); } }; }; } function safeCall(fn, args, subject) { try { return fn(...args); } catch (error) { throw new Error(`${subject} threw error`, { cause: error }); } } function asType(value, type) { if (type == null) return value; if (typeof type === "function") return type(value); throw new Error(`Invalid type definition: ${printValue(type)}`); } function readBoolean(value) { if (typeof value === "boolean") return value; throw typeError("boolean", value); } function readString(value, options) { if (typeof value !== "string") throw typeError("string", value); if (options?.enum && !options.enum.includes(value)) { throw typeError(`one of (${options.enum.map((s) => `'${s}'`).join(", ")})`, value); } if (options?.pattern && !options.pattern.test(value)) { throw typeError(`string matching pattern ${options.pattern}`, value); } return value; } function readNumber(input, options) { if (!Number.isFinite(input)) throw typeError("number", input); const num = input; if (options?.minimum != null && !(num >= options.minimum)) { throw typeError(`number >= ${options.minimum}`, input); } if (options?.maximum != null && !(num <= options.maximum)) { throw typeError(`number <= ${options.maximum}`, input); } return num; } function readDate(value) { if (value instanceof Date) { if (Number.isNaN(value.getTime())) { throw typeError("valid Date", value); } return value; } throw typeError("Date", value); } function readArray(input, items, options) { if (!Array.isArray(input)) throw typeError("array", input); if (options?.minItems != null && input.length < options.minItems) { throw typeError(`array with minimum length ${options.minItems}`, input); } if (options?.maxItems != null && input.length > options.maxItems) { throw typeError(`array with maximum length ${options.maxItems}`, input); } if (options?.uniqueItems === true && !isUniq(input)) { throw typeError(`array with unique items`, input); } return items ? mapItems(input, items) : input; } function isUniq(array) { return array.every((v, i, a) => a.indexOf(v) === i); } function mapItems(array, type) { return array.map((item, idx) => readAs(item, idx.toString(), type)); } function readObject(input, properties, options) { if (!isObject(input)) throw typeError("object", input); if (options?.minProperties != null && Object.keys(input).length < options.minProperties) { throw typeError(`object with min. ${options.minProperties} properties`, input); } if (options?.maxProperties != null && Object.keys(input).length > options.maxProperties) { throw typeError(`object with max. ${options.maxProperties} properties`, input); } if (!properties) return input; return pickDefined( Object.fromEntries( Object.entries(properties).map(([key, type]) => { return [key, readFrom(input, key, type)]; }) ) ); } function isObject(value) { return value != null && typeof value === "object" && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]"; } function typeError(expected, value) { return new TypeError(`Expected ${expected}, got: ${printValue(value)}`); } // src/box.ts var ZERO_EDGES = Object.freeze(parseEdges(0)); function subtractEdges(box, edges) { return { x: box.x + (edges?.left ?? 0), y: box.y + (edges?.top ?? 0), width: Math.max(0, box.width - (edges?.left ?? 0) - (edges?.right ?? 0)), height: Math.max(0, box.height - (edges?.top ?? 0) - (edges?.bottom ?? 0)) }; } function parseEdges(input) { if (typeof input === "number" || typeof input === "string") { const value = parseLength(input); return { right: value, left: value, top: value, bottom: value }; } if (isObject(input)) { const obj = input; return { right: parseLength(obj.right ?? obj.x ?? 0), left: parseLength(obj.left ?? obj.x ?? 0), top: parseLength(obj.top ?? obj.y ?? 0), bottom: parseLength(obj.bottom ?? obj.y ?? 0) }; } throw typeError("number, length string, or object", input); } function parseLength(input) { if (typeof input === "number" && Number.isFinite(input)) { return input; } if (typeof input === "string") { const unit = input.slice(-2); if (unit === "pt" || unit === "in" || unit === "mm" || unit === "cm") { const value = parseFloat(input.slice(0, -2)); if (Number.isFinite(value)) { return convertToPt(value, unit); } } } throw typeError("number or length string", input); } function convertToPt(value, fromUnit) { switch (fromUnit) { case "pt": return value * 1; case "in": return value * 72; case "mm": return value * 72 / 25.4; case "cm": return value * 72 / 2.54; default: throw new TypeError(`Invalid unit: '${fromUnit}'`); } } // src/colors.ts var rgb2 = (red, green, blue) => { assertRange(red, "red", 0, 1); assertRange(green, "green", 0, 1); assertRange(blue, "blue", 0, 1); return { type: "RGB", red, green, blue }; }; function setFillingColor(cs, color) { if (color.type === "RGB") { cs.setFillRGB(color.red, color.green, color.blue); } else throw new Error(`Invalid color: ${JSON.stringify(color)}`); } function setStrokingColor(cs, color) { if (color.type === "RGB") { cs.setStrokeRGB(color.red, color.green, color.blue); } else throw new Error(`Invalid color: ${JSON.stringify(color)}`); } function assertRange(value, valueName, min, max) { if (typeof value !== "number" || value < min || value > max) { throw new Error(`${valueName} must be a number between ${min} and ${max}, got: ${value}`); } } // src/util/utils.ts function compact(array) { return array.filter(Boolean); } function omit(obj, ...keys) { const result = { ...obj }; for (const key of keys) { delete result[key]; } return result; } function multiplyMatrices(matrix1, matrix2) { const result = []; const [a, b, c, d, e, f] = matrix1; const [g, h, i, j, k, l] = matrix2; result[0] = a * g + c * h; result[1] = b * g + d * h; result[2] = a * i + c * j; result[3] = b * i + d * j; result[4] = a * k + c * l + e; result[5] = b * k + d * l + f; return result; } function round(n, precision = 6) { const factor = Math.pow(10, precision); return Math.round(n * factor) / factor; } // src/guides.ts function createFrameGuides(frame, block) { const { width: w, height: h } = frame; const { left: ml, right: mr, top: mt, bottom: mb } = block.margin ?? ZERO_EDGES; const { left: pl, right: pr, top: pt, bottom: pb } = block.padding ?? ZERO_EDGES; const { breakBefore: bb, breakAfter: ba, isPage: page } = block; const stroke = { lineColor: rgb2(0, 0, 0), lineWidth: 0.5, lineOpacity: 0.25 }; const fill = { fillColor: rgb2(0, 0, 0), fillOpacity: 0.25 }; const mFill = { fillColor: rgb2(0.9, 0.8, 0.3), fillOpacity: 0.15 }; const pFill = { fillColor: rgb2(0.2, 0.2, 0.7), fillOpacity: 0.15 }; const shapes = compact([ rect2({ x: 0, y: 0, width: w, height: h, ...stroke }), // margin !!mt && rect2({ x: -ml, y: -mt, width: w + ml + mr, height: mt, ...mFill }), !!ml && rect2({ x: -ml, y: 0, width: ml, height: h, ...mFill }), !!mr && rect2({ x: w, y: 0, width: mr, height: h, ...mFill }), !!mb && rect2({ x: -ml, y: h, width: w + ml + mr, height: mb, ...mFill }), // padding !!pt && rect2({ x: 0, y: 0, width: w, height: pt, ...pFill }), !!pl && rect2({ x: 0, y: pt, width: pl, height: h - pt - pb, ...pFill }), !!pr && rect2({ x: w - pr, y: pt, width: pr, height: h - pt - pb, ...pFill }), !!pb && rect2({ x: 0, y: h - pb, width: w, height: pb, ...pFill }), // indicators for breakBefore and breakAfter bb === "avoid" && circle2({ cx: 5, cy: 0, r: 3, ...fill }), bb === "always" && rect2({ x: 0, y: -3, width: 30, height: 3, ...fill }), ba === "avoid" && circle2({ cx: w - 5, cy: h, r: 3, ...fill }), ba === "always" && rect2({ x: w - 30, y: h, width: 30, height: 3, ...fill }), // for pages, separator lines for header and footer page && line2({ x1: -ml, y1: -mt, x2: w + mr + ml, y2: -mt, ...stroke }), page && line2({ x1: -ml, y1: h + mb, x2: w + mr + ml, y2: h + mb, ...stroke }) ]); return { type: "graphics", shapes }; } function createRowGuides({ x, y, width, height, baseline }) { const stroke = { lineColor: rgb2(0, 0.5, 0), lineWidth: 0.5, lineOpacity: 0.25 }; const fill = { fillColor: rgb2(0, 0.5, 0), fillOpacity: 0.25 }; const by = y + baseline; const shapes = [ rect2({ x, y, width: 3, height: 3, ...fill }), rect2({ x, y, width, height, ...stroke }), line2({ x1: x, y1: by, x2: x + width, y2: by, ...stroke }) ]; return { type: "graphics", shapes }; } function rect2(args) { return { type: "rect", ...args }; } function circle2(args) { return { type: "circle", ...args }; } function line2(args) { return { type: "line", ...args }; } // src/read/read-page-size.ts function readPageSize(def) { if (typeof def === "string") { const size = paperSizes[def]; if (!size) throw typeError("valid paper size", def); const { width, height } = size; return { width, height }; } if (isObject(def)) { const width = readFrom(def, "width", required(parseLength)); const height = readFrom(def, "height", required(parseLength)); if (width <= 0) throw typeError("positive width", width); if (height <= 0) throw typeError("positive height", height); return { width, height }; } throw typeError("valid page size", def); } function parseOrientation(def) { if (def === "portrait" || def === "landscape") return def; throw typeError("'portrait' or 'landscape'", def); } function applyOrientation(size, orientation) { const { width, height } = size; if (orientation === "portrait") { return { width: Math.min(width, height), height: Math.max(width, height) }; } if (orientation === "landscape") { return { width: Math.max(width, height), height: Math.min(width, height) }; } return size; } // src/layout/layout-columns.ts async function layoutColumnsContent(block, box, ctx) { const children = []; let remainingWidth = box.width; let maxColHeight = 0; const layoutColumn = async (column, idx) => { const marginX = column.margin ? column.margin.left + column.margin.right : 0; const marginY = column.margin ? column.margin.top + column.margin.bottom : 0; const width = column.width ?? (column.autoWidth ? remainingWidth : remainingColWidth) - marginX; const height = column.height ?? box.height; const colBox = { x: 0, y: 0, width, height }; const autoWidth = column.width == null && (column.autoWidth || block.autoWidth); const { frame } = await layoutBlock( { ...column, autoWidth, breakInside: "avoid" }, colBox, ctx ); children[idx] = frame; remainingWidth -= frame.width + marginX; maxColHeight = Math.max(maxColHeight, frame.height + marginY); }; for (const [idx, column] of block.columns.entries()) { if (column.width != null) { await layoutColumn(column, idx); } } for (const [idx, column] of block.columns.entries()) { if (column.autoWidth) { await layoutColumn(column, idx); } } const remainingColCount = block.columns.filter((_, idx) => !children[idx]).length; const remainingColWidth = remainingColCount ? Math.max(0, remainingWidth) / remainingColCount : 0; for (const [idx, column] of block.columns.entries()) { if (!children[idx]) { await layoutColumn(column, idx); } } let colX = box.x; block.columns.forEach((column, idx) => { const child = children[idx]; colX += column.margin?.left ?? 0; child.x = colX; colX += child.width + (column.margin?.right ?? 0); }); const intrinsicWidth = colX - box.x; block.columns.forEach((column, idx) => { const child = children[idx]; const marginY = column.margin ? column.margin.top + column.margin.bottom : 0; child.y = box.y + (column.margin?.top ?? 0); if (column.verticalAlign === "middle") { child.y += (maxColHeight - child.height - marginY) / 2; } else if (column.verticalAlign === "bottom") { child.y += maxColHeight - child.height - marginY; } }); return { frame: { children, width: block.autoWidth ? intrinsicWidth : box.width, height: maxColHeight } }; } // src/layout/layout-image.ts async function layoutImageContent(block, box, ctx) { const image2 = await ctx.imageLoader(block.image); const hasFixedWidth = block.width != null; const hasFixedHeight = block.height != null; const scale = getScale(image2, box, hasFixedWidth, hasFixedHeight); const imageSize = { width: image2.width * scale, height: image2.height * scale }; const width = block.autoWidth ? imageSize.width : box.width; const imageBox = { x: box.x, y: box.y, width, height: imageSize.height }; const imagePos = align(imageBox, imageSize, block.imageAlign); const imageObj = createImageObject(image2, imagePos, imageSize); const objects = [imageObj]; return { frame: { objects, width, height: imageSize.height } }; } function getScale(image2, box, fixedWidth, fixedHeight) { const xScale = box.width / image2.width; const yScale = box.height / image2.height; if (fixedWidth && fixedHeight) return Math.min(xScale, yScale); if (fixedWidth) return xScale; if (fixedHeight) return yScale; return Math.min(xScale, 1); } function align(box, size, alignment) { const space = { width: box.width - size.width, height: box.height - size.height }; const is = (a) => alignment === a; const xShift = is("left") ? 0 : is("right") ? space.width : space.width / 2; const yShift = is("top") ? 0 : is("bottom") ? space.height : space.height / 2; return { x: box.x + xShift, y: box.y + yShift }; } function createImageObject(image2, pos, size) { return { type: "image", image: image2, x: pos.x, y: pos.y, width: size.width, height: size.height }; } // src/layout/layout-rows.ts async function layoutRowsContent(block, box, ctx) { let rowY = box.y; let lastMargin = 0; let remainingHeight = box.height; let aggregatedHeight = 0; const aggregatedHeights = []; let lastBreakOpportunity = -1; const frames = []; let remainingRows = []; for (const [rowIdx, row] of block.rows.entries()) { const margin = row.margin ?? ZERO_EDGES; const topMargin = Math.max(lastMargin, margin.top); const nextPos = { x: box.x + margin.left, y: rowY + topMargin }; const maxSize = { width: box.width - margin.left - margin.right, height: remainingHeight - topMargin - margin.bottom }; const autoWidth = row.width == null && (row.autoWidth || block.autoWidth); const { frame, remainder: remainder2 } = await layoutBlock( { ...row, autoWidth }, { ...nextPos, ...maxSize }, ctx ); const performBreakAt = (breakIdx, remainder3) => { frames.splice(breakIdx); const insertedBlock = block.insertAfterBreak?.(); remainingRows = compact([insertedBlock, remainder3, ...block.rows.slice(breakIdx)]); }; if (frame.height + topMargin + margin.bottom > remainingHeight) { if (lastBreakOpportunity >= 0) { performBreakAt(lastBreakOpportunity + 1); break; } else if (block.breakInside === "enforce-auto") { if (rowIdx > 0) { performBreakAt(rowIdx); break; } } } frames.push(frame); lastMargin = margin.bottom; aggregatedHeight += topMargin + frame.height; aggregatedHeights.push(aggregatedHeight + lastMargin); if (remainder2) { performBreakAt(rowIdx + 1, remainder2); break; } if (row.breakAfter === "always" || block.rows[rowIdx + 1]?.breakBefore === "always") { performBreakAt(rowIdx + 1); break; } rowY += topMargin + frame.height; remainingHeight -= topMargin + frame.height; if (block.breakInside !== "avoid" && isBreakPossible(block.rows, rowIdx)) { lastBreakOpportunity = rowIdx; } } const remainder = remainingRows.length ? ( // do not include the id in the remainder to avoid duplicate anchors { ...omit(block, "id", "breakInside"), rows: remainingRows } ) : void 0; const width = block.autoWidth ? Math.max( ...frames.map((frame, idx) => { const row = block.rows[idx]; const marginX = row.margin ? row.margin.left + row.margin.right : 0; return frame.width + marginX; }) ) : box.width; return { frame: { width, height: aggregatedHeights[frames.length - 1] ?? 0, children: frames }, remainder }; } // src/text.ts import { getTextWidth } from "@ralfstx/pdf-core"; // src/language-tags.gen.ts var langSysTagMap = { aa: "AFR", ab: "ABK", af: "AFK", ak: "AKA", am: "AMH", an: "ARG", ar: "ARA", as: "ASM", av: "AVR", ay: "AYM", az: "AZE", ba: "BSH", be: "BEL", bg: "BGR", bi: "BIS", bm: "BMB", bn: "BEN", bo: "TIB", br: "BRE", bs: "BOS", ca: "CAT", ce: "CHE", ch: "CHA", co: "COS", cr: "CRE", cs: "CSY", cu: "CSL", cv: "CHU", cy: "WEL", da: "DAN", de: "DEU", dv: "DIV", dz: "DZN", ee: "EWE", el: "ELL", en: "ENG", eo: "NTO", es: "ESP", et: "ETI", eu: "EUQ", fa: "FAR", ff: "FUL", fi: "FIN", fj: "FJI", fo: "FOS", fr: "FRA", fy: "FRI", ga: "IRI", gd: "GAE", gl: "GAL", gn: "GUA", gu: "GUJ", gv: "MNX", ha: "HAU", he: "IWR", hi: "HIN", ho: "HMO", hr: "HRV", ht: "HAI", hu: "HUN", hy: "HYE", hz: "HER", ia: "INA", id: "IND", ie: "ILE", ig: "IBO", ii: "YIM", ik: "IPK", io: "IDO", is: "ISL", it: "ITA", iu: "INU", ja: "JAN", jv: "JAV", ka: "KAT", ki: "KIK", kj: "KUA", kk: "KAZ", kl: "GRN", km: "KHM", kn: "KAN", ko: "KOR", kr: "KNR", ks: "KSH", ku: "KUR", kv: "KOM", kw: "COR", ky: "KIR", la: "LAT", lb: "LTZ", lg: "LUG", li: "LIM", ln: "LIN", lo: "LAO", lt: "LTH", lu: "LUB", lv: "LVI", mg: "MLG", mh: "MAH", mi: "MRI", mk: "MKD", ml: "MAL", mn: "MNG", mr: "MAR", ms: "MLY", mt: "MTS", my: "BRM", na: "NAU", nb: "NOR", nd: "NDB", ne: "NEP", ng: "NDG", nl: "NLD", nn: "NYN", no: "NOR", nr: "NDB", nv: "NAV", ny: "CHI", oc: "OCI", oj: "OJB", om: "ORO", or: "ORI", os: "OSS", pa: "PAN", pi: "PAL", pl: "PLK", ps: "PAS", pt: "PTG", qu: "QUZ", rm: "RMS", rn: "RUN", ro: "ROM", ru: "RUS", rw: "RUA", sa: "SAN", sc: "SRD", sd: "SND", se: "NSM", sg: "SGO", sh: "BOS", si: "SNH", sk: "SKY", sl: "SLV", sm: "SMO", so: "SML", sq: "SQI", sr: "SRB", ss: "SWZ", st: "SOT", su: "SUN", sv: "SVE", sw: "SWK", ta: "TAM", te: "TEL", tg: "TAJ", th: "THA", ti: "TGY", tk: "TKM", tl: "TGL", tn: "TNA", to: "TGN", tr: "TRK", ts: "TSG", tt: "TAT", tw: "TWI", ty: "THT", ug: "UYG", uk: "UKR", ur: "URD", uz: "UZB", ve: "VEN", vi: "VIT", vo: "VOL", wa: "WLN", wo: "WLF", xh: "XHS", yi: "JII", yo: "YBA", za: "ZHA", zh: "ZHS", zu: "ZUL" }; function languageToOpenTypeTag(language) { const primary = language.split("-")[0].toLowerCase(); return langSysTagMap[primary]; } // src/script-ranges.gen.ts var scriptRanges = [ { start: 0, end: 64, script: "Common" }, { start: 65, end: 90, script: "Latin" }, { start: 91, end: 96, script: "Common" }, { start: 97, end: 122, script: "Latin" }, { start: 123, end: 169, script: "Common" }, { start: 170, end: 170, script: "Latin" }, { start: 171, end: 185, script: "Common" }, { start: 186, end: 186, script: "Latin" }, { start: 187, end: 191, script: "Common" }, { start: 192, end: 214, script: "Latin" }, { start: 215, end: 215, script: "Common" }, { start: 216, end: 246, script: "Latin" }, { start: 247, end: 247, script: "Common" }, { start: 248, end: 696, script: "Latin" }, { start: 697, end: 735, script: "Common" }, { start: 736, end: 740, script: "Latin" }, { start: 741, end: 745, script: "Common" }, { start: 748, end: 767, script: "Common" }, { start: 768, end: 879, script: "Inherited" }, { start: 880, end: 883, script: "Greek" }, { start: 884, end: 884, script: "Common" }, { start: 885, end: 887, script: "Greek" }, { start: 890, end: 893, script: "Greek" }, { start: 894, end: 894, script: "Common" }, { start: 895, end: 895, script: "Greek" }, { start: 900, end: 900, script: "Greek" }, { start: 901, end: 901, script: "Common" }, { start: 902, end: 902, script: "Greek" }, { start: 903, end: 903, script: "Common" }, { start: 904, end: 906, script: "Greek" }, { start: 908, end: 908, script: "Greek" }, { start: 910, end: 929, script: "Greek" }, { start: 931, end: 993, script: "Greek" }, { start: 1008, end: 1023, script: "Greek" }, { start: 1024, end: 1156, script: "Cyrillic" }, { start: 1157, end: 1158, script: "Inherited" }, { start: 1159, end: 1327, script: "Cyrillic" }, { start: 1329, end: 1366, script: "Armenian" }, { start: 1369, end: 1418, script: "Armenian" }, { start: 1421, end: 1423, script: "Armenian" }, { start: 1425, end: 1479, script: "Hebrew" }, { start: 1488, end: 1514, script: "Hebrew" }, { start: 1519, end: 1524, script: "Hebrew" }, { start: 1536, end: 1540, script: "Arabic" }, { start: 1541, end: 1541, script: "Common" }, { start: 1542, end: 1547, script: "Arabic" }, { start: 1548, end: 1548, script: "Common" }, { start: 1549, end: 1562, script: "Arabic" }, { start: 1563, end: 1563, script: "Common" }, { start: 1564, end: 1566, script: "Arabic" }, { start: 1567, end: 1567, script: "Common" }, { start: 1568, end: 1599, script: "Arabic" }, { start: 1600, end: 1600, script: "Common" }, { start: 1601, end: 1610, script: "Arabic" }, { start: 1611, end: 1621, script: "Inherited" }, { start: 1622, end: 1647, script: "Arabic" }, { start: 1648, end: 1648, script: "Inherited" }, { start: 1649, end: 1756, script: "Arabic" }, { start: 1757, end: 1757, script: "Common" }, { start: 1758, end: 1791, script: "Arabic" }, { start: 1872, end: 1919, script: "Arabic" }, { start: 2160, end: 2190, script: "Arabic" }, { start: 2192, end: 2193, script: "Arabic" }, { start: 2200, end: 2273, script: "Arabic" }, { start: 2274, end: 2274, script: "Common" }, { start: 2275, end: 2303, script: "Arabic" }, { start: 2304, end: 2384, script: "Devanagari" }, { start: 2385, end: 2388, script: "Inherited" }, { start: 2389, end: 2403, script: "Devanagari" }, { start: 2404, end: 2405, script: "Common" }, { start: 2406, end: 2431, script: "Devanagari" }, { start: 2432, end: 2435, script: "Bengali" }, { start: 2437, end: 2444, script: "Bengali" }, { start: 2447, end: 2448, script: "Bengali" }, { start: 2451, end: 2472, script: "Bengali" }, { start: 2474, end: 2480, script: "Bengali" }, { start: 2482, end: 2482, script: "Bengali" }, { start: 2486, end: 2489, script: "Bengali" }, { start: 2492, end: 2500, script: "Bengali" }, { start: 2503, end: 2504, script: "Bengali" }, { start: 2507, end: 2510, script: "Bengali" }, { start: 2519, end: 2519, script: "Bengali" }, { start: 2524, end: 2525, script: "Bengali" }, { start: 2527, end: 2531, script: "Bengali" }, { start: 2534, end: 2558, script: "Bengali" }, { start: 2561, end: 2563, script: "Gurmukhi" }, { start: 2565, end: 2570, script: "Gurmukhi" }, { start: 2575, end: 2576, script: "Gurmukhi" }, { start: 2579, end: 2600, script: "Gurmukhi" }, { start: 2602, end: 2608, script: "Gurmukhi" }, { start: 2610, end: 2611, script: "Gurmukhi" }, { start: 2613, end: 2614, script: "Gurmukhi" }, { start: 2616, end: 2617, script: "Gurmukhi" }, { start: 2620, end: 2620, script: "Gurmukhi" }, { start: 2622, end: 2626, script: "Gurmukhi" }, { start: 2631, end: 2632, script: "Gurmukhi" }, { start: 2635, end: 2637, script: "Gurmukhi" }, { start: 2641, end: 2641, script: "Gurmukhi" }, { start: 2649, end: 2652, script: "Gurmukhi" }, { start: 2654, end: 2654, script: "Gurmukhi" }, { start: 2662, end: 2678, script: "Gurmukhi" }, { start: 2689, end: 2691, script: "Gujarati" }, { start: 2693, end: 2701, script: "Gujarati" }, { start: 2703, end: 2705, script: "Gujarati" }, { start: 2707, end: 2728, script: "Gujarati" }, { start: 2730, end: 2736, script: "Gujarati" }, { start: 2738, end: 2739, script: "Gujarati" }, { start: 2741, end: 2745, script: "Gujarati" }, { start: 2748, end: 2757, script: "Gujarati" }, { start: 2759, end: 2761, script: "Gujarati" }, { start: 2763, end: 2765, script: "Gujarati" }, { start: 2768, end: 2768, script: "Gujarati" }, { start: 2784, end: 2787, script: "Gujarati" }, { start: 2790, end: 2801, script: "Gujarati" }, { start: 2809, end: 2815, script: "Gujarati" }, { start: 2946, end: 2947, script: "Tamil" }, { start: 2949, end: 2954, script: "Tamil" }, { start: 2958, end: 2960, script: "Tamil" }, { start: 2962, end: 2965, script: "Tamil" }, { start: 2969, end: 2970, script: "Tamil" }, { start: 2972, end: 2972, script: "Tamil" }, { start: 2974, end: 2975, script: "Tamil" }, { start: 2979, end: 2980, script: "Tamil" }, { start: 2984, end: 2986, script: "Tamil" }, { start: 2990, end: 3001, script: "Tamil" }, { start: 3006, end: 3010, script: "Tamil" }, { start: 3014, end: 3016, script: "Tamil" }, { start: 3018, end: 3021, script: "Tamil" }, { start: 3024, end: 3024, script: "Tamil" }, { start: 3031, end: 3031, script: "Tamil" }, { start: 3046, end: 3066, script: "Tamil" }, { start: 3072, end: 3084, script: "Telugu" }, { start: 3086, end: 3088, script: "Telugu" }, { start: 3090, end: 3112, script: "Telugu" }, { start: 3114, end: 3129, script: "Telugu" }, { start: 3132, end: 3140, script: "Telugu" }, { start: 3142, end: 3144, script: "Telugu" }, { start: 3146, end: 3149, script: "Telugu" }, { start: 3157, end: 3158, script: "Telugu" }, { start: 3160, end: 3162, script: "Telugu" }, { start: 3165, end: 3165, script: "Telugu" }, { start: 3168, end: 3171, script: "Telugu" }, { start: 3174, end: 3183, script: "Telugu" }, { start: 3191, end: 3199, script: "Telugu" }, { start: 3200, end: 3212, script: "Kannada" }, { start: 3214, end: 3216, script: "Kannada" }, { start: 3218, end: 3240, script: "Kannada" }, { start: 3242, end: 3251, script: "Kannada" }, { start: 3253, end: 3257, script: "Kannada" }, { start: 3260, end: 3268, script: "Kannada" }, { start: 3270, end: 3272, script: "Kannada" }, { start: 3274, end: 3277, script: "Kannada" }, { start: 3285, end: 3286, script: "Kannada" }, { start: 3293, end: 3294, script: "Kannada" }, { start: 3296, end: 3299, script: "Kannada" }, { start: 3302, end: 3311, script: "Kannada" }, { start: 3313, end: 3315, script: "Kannada" }, { start: 3328, end: 3340, script: "Malayalam" }, { start: 3342, end: 3344, script: "Malayalam" }, { start: 3346, end: 3396, script: "Malayalam" }, { start: 3398, end: 3400, script: "Malayalam" }, { start: 3402, end: 3407, script: "Malayalam" }, { start: 3412, end: 3427, script: "Malayalam" }, { start: 3430, end: 3455, script: "Malayalam" }, { start: 3585, end: 3642, script: "Thai" }, { start: 3647, end: 3647, script: "Common" }, { start: 3648, end: 3675, script: "Thai" }, { start: 4053, end: 4056, script: "Common" }, { start: 4256, end: 4293, script: "Georgian" }, { start: 4295, end: 4295, script: "Georgian" }, { start: 4301, end: 4301, script: "Georgian" }, { start: 4304, end: 4346, script: "Georgian" }, { start: 4347, end: 4347, script: "Common" }, { start: 4348, end: 4351, script: "Georgian" }, { start: 4352, end: 4607, script: "Hangul" }, { start: 5867, end: 5869, script: "Common" }, { start: 5941, end: 5942, script: "Common" }, { start: 6146, end: 6147, script: "Common" }, { start: 6149, end: 6149, script: "Common" }, { start: 6832, end: 6862, script: "Inherited" }, { start: 7296, end: 7304, script: "Cyrillic" }, { start: 7312, end: 7354, script: "Georgian" }, { start: 7357, end: 7359, script: "Georgian" }, { start: 7376, end: 7378, script: "Inherited" }, { start: 7379, end: 7379, script: "Common" }, { start: 7380, end: 7392, script: "Inherited" }, { start: 7393, end: 7393, script: "Common" }, { start: 7394, end: 7400, script: "Inherited" }, { start: 7401, end: 7404, script: "Common" }, { start: 7405, end: 7405, script: "Inherited" }, { start: 7406, end: 7411, script: "Common" }, { start: 7412, end: 7412, script: "Inherited" }, { start: 7413, end: 7415, script: "Common" }, { start: 7416, end: 7417, script: "Inherited" }, { start: 7418, end: 7418, script: "Common" }, { start: 7424, end: 7461, script: "Latin" }, { start: 7462, end: 7466, script: "Greek" }, { start: 7467, end: 7467, script: "Cyrillic" }, { start: 7468, end: 7516, script: "Latin" }, { start: 7517, end: 7521, script: "Greek" }, { start: 7522, end: 7525, script: "Latin" }, { start: 7526, end: 7530, script: "Greek" }, { start: 7531, end: 7543, script: "Latin" }, { start: 7544, end: 7544, script: "Cyrillic" }, { start: 7545, end: 7614, script: "Latin" }, { start: 7615, end: 7615, script: "Greek" }, { start: 7616, end: 7679, script: "Inherited" }, { start: 7680, end: 7935, script: "Latin" }, { start: 7936, end: 7957, script: "Greek" }, { start: 7960, end: 7965, script: "Greek" }, { start: 7968, end: 8005, script: "Greek" }, { start: 8008, end: 8013, script: "Greek" }, { start: 8016, end: 8023, script: "Greek" }, { start: 8025, end: 8025, script: "Greek" }, { start: 8027, end: 8027, script: "Greek" }, { start: 8029, end: 8029, script: "Greek" }, { start: 8031, end: 8061, script: "Greek" }, { start: 8064, end: 8116, script: "Greek" }, { start: 8118, end: 8132, script: "Greek" }, { start: 8134, end: 8147, script: "Greek" }, { start: 8150, end: 8155, script: "Greek" }, { start: 8157, end: 8175, script: "Greek" }, { start: 8178, end: 8180, script: "Greek" }, { start: 8182, end: 8190, script: "Greek" }, { start: 8192, end: 8203, script: "Common" }, { start: 8204, end: 8205, script: "Inherited" }, { start: 8206, end: 8292, script: "Common" }, { start: 8294, end: 8304, script: "Common" }, { start: 8305, end: 8305, script: "Latin" }, { start: 8308, end: 8318, script: "Common" }, { start: 8319, end: 8319, script: "Latin" }, { start: 8320, end: 8334, script: "Common" }, { start: 8336, end: 8348, script: "Latin" }, { start: 8352, end: 8384, script: "Common" }, { start: 8400, end: 8432, script: "Inherited" }, { start: 8448, end: 8485, script: "Common" }, { start: 8486, end: 8486, script: "Greek" }, { start: 8487, end: 8489, script: "Common" }, { start: 8490, end: 8491, script: "Latin" }, { start: 8492, end: 8497, script: "Common" }, { start: 8498, end: 8498, script: "Latin" }, { start: 8499, end: 8525, script: "Common" }, { start: 8526, end: 8526, script: "Latin" }, { start: 8527, end: 8543, script: "Common" }, { start: 8544, end: 8584, script: "Latin" }, { start: 8585, end: 8587, script: "Common" }, { start: 8592, end: 9254, script: "Common" }, { start: 9280, end: 9290, script: "Common" }, { start: 9312, end: 10239, script: "Common" }, { start: 10496, end: 11123, script: "Common" }, { start: 11126, end: 11157, script: "Common" }, { start: 11159, end: 11263, script: "Common" }, { start: 11360, end: 11391, script: "Latin" }, { start: 11520, end: 11557, script: "Georgian" }, { start: 11559, end: 11559, script: "Georgian" }, { start: 11565, end: 11565, script: "Georgian" }, { start: 11744, end: 11775, script: "Cyrillic" }, { start: 11776, end: 11869, script: "Common" }, { start: 11904, end: 11929, script: "Han" }, { start: 11931, end: 12019, script: "Han" }, { start: 12032, end: 12245, script: "Han" }, { start: 12272, end: 12292, script: "Common" }, { start: 12293, end: 12293, script: "Han" }, { start: 12294, end: 12294, script: "Common" }, { start: 12295, end: 12295, script: "Han" }, { start: 12296, end: 12320, script: "Common" }, { start: 12321, end: 12329, script: "Han" }, { start: 12330, end: 12333, script: "Inherited" }, { start: 12334, end: 12335, script: "Hangul" }, { start: 12336, end: 12343, script: "Common" }, { start: 12344, end: 12347, script: "Han" }, { start: 12348, end: 12351, script: "Common" }, { start: 12353, end: 12438, script: "Hiragana" }, { start: 12441, end: 12442, script: "Inherited" }, { start: 12443, end: 12444, script: "Common" }, { start: 12445, end: 12447, script: "Hiragana" }, { start: 12448, end: 12448, script: "Common" }, { start: 12449, end: 12538, script: "Katakana" }, { start: 12539, end: 12540, script: "Common" }, { start: 12541, end: 12543, script: "Katakana" }, { start: 12593, end: 12686, script: "Hangul" }, { start: 12688, end: 12703, script: "Common" }, { start: 12736, end: 12771, script: "Common" }, { start: 12783, end: 12783, script: "Common" }, { start: 12784, end: 12799, script: "Katakana" }, { start: 12800, end: 12830, script: "Hangul" }, { start: 12832, end: 12895, script: "Common" }, {