@pdfme/schemas 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +31 -0
- package/dist/cjs/__tests__/barcode.test.js +341 -0
- package/dist/cjs/__tests__/barcode.test.js.map +1 -0
- package/dist/cjs/__tests__/text.test.js +318 -0
- package/dist/cjs/__tests__/text.test.js.map +1 -0
- package/dist/cjs/src/barcodes/constants.js +19 -0
- package/dist/cjs/src/barcodes/constants.js.map +1 -0
- package/dist/cjs/src/barcodes/helper.js +147 -0
- package/dist/cjs/src/barcodes/helper.js.map +1 -0
- package/dist/cjs/src/barcodes/index.js +11 -0
- package/dist/cjs/src/barcodes/index.js.map +1 -0
- package/dist/cjs/src/barcodes/pdfRender.js +37 -0
- package/dist/cjs/src/barcodes/pdfRender.js.map +1 -0
- package/dist/cjs/src/barcodes/propPanel.js +69 -0
- package/dist/cjs/src/barcodes/propPanel.js.map +1 -0
- package/dist/cjs/src/barcodes/types.js +3 -0
- package/dist/cjs/src/barcodes/types.js.map +1 -0
- package/dist/cjs/src/barcodes/uiRender.js +92 -0
- package/dist/cjs/src/barcodes/uiRender.js.map +1 -0
- package/dist/cjs/src/image/constants.js +3 -0
- package/dist/cjs/src/image/constants.js.map +1 -0
- package/dist/cjs/src/image/helper.js +3 -0
- package/dist/cjs/src/image/helper.js.map +1 -0
- package/dist/cjs/src/image/index.js +8 -0
- package/dist/cjs/src/image/index.js.map +1 -0
- package/dist/cjs/src/image/pdfRender.js +34 -0
- package/dist/cjs/src/image/pdfRender.js.map +1 -0
- package/dist/cjs/src/image/propPanel.js +9 -0
- package/dist/cjs/src/image/propPanel.js.map +1 -0
- package/dist/cjs/src/image/types.js +3 -0
- package/dist/cjs/src/image/types.js.map +1 -0
- package/dist/cjs/src/image/uiRender.js +108 -0
- package/dist/cjs/src/image/uiRender.js.map +1 -0
- package/dist/cjs/src/index.js +13 -0
- package/dist/cjs/src/index.js.map +1 -0
- package/dist/cjs/src/renderUtils.js +65 -0
- package/dist/cjs/src/renderUtils.js.map +1 -0
- package/dist/cjs/src/text/constants.js +22 -0
- package/dist/cjs/src/text/constants.js.map +1 -0
- package/dist/cjs/src/text/helper.js +270 -0
- package/dist/cjs/src/text/helper.js.map +1 -0
- package/dist/cjs/src/text/index.js +8 -0
- package/dist/cjs/src/text/index.js.map +1 -0
- package/dist/cjs/src/text/pdfRender.js +111 -0
- package/dist/cjs/src/text/pdfRender.js.map +1 -0
- package/dist/cjs/src/text/propPanel.js +122 -0
- package/dist/cjs/src/text/propPanel.js.map +1 -0
- package/dist/cjs/src/text/types.js +3 -0
- package/dist/cjs/src/text/types.js.map +1 -0
- package/dist/cjs/src/text/uiRender.js +118 -0
- package/dist/cjs/src/text/uiRender.js.map +1 -0
- package/dist/esm/__tests__/barcode.test.js +336 -0
- package/dist/esm/__tests__/barcode.test.js.map +1 -0
- package/dist/esm/__tests__/text.test.js +293 -0
- package/dist/esm/__tests__/text.test.js.map +1 -0
- package/dist/esm/src/barcodes/constants.js +16 -0
- package/dist/esm/src/barcodes/constants.js.map +1 -0
- package/dist/esm/src/barcodes/helper.js +137 -0
- package/dist/esm/src/barcodes/helper.js.map +1 -0
- package/dist/esm/src/barcodes/index.js +9 -0
- package/dist/esm/src/barcodes/index.js.map +1 -0
- package/dist/esm/src/barcodes/pdfRender.js +33 -0
- package/dist/esm/src/barcodes/pdfRender.js.map +1 -0
- package/dist/esm/src/barcodes/propPanel.js +65 -0
- package/dist/esm/src/barcodes/propPanel.js.map +1 -0
- package/dist/esm/src/barcodes/types.js +2 -0
- package/dist/esm/src/barcodes/types.js.map +1 -0
- package/dist/esm/src/barcodes/uiRender.js +88 -0
- package/dist/esm/src/barcodes/uiRender.js.map +1 -0
- package/dist/esm/src/image/constants.js +2 -0
- package/dist/esm/src/image/constants.js.map +1 -0
- package/dist/esm/src/image/helper.js +2 -0
- package/dist/esm/src/image/helper.js.map +1 -0
- package/dist/esm/src/image/index.js +6 -0
- package/dist/esm/src/image/index.js.map +1 -0
- package/dist/esm/src/image/pdfRender.js +30 -0
- package/dist/esm/src/image/pdfRender.js.map +1 -0
- package/dist/esm/src/image/propPanel.js +6 -0
- package/dist/esm/src/image/propPanel.js.map +1 -0
- package/dist/esm/src/image/types.js +2 -0
- package/dist/esm/src/image/types.js.map +1 -0
- package/dist/esm/src/image/uiRender.js +104 -0
- package/dist/esm/src/image/uiRender.js.map +1 -0
- package/dist/esm/src/index.js +7 -0
- package/dist/esm/src/index.js.map +1 -0
- package/dist/esm/src/renderUtils.js +56 -0
- package/dist/esm/src/renderUtils.js.map +1 -0
- package/dist/esm/src/text/constants.js +19 -0
- package/dist/esm/src/text/constants.js.map +1 -0
- package/dist/esm/src/text/helper.js +237 -0
- package/dist/esm/src/text/helper.js.map +1 -0
- package/dist/esm/src/text/index.js +6 -0
- package/dist/esm/src/text/index.js.map +1 -0
- package/dist/esm/src/text/pdfRender.js +107 -0
- package/dist/esm/src/text/pdfRender.js.map +1 -0
- package/dist/esm/src/text/propPanel.js +119 -0
- package/dist/esm/src/text/propPanel.js.map +1 -0
- package/dist/esm/src/text/types.js +2 -0
- package/dist/esm/src/text/types.js.map +1 -0
- package/dist/esm/src/text/uiRender.js +114 -0
- package/dist/esm/src/text/uiRender.js.map +1 -0
- package/dist/types/__tests__/barcode.test.d.ts +1 -0
- package/dist/types/__tests__/text.test.d.ts +1 -0
- package/dist/types/src/barcodes/constants.d.ts +3 -0
- package/dist/types/src/barcodes/helper.d.ts +20 -0
- package/dist/types/src/barcodes/index.d.ts +4 -0
- package/dist/types/src/barcodes/pdfRender.d.ts +3 -0
- package/dist/types/src/barcodes/propPanel.d.ts +3 -0
- package/dist/types/src/barcodes/types.d.ts +9 -0
- package/dist/types/src/barcodes/uiRender.d.ts +3 -0
- package/dist/types/src/image/constants.d.ts +1 -0
- package/dist/types/src/image/helper.d.ts +1 -0
- package/dist/types/src/image/index.d.ts +4 -0
- package/dist/types/src/image/pdfRender.d.ts +3 -0
- package/dist/types/src/image/propPanel.d.ts +3 -0
- package/dist/types/src/image/types.d.ts +3 -0
- package/dist/types/src/image/uiRender.d.ts +3 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/renderUtils.d.ts +16 -0
- package/dist/types/src/text/constants.d.ts +19 -0
- package/dist/types/src/text/helper.d.ts +29 -0
- package/dist/types/src/text/index.d.ts +4 -0
- package/dist/types/src/text/pdfRender.d.ts +3 -0
- package/dist/types/src/text/propPanel.d.ts +3 -0
- package/dist/types/src/text/types.d.ts +26 -0
- package/dist/types/src/text/uiRender.d.ts +3 -0
- package/package.json +86 -0
- package/src/barcodes/constants.ts +17 -0
- package/src/barcodes/helper.ts +161 -0
- package/src/barcodes/index.ts +16 -0
- package/src/barcodes/pdfRender.ts +29 -0
- package/src/barcodes/propPanel.ts +133 -0
- package/src/barcodes/types.ts +11 -0
- package/src/barcodes/uiRender.ts +116 -0
- package/src/image/constants.ts +1 -0
- package/src/image/helper.ts +1 -0
- package/src/image/index.ts +8 -0
- package/src/image/pdfRender.ts +24 -0
- package/src/image/propPanel.ts +8 -0
- package/src/image/types.ts +3 -0
- package/src/image/uiRender.ts +118 -0
- package/src/index.ts +7 -0
- package/src/renderUtils.ts +74 -0
- package/src/text/constants.ts +22 -0
- package/src/text/helper.ts +317 -0
- package/src/text/index.ts +9 -0
- package/src/text/pdfRender.ts +155 -0
- package/src/text/propPanel.ts +148 -0
- package/src/text/types.ts +29 -0
- package/src/text/uiRender.ts +153 -0
- package/tsconfig.cjs.json +10 -0
- package/tsconfig.esm.json +10 -0
- package/tsconfig.json +6 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
import { PDFFont, PDFDocument } from '@pdfme/pdf-lib';
|
2
|
+
import { PDFRenderProps, Font, getDefaultFont, getFallbackFontName } from '@pdfme/common';
|
3
|
+
import type { TextSchema, FontWidthCalcValues } from './types';
|
4
|
+
import {
|
5
|
+
VERTICAL_ALIGN_TOP,
|
6
|
+
VERTICAL_ALIGN_MIDDLE,
|
7
|
+
VERTICAL_ALIGN_BOTTOM,
|
8
|
+
DEFAULT_FONT_SIZE,
|
9
|
+
DEFAULT_ALIGNMENT,
|
10
|
+
DEFAULT_VERTICAL_ALIGNMENT,
|
11
|
+
DEFAULT_LINE_HEIGHT,
|
12
|
+
DEFAULT_CHARACTER_SPACING,
|
13
|
+
DEFAULT_FONT_COLOR,
|
14
|
+
} from './constants';
|
15
|
+
import {
|
16
|
+
calculateDynamicFontSize,
|
17
|
+
heightOfFontAtSize,
|
18
|
+
getFontDescentInPt,
|
19
|
+
getFontKitFont,
|
20
|
+
getSplittedLines,
|
21
|
+
widthOfTextAtSize,
|
22
|
+
} from './helper';
|
23
|
+
import {
|
24
|
+
hex2RgbColor,
|
25
|
+
calcX,
|
26
|
+
calcY,
|
27
|
+
renderBackgroundColor,
|
28
|
+
convertSchemaDimensionsToPt,
|
29
|
+
} from '../renderUtils';
|
30
|
+
|
31
|
+
const embedAndGetFontObjCache = new WeakMap();
|
32
|
+
const embedAndGetFontObj = async (arg: { pdfDoc: PDFDocument; font: Font }) => {
|
33
|
+
const { pdfDoc, font } = arg;
|
34
|
+
if (embedAndGetFontObjCache.has(pdfDoc)) {
|
35
|
+
return embedAndGetFontObjCache.get(pdfDoc);
|
36
|
+
}
|
37
|
+
|
38
|
+
const fontValues = await Promise.all(
|
39
|
+
Object.values(font).map(async (v) => {
|
40
|
+
let fontData = v.data;
|
41
|
+
if (typeof fontData === 'string' && fontData.startsWith('http')) {
|
42
|
+
fontData = await fetch(fontData).then((res) => res.arrayBuffer());
|
43
|
+
}
|
44
|
+
return pdfDoc.embedFont(fontData, {
|
45
|
+
subset: typeof v.subset === 'undefined' ? true : v.subset,
|
46
|
+
});
|
47
|
+
})
|
48
|
+
);
|
49
|
+
|
50
|
+
const fontObj = Object.keys(font).reduce(
|
51
|
+
(acc, cur, i) => Object.assign(acc, { [cur]: fontValues[i] }),
|
52
|
+
{} as { [key: string]: PDFFont }
|
53
|
+
);
|
54
|
+
|
55
|
+
embedAndGetFontObjCache.set(pdfDoc, fontObj);
|
56
|
+
return fontObj;
|
57
|
+
};
|
58
|
+
|
59
|
+
const getFontProp = async ({
|
60
|
+
value,
|
61
|
+
font,
|
62
|
+
schema,
|
63
|
+
}: {
|
64
|
+
value: string;
|
65
|
+
font: Font;
|
66
|
+
schema: TextSchema;
|
67
|
+
}) => {
|
68
|
+
const fontSize = schema.dynamicFontSize
|
69
|
+
? await calculateDynamicFontSize({ textSchema: schema, font, value })
|
70
|
+
: schema.fontSize ?? DEFAULT_FONT_SIZE;
|
71
|
+
const color = hex2RgbColor(schema.fontColor || DEFAULT_FONT_COLOR);
|
72
|
+
|
73
|
+
return {
|
74
|
+
alignment: schema.alignment ?? DEFAULT_ALIGNMENT,
|
75
|
+
verticalAlignment: schema.verticalAlignment ?? DEFAULT_VERTICAL_ALIGNMENT,
|
76
|
+
lineHeight: schema.lineHeight ?? DEFAULT_LINE_HEIGHT,
|
77
|
+
characterSpacing: schema.characterSpacing ?? DEFAULT_CHARACTER_SPACING,
|
78
|
+
fontSize,
|
79
|
+
color,
|
80
|
+
};
|
81
|
+
};
|
82
|
+
|
83
|
+
export const pdfRender = async (arg: PDFRenderProps<TextSchema>) => {
|
84
|
+
const { value, pdfDoc, pdfLib, page, options, schema } = arg;
|
85
|
+
|
86
|
+
const { font = getDefaultFont() } = options;
|
87
|
+
|
88
|
+
const [pdfFontObj, fontKitFont, fontProp] = await Promise.all([
|
89
|
+
embedAndGetFontObj({ pdfDoc, font }),
|
90
|
+
getFontKitFont(schema, font),
|
91
|
+
getFontProp({ value, font, schema }),
|
92
|
+
]);
|
93
|
+
|
94
|
+
const { fontSize, color, alignment, verticalAlignment, lineHeight, characterSpacing } = fontProp;
|
95
|
+
|
96
|
+
const fontName = (
|
97
|
+
schema.fontName ? schema.fontName : getFallbackFontName(font)
|
98
|
+
) as keyof typeof pdfFontObj;
|
99
|
+
const pdfFontValue = pdfFontObj[fontName];
|
100
|
+
|
101
|
+
const pageHeight = page.getHeight();
|
102
|
+
renderBackgroundColor({ schema, page, pageHeight });
|
103
|
+
|
104
|
+
const { width, height, rotate } = convertSchemaDimensionsToPt(schema);
|
105
|
+
|
106
|
+
page.pushOperators(pdfLib.setCharacterSpacing(characterSpacing ?? DEFAULT_CHARACTER_SPACING));
|
107
|
+
|
108
|
+
const firstLineTextHeight = heightOfFontAtSize(fontKitFont, fontSize);
|
109
|
+
const descent = getFontDescentInPt(fontKitFont, fontSize);
|
110
|
+
const halfLineHeightAdjustment = lineHeight === 0 ? 0 : ((lineHeight - 1) * fontSize) / 2;
|
111
|
+
|
112
|
+
const fontWidthCalcValues: FontWidthCalcValues = {
|
113
|
+
font: fontKitFont,
|
114
|
+
fontSize,
|
115
|
+
characterSpacing,
|
116
|
+
boxWidthInPt: width,
|
117
|
+
};
|
118
|
+
|
119
|
+
let lines: string[] = [];
|
120
|
+
value.split(/\r|\n|\r\n/g).forEach((line) => {
|
121
|
+
lines = lines.concat(getSplittedLines(line, fontWidthCalcValues));
|
122
|
+
});
|
123
|
+
|
124
|
+
// Text lines are rendered from the bottom upwards, we need to adjust the position down
|
125
|
+
let yOffset = 0;
|
126
|
+
if (verticalAlignment === VERTICAL_ALIGN_TOP) {
|
127
|
+
yOffset = firstLineTextHeight + halfLineHeightAdjustment;
|
128
|
+
} else {
|
129
|
+
const otherLinesHeight = lineHeight * fontSize * (lines.length - 1);
|
130
|
+
|
131
|
+
if (verticalAlignment === VERTICAL_ALIGN_BOTTOM) {
|
132
|
+
yOffset = height - otherLinesHeight + descent - halfLineHeightAdjustment;
|
133
|
+
} else if (verticalAlignment === VERTICAL_ALIGN_MIDDLE) {
|
134
|
+
yOffset =
|
135
|
+
(height - otherLinesHeight - firstLineTextHeight + descent) / 2 + firstLineTextHeight;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
lines.forEach((line, rowIndex) => {
|
140
|
+
const textWidth = widthOfTextAtSize(line, fontKitFont, fontSize, characterSpacing);
|
141
|
+
const rowYOffset = lineHeight * fontSize * rowIndex;
|
142
|
+
|
143
|
+
page.drawText(line, {
|
144
|
+
x: calcX(schema.position.x, alignment, width, textWidth),
|
145
|
+
y: calcY(schema.position.y, pageHeight, yOffset) - rowYOffset,
|
146
|
+
rotate,
|
147
|
+
size: fontSize,
|
148
|
+
color,
|
149
|
+
lineHeight: lineHeight * fontSize,
|
150
|
+
maxWidth: width,
|
151
|
+
font: pdfFontValue,
|
152
|
+
wordBreaks: [''],
|
153
|
+
});
|
154
|
+
});
|
155
|
+
};
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import {
|
2
|
+
DEFAULT_FONT_NAME,
|
3
|
+
PropPanel,
|
4
|
+
PropPanelWidgetProps,
|
5
|
+
PropPanelSchema,
|
6
|
+
getFallbackFontName,
|
7
|
+
} from '@pdfme/common';
|
8
|
+
import type { TextSchema } from './types';
|
9
|
+
import {
|
10
|
+
DEFAULT_FONT_SIZE,
|
11
|
+
DEFAULT_ALIGNMENT,
|
12
|
+
DEFAULT_VERTICAL_ALIGNMENT,
|
13
|
+
DEFAULT_CHARACTER_SPACING,
|
14
|
+
DEFAULT_LINE_HEIGHT,
|
15
|
+
VERTICAL_ALIGN_TOP,
|
16
|
+
VERTICAL_ALIGN_MIDDLE,
|
17
|
+
VERTICAL_ALIGN_BOTTOM,
|
18
|
+
DEFAULT_FONT_COLOR,
|
19
|
+
DYNAMIC_FIT_VERTICAL,
|
20
|
+
DYNAMIC_FIT_HORIZONTAL,
|
21
|
+
DEFAULT_DYNAMIC_FIT,
|
22
|
+
DEFAULT_DYNAMIC_MIN_FONT_SIZE,
|
23
|
+
DEFAULT_DYNAMIC_MAX_FONT_SIZE,
|
24
|
+
ALIGN_RIGHT,
|
25
|
+
ALIGN_CENTER,
|
26
|
+
} from './constants';
|
27
|
+
|
28
|
+
const UseDynamicFontSize = (props: PropPanelWidgetProps) => {
|
29
|
+
const { rootElement, changeSchemas, activeSchema } = props;
|
30
|
+
|
31
|
+
const checkbox = document.createElement('input');
|
32
|
+
checkbox.type = 'checkbox';
|
33
|
+
checkbox.checked = Boolean((activeSchema as any)?.dynamicFontSize);
|
34
|
+
checkbox.onchange = (e: any) => {
|
35
|
+
const val = e.target.checked
|
36
|
+
? {
|
37
|
+
min: DEFAULT_DYNAMIC_MIN_FONT_SIZE,
|
38
|
+
max: DEFAULT_DYNAMIC_MAX_FONT_SIZE,
|
39
|
+
fit: DEFAULT_DYNAMIC_FIT,
|
40
|
+
}
|
41
|
+
: undefined;
|
42
|
+
changeSchemas([{ key: 'dynamicFontSize', value: val, schemaId: activeSchema.id }]);
|
43
|
+
};
|
44
|
+
const label = document.createElement('label');
|
45
|
+
label.innerText = 'Dynamic Font Size';
|
46
|
+
label.style.cssText = 'display: flex; width: 100%;';
|
47
|
+
label.appendChild(checkbox);
|
48
|
+
rootElement.appendChild(label);
|
49
|
+
};
|
50
|
+
|
51
|
+
export const propPanel: PropPanel<TextSchema> = {
|
52
|
+
propPanelSchema: ({ options, activeSchema }) => {
|
53
|
+
const font = options.font || { [DEFAULT_FONT_NAME]: { data: '', fallback: true } };
|
54
|
+
const fontNames = Object.keys(font);
|
55
|
+
const fallbackFontName = getFallbackFontName(font);
|
56
|
+
|
57
|
+
const enableDynamicFont = Boolean((activeSchema as any)?.dynamicFontSize);
|
58
|
+
|
59
|
+
const textSchema: Record<string, PropPanelSchema> = {
|
60
|
+
fontName: {
|
61
|
+
title: 'Font Name',
|
62
|
+
type: 'string',
|
63
|
+
widget: 'select',
|
64
|
+
default: fallbackFontName,
|
65
|
+
props: { options: fontNames.map((name) => ({ label: name, value: name })) },
|
66
|
+
span: 8,
|
67
|
+
},
|
68
|
+
alignment: {
|
69
|
+
title: 'Text Align',
|
70
|
+
type: 'string',
|
71
|
+
widget: 'select',
|
72
|
+
props: {
|
73
|
+
options: [
|
74
|
+
{ label: 'Left', value: DEFAULT_ALIGNMENT },
|
75
|
+
{ label: 'Center', value: ALIGN_CENTER },
|
76
|
+
{ label: 'Right', value: ALIGN_RIGHT },
|
77
|
+
],
|
78
|
+
},
|
79
|
+
span: 8,
|
80
|
+
},
|
81
|
+
verticalAlignment: {
|
82
|
+
title: 'Vertical Align',
|
83
|
+
type: 'string',
|
84
|
+
widget: 'select',
|
85
|
+
props: {
|
86
|
+
options: [
|
87
|
+
{ label: 'Top', value: VERTICAL_ALIGN_TOP },
|
88
|
+
{ label: 'Middle', value: VERTICAL_ALIGN_MIDDLE },
|
89
|
+
{ label: 'Bottom', value: VERTICAL_ALIGN_BOTTOM },
|
90
|
+
],
|
91
|
+
},
|
92
|
+
span: 8,
|
93
|
+
},
|
94
|
+
fontSize: {
|
95
|
+
title: 'Font Size',
|
96
|
+
type: 'number',
|
97
|
+
widget: 'inputNumber',
|
98
|
+
span: 8,
|
99
|
+
disabled: enableDynamicFont,
|
100
|
+
},
|
101
|
+
lineHeight: { title: 'Line Height', type: 'number', widget: 'inputNumber', span: 8 },
|
102
|
+
characterSpacing: { title: 'Char Spc', type: 'number', widget: 'inputNumber', span: 8 },
|
103
|
+
useDynamicFontSize: { type: 'boolean', widget: 'UseDynamicFontSize', bind: false },
|
104
|
+
dynamicFontSize: {
|
105
|
+
type: 'object',
|
106
|
+
widget: 'card',
|
107
|
+
column: 3,
|
108
|
+
properties: {
|
109
|
+
min: { title: 'Min', type: 'number', widget: 'inputNumber', hidden: !enableDynamicFont },
|
110
|
+
max: { title: 'Max', type: 'number', widget: 'inputNumber', hidden: !enableDynamicFont },
|
111
|
+
fit: {
|
112
|
+
title: 'Fit',
|
113
|
+
type: 'string',
|
114
|
+
widget: 'select',
|
115
|
+
hidden: !enableDynamicFont,
|
116
|
+
props: {
|
117
|
+
options: [
|
118
|
+
{ label: 'Horizontal', value: DYNAMIC_FIT_HORIZONTAL },
|
119
|
+
{ label: 'Vertical', value: DYNAMIC_FIT_VERTICAL },
|
120
|
+
],
|
121
|
+
},
|
122
|
+
},
|
123
|
+
},
|
124
|
+
},
|
125
|
+
fontColor: { title: 'Font Color', type: 'string', widget: 'color' },
|
126
|
+
backgroundColor: { title: 'Background', type: 'string', widget: 'color' },
|
127
|
+
};
|
128
|
+
|
129
|
+
return textSchema;
|
130
|
+
},
|
131
|
+
widgets: { UseDynamicFontSize },
|
132
|
+
defaultValue: 'Type Something...',
|
133
|
+
defaultSchema: {
|
134
|
+
type: 'text',
|
135
|
+
position: { x: 0, y: 0 },
|
136
|
+
width: 45,
|
137
|
+
height: 10,
|
138
|
+
alignment: DEFAULT_ALIGNMENT,
|
139
|
+
verticalAlignment: DEFAULT_VERTICAL_ALIGNMENT,
|
140
|
+
fontSize: DEFAULT_FONT_SIZE,
|
141
|
+
lineHeight: DEFAULT_LINE_HEIGHT,
|
142
|
+
characterSpacing: DEFAULT_CHARACTER_SPACING,
|
143
|
+
dynamicFontSize: undefined,
|
144
|
+
fontColor: DEFAULT_FONT_COLOR,
|
145
|
+
fontName: undefined,
|
146
|
+
backgroundColor: '',
|
147
|
+
},
|
148
|
+
};
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import type { Schema } from '@pdfme/common';
|
2
|
+
import type { Font as FontKitFont } from 'fontkit';
|
3
|
+
|
4
|
+
export type ALIGNMENT = 'left' | 'center' | 'right';
|
5
|
+
export type VERTICAL_ALIGNMENT = 'top' | 'middle' | 'bottom';
|
6
|
+
export type DYNAMIC_FONT_SIZE_FIT = 'horizontal' | 'vertical';
|
7
|
+
|
8
|
+
export type FontWidthCalcValues = {
|
9
|
+
font: FontKitFont;
|
10
|
+
fontSize: number;
|
11
|
+
characterSpacing: number;
|
12
|
+
boxWidthInPt: number;
|
13
|
+
};
|
14
|
+
export interface TextSchema extends Schema {
|
15
|
+
fontName?: string;
|
16
|
+
alignment: ALIGNMENT;
|
17
|
+
verticalAlignment: VERTICAL_ALIGNMENT;
|
18
|
+
fontSize: number;
|
19
|
+
lineHeight: number;
|
20
|
+
characterSpacing: number;
|
21
|
+
dynamicFontSize?: {
|
22
|
+
min: number;
|
23
|
+
max: number;
|
24
|
+
fit: DYNAMIC_FONT_SIZE_FIT;
|
25
|
+
};
|
26
|
+
|
27
|
+
fontColor: string;
|
28
|
+
backgroundColor: string;
|
29
|
+
}
|
@@ -0,0 +1,153 @@
|
|
1
|
+
import type * as CSS from 'csstype';
|
2
|
+
import { UIRenderProps, Schema, getDefaultFont } from '@pdfme/common';
|
3
|
+
import type { TextSchema } from './types';
|
4
|
+
import {
|
5
|
+
DEFAULT_FONT_SIZE,
|
6
|
+
DEFAULT_ALIGNMENT,
|
7
|
+
VERTICAL_ALIGN_TOP,
|
8
|
+
VERTICAL_ALIGN_MIDDLE,
|
9
|
+
VERTICAL_ALIGN_BOTTOM,
|
10
|
+
DEFAULT_VERTICAL_ALIGNMENT,
|
11
|
+
DEFAULT_LINE_HEIGHT,
|
12
|
+
DEFAULT_CHARACTER_SPACING,
|
13
|
+
DEFAULT_FONT_COLOR,
|
14
|
+
} from './constants';
|
15
|
+
import {
|
16
|
+
calculateDynamicFontSize,
|
17
|
+
getFontKitFont,
|
18
|
+
getBrowserVerticalFontAdjustments,
|
19
|
+
} from './helper';
|
20
|
+
|
21
|
+
const mapVerticalAlignToFlex = (verticalAlignmentValue: string | undefined) => {
|
22
|
+
switch (verticalAlignmentValue) {
|
23
|
+
case VERTICAL_ALIGN_TOP:
|
24
|
+
return 'flex-start';
|
25
|
+
case VERTICAL_ALIGN_MIDDLE:
|
26
|
+
return 'center';
|
27
|
+
case VERTICAL_ALIGN_BOTTOM:
|
28
|
+
return 'flex-end';
|
29
|
+
}
|
30
|
+
return 'flex-start';
|
31
|
+
};
|
32
|
+
|
33
|
+
export const uiRender = async (arg: UIRenderProps<TextSchema>) => {
|
34
|
+
const {
|
35
|
+
value,
|
36
|
+
schema,
|
37
|
+
rootElement,
|
38
|
+
mode,
|
39
|
+
onChange,
|
40
|
+
stopEditing,
|
41
|
+
tabIndex,
|
42
|
+
placeholder,
|
43
|
+
options,
|
44
|
+
} = arg;
|
45
|
+
const font = options?.font || getDefaultFont();
|
46
|
+
|
47
|
+
let dynamicFontSize: undefined | number = undefined;
|
48
|
+
if (schema.dynamicFontSize && value) {
|
49
|
+
dynamicFontSize = await calculateDynamicFontSize({
|
50
|
+
textSchema: schema,
|
51
|
+
font,
|
52
|
+
value,
|
53
|
+
startingFontSize: dynamicFontSize,
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
const fontKitFont = await getFontKitFont(schema, font);
|
58
|
+
// Depending on vertical alignment, we need to move the top or bottom of the font to keep
|
59
|
+
// it within it's defined box and align it with the generated pdf.
|
60
|
+
const { topAdj, bottomAdj } = getBrowserVerticalFontAdjustments(
|
61
|
+
fontKitFont,
|
62
|
+
dynamicFontSize ?? schema.fontSize ?? DEFAULT_FONT_SIZE,
|
63
|
+
schema.lineHeight ?? DEFAULT_LINE_HEIGHT,
|
64
|
+
schema.verticalAlignment ?? DEFAULT_VERTICAL_ALIGNMENT
|
65
|
+
);
|
66
|
+
|
67
|
+
const topAdjustment = topAdj;
|
68
|
+
const bottomAdjustment = bottomAdj;
|
69
|
+
|
70
|
+
const container = document.createElement('div');
|
71
|
+
function getBackgroundColor(mode: 'form' | 'viewer', value: string, schema: Schema) {
|
72
|
+
if (mode === 'form' && value && schema.backgroundColor) {
|
73
|
+
return schema.backgroundColor as string;
|
74
|
+
} else if (mode === 'viewer') {
|
75
|
+
return (schema.backgroundColor as string) ?? 'transparent';
|
76
|
+
} else {
|
77
|
+
return 'rgb(242 244 255 / 75%)';
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
const containerStyle: CSS.Properties = {
|
82
|
+
padding: 0,
|
83
|
+
resize: 'none',
|
84
|
+
backgroundColor: getBackgroundColor(mode, value, schema),
|
85
|
+
border: 'none',
|
86
|
+
display: 'flex',
|
87
|
+
flexDirection: 'column',
|
88
|
+
justifyContent: mapVerticalAlignToFlex(schema.verticalAlignment),
|
89
|
+
width: '100%',
|
90
|
+
height: '100%',
|
91
|
+
};
|
92
|
+
Object.assign(container.style, containerStyle);
|
93
|
+
rootElement.innerHTML = '';
|
94
|
+
rootElement.appendChild(container);
|
95
|
+
|
96
|
+
const fontStyles: CSS.Properties = {
|
97
|
+
fontFamily: schema.fontName ? `'${schema.fontName}'` : 'inherit',
|
98
|
+
color: schema.fontColor ? schema.fontColor : DEFAULT_FONT_COLOR,
|
99
|
+
fontSize: `${dynamicFontSize ?? schema.fontSize ?? DEFAULT_FONT_SIZE}pt`,
|
100
|
+
letterSpacing: `${schema.characterSpacing ?? DEFAULT_CHARACTER_SPACING}pt`,
|
101
|
+
lineHeight: `${schema.lineHeight ?? DEFAULT_LINE_HEIGHT}em`,
|
102
|
+
textAlign: schema.alignment ?? DEFAULT_ALIGNMENT,
|
103
|
+
whiteSpace: 'pre-wrap',
|
104
|
+
wordBreak: 'break-word',
|
105
|
+
};
|
106
|
+
|
107
|
+
if (mode === 'form') {
|
108
|
+
const textarea = document.createElement('textarea');
|
109
|
+
const textareaStyle: CSS.Properties = {
|
110
|
+
padding: 0,
|
111
|
+
resize: 'none',
|
112
|
+
border: 'none',
|
113
|
+
outline: 'none',
|
114
|
+
paddingTop: topAdjustment + 'px',
|
115
|
+
backgroundColor: 'transparent',
|
116
|
+
width: '100%',
|
117
|
+
height: '100%',
|
118
|
+
};
|
119
|
+
Object.assign(textarea.style, textareaStyle, fontStyles);
|
120
|
+
textarea.rows = 1;
|
121
|
+
textarea.placeholder = placeholder || '';
|
122
|
+
textarea.tabIndex = tabIndex || 0;
|
123
|
+
|
124
|
+
textarea.addEventListener(
|
125
|
+
'change',
|
126
|
+
(e: Event) => onChange && onChange((e.target as HTMLTextAreaElement).value)
|
127
|
+
);
|
128
|
+
textarea.addEventListener('blur', () => stopEditing && stopEditing());
|
129
|
+
textarea.value = value;
|
130
|
+
container.appendChild(textarea);
|
131
|
+
textarea.setSelectionRange(value.length, value.length);
|
132
|
+
textarea.focus();
|
133
|
+
} else {
|
134
|
+
const div = document.createElement('div');
|
135
|
+
const divStyle: CSS.Properties = {
|
136
|
+
...fontStyles,
|
137
|
+
marginBottom: bottomAdjustment + 'px',
|
138
|
+
paddingTop: topAdjustment + 'px',
|
139
|
+
};
|
140
|
+
Object.assign(div.style, divStyle);
|
141
|
+
div.innerHTML = value
|
142
|
+
.split('')
|
143
|
+
.map(
|
144
|
+
(l: string, i: number) =>
|
145
|
+
`<span style="letter-spacing:${
|
146
|
+
String(value).length === i + 1 ? 0 : 'inherit'
|
147
|
+
};">${l}</span>`
|
148
|
+
)
|
149
|
+
.join('');
|
150
|
+
|
151
|
+
container.appendChild(div);
|
152
|
+
}
|
153
|
+
};
|