@pdfme/schemas
Version:
TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!
153 lines (144 loc) • 4.47 kB
text/typescript
import { Plugin, Schema, mm2pt } from '@pdfme/common';
import { HEX_COLOR_PATTERN } from '../constants.js';
import { hex2PrintingColor, convertForPdfLayoutProps, createSvgStr } from '../utils.js';
import { toRadians } from '@pdfme/pdf-lib';
import { Circle, Square } from 'lucide';
interface ShapeSchema extends Schema {
type: 'ellipse' | 'rectangle';
borderWidth: number;
borderColor: string;
color: string;
radius?: number;
}
const shape: Plugin<ShapeSchema> = {
ui: (arg) => {
const { schema, rootElement } = arg;
const div = document.createElement('div');
div.style.width = '100%';
div.style.height = '100%';
div.style.boxSizing = 'border-box';
if (schema.type === 'ellipse') {
div.style.borderRadius = '50%';
} else if (schema.radius && schema.radius > 0) {
div.style.borderRadius = `${schema.radius}mm`;
}
div.style.borderWidth = `${schema.borderWidth ?? 0}mm`;
div.style.borderStyle = schema.borderWidth && schema.borderColor ? 'solid' : 'none';
div.style.borderColor = schema.borderColor ?? 'transparent';
div.style.backgroundColor = schema.color ?? 'transparent';
rootElement.appendChild(div);
},
pdf: (arg) => {
const { schema, page, options } = arg;
if (!schema.color && !schema.borderColor) return;
const { colorType } = options;
const pageHeight = page.getHeight();
const cArg = { schema, pageHeight };
const { position, width, height, rotate, opacity } = convertForPdfLayoutProps(cArg);
const {
position: { x: x4Ellipse, y: y4Ellipse },
} = convertForPdfLayoutProps({ ...cArg, applyRotateTranslate: false });
const borderWidth = schema.borderWidth ? mm2pt(schema.borderWidth) : 0;
const drawOptions = {
rotate,
borderWidth,
borderColor: hex2PrintingColor(schema.borderColor, colorType),
color: hex2PrintingColor(schema.color, colorType),
opacity,
borderOpacity: opacity,
};
if (schema.type === 'ellipse') {
page.drawEllipse({
x: x4Ellipse + width / 2,
y: y4Ellipse + height / 2,
xScale: width / 2 - borderWidth / 2,
yScale: height / 2 - borderWidth / 2,
...drawOptions,
});
} else if (schema.type === 'rectangle') {
const radius = schema.radius ?? 0;
page.drawRectangle({
x:
position.x +
borderWidth * ((1 - Math.sin(toRadians(rotate))) / 2) +
Math.tan(toRadians(rotate)) * Math.PI ** 2,
y:
position.y +
borderWidth * ((1 + Math.sin(toRadians(rotate))) / 2) +
Math.tan(toRadians(rotate)) * Math.PI ** 2,
width: width - borderWidth,
height: height - borderWidth,
...(radius ? { radius: mm2pt(radius) } : {}),
...drawOptions,
});
}
},
propPanel: {
schema: ({ i18n }) => ({
borderWidth: {
title: i18n('schemas.borderWidth'),
type: 'number',
widget: 'inputNumber',
props: { min: 0, step: 1 },
span: 12,
},
borderColor: {
title: i18n('schemas.borderColor'),
type: 'string',
widget: 'color',
props: {
disabledAlpha: true,
},
rules: [{ pattern: HEX_COLOR_PATTERN, message: i18n('validation.hexColor') }],
span: 12,
},
color: {
title: i18n('schemas.color'),
type: 'string',
widget: 'color',
props: {
disabledAlpha: true,
},
rules: [{ pattern: HEX_COLOR_PATTERN, message: i18n('validation.hexColor') }],
},
radius: {
title: i18n('schemas.radius'),
type: 'number',
widget: 'inputNumber',
props: { min: 0, step: 1 },
span: 12,
},
}),
defaultSchema: {
name: '',
type: 'rectangle',
position: { x: 0, y: 0 },
width: 62.5,
height: 37.5,
rotate: 0,
opacity: 1,
borderWidth: 1,
borderColor: '#000000',
color: '',
readOnly: true,
radius: 0,
},
},
};
const getPropPanelSchema = (type: 'rectangle' | 'ellipse') => ({
...shape.propPanel,
defaultSchema: {
...(shape.propPanel.defaultSchema as ShapeSchema),
type,
},
});
export const rectangle = {
...shape,
propPanel: getPropPanelSchema('rectangle'),
icon: createSvgStr(Square),
};
export const ellipse = {
...shape,
propPanel: getPropPanelSchema('ellipse'),
icon: createSvgStr(Circle),
};