pdfmkr
Version:
Generate PDF documents from JavaScript objects
1,454 lines (1,426 loc) • 112 kB
JavaScript
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" },
{