printmaker
Version:
Generate PDF documents and from JavaScript objects
95 lines (87 loc) • 2.82 kB
text/typescript
import { isObject, Obj, typeError } from './types.js';
export type Pos = { x: number; y: number };
export type Size = { width: number; height: number };
export type Box = Size & Pos;
export type BoxEdges = {
left: number;
right: number;
top: number;
bottom: number;
};
export const ZERO_EDGES = Object.freeze(parseEdges(0));
/**
* Computes an inner box by subtracting the given space (e.g. a padding) from the given box.
*
* @param box The outer box.
* @param edges The space to subtract.
* @returns The resulting inner box.
*/
export function subtractEdges(box: Box, edges: BoxEdges) {
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)),
};
}
/**
* Parses a given value as a box lengths definition (`BoxLengths | Length`) to the lengths of the
* edges.
* Throws if the given input is invalid.
*
* @param input the input value
* @returns an object with the four edges in `pt`, or `undefined` if the input is missing or `null`
*/
export function parseEdges(input: unknown): BoxEdges {
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 as Obj;
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);
}
/**
* Converts a length definition into the corresponding value in points (`pt`).
* Throws if the given value is not a valid length.
*
* @param input the input value
* @returns the length in `pt`, or `undefined` if the input is missing or `null`
*/
export function parseLength(input: unknown): number {
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: number, fromUnit: string): number {
// 1in = 72pt = 25.4mm
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}'`);
}
}