@pdfme/schemas 2.2.1 → 3.0.0-beta.2
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/dist/cjs/__tests__/renderUtils.test.js +96 -0
- package/dist/cjs/__tests__/renderUtils.test.js.map +1 -0
- package/dist/cjs/src/barcodes/pdfRender.js +3 -9
- package/dist/cjs/src/barcodes/pdfRender.js.map +1 -1
- package/dist/cjs/src/barcodes/propPanel.js +11 -11
- package/dist/cjs/src/barcodes/propPanel.js.map +1 -1
- package/dist/cjs/src/barcodes/uiRender.js +9 -7
- package/dist/cjs/src/barcodes/uiRender.js.map +1 -1
- package/dist/cjs/src/image/pdfRender.js +3 -9
- package/dist/cjs/src/image/pdfRender.js.map +1 -1
- package/dist/cjs/src/image/propPanel.js +9 -1
- package/dist/cjs/src/image/propPanel.js.map +1 -1
- package/dist/cjs/src/image/uiRender.js +7 -7
- package/dist/cjs/src/image/uiRender.js.map +1 -1
- package/dist/cjs/src/index.js +2 -2
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/renderUtils.js +39 -55
- package/dist/cjs/src/renderUtils.js.map +1 -1
- package/dist/cjs/src/text/pdfRender.js +45 -5
- package/dist/cjs/src/text/pdfRender.js.map +1 -1
- package/dist/cjs/src/text/propPanel.js +12 -9
- package/dist/cjs/src/text/propPanel.js.map +1 -1
- package/dist/cjs/src/text/uiRender.js +17 -15
- package/dist/cjs/src/text/uiRender.js.map +1 -1
- package/dist/esm/__tests__/renderUtils.test.js +94 -0
- package/dist/esm/__tests__/renderUtils.test.js.map +1 -0
- package/dist/esm/src/barcodes/pdfRender.js +4 -10
- package/dist/esm/src/barcodes/pdfRender.js.map +1 -1
- package/dist/esm/src/barcodes/propPanel.js +11 -11
- package/dist/esm/src/barcodes/propPanel.js.map +1 -1
- package/dist/esm/src/barcodes/uiRender.js +9 -7
- package/dist/esm/src/barcodes/uiRender.js.map +1 -1
- package/dist/esm/src/image/pdfRender.js +4 -10
- package/dist/esm/src/image/pdfRender.js.map +1 -1
- package/dist/esm/src/image/propPanel.js +9 -1
- package/dist/esm/src/image/propPanel.js.map +1 -1
- package/dist/esm/src/image/uiRender.js +7 -7
- package/dist/esm/src/image/uiRender.js.map +1 -1
- package/dist/esm/src/index.js +4 -6
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/renderUtils.js +37 -50
- package/dist/esm/src/renderUtils.js.map +1 -1
- package/dist/esm/src/text/pdfRender.js +46 -6
- package/dist/esm/src/text/pdfRender.js.map +1 -1
- package/dist/esm/src/text/propPanel.js +12 -9
- package/dist/esm/src/text/propPanel.js.map +1 -1
- package/dist/esm/src/text/uiRender.js +17 -15
- package/dist/esm/src/text/uiRender.js.map +1 -1
- package/dist/types/__tests__/renderUtils.test.d.ts +1 -0
- package/dist/types/src/index.d.ts +4 -3
- package/dist/types/src/renderUtils.d.ts +18 -9
- package/package.json +2 -2
- package/src/barcodes/pdfRender.ts +10 -10
- package/src/barcodes/propPanel.ts +25 -3
- package/src/barcodes/uiRender.ts +10 -7
- package/src/image/pdfRender.ts +11 -10
- package/src/image/propPanel.ts +9 -1
- package/src/image/uiRender.ts +8 -8
- package/src/index.ts +4 -6
- package/src/renderUtils.ts +51 -61
- package/src/text/pdfRender.ts +60 -15
- package/src/text/propPanel.ts +12 -9
- package/src/text/uiRender.ts +19 -12
@@ -13,7 +13,14 @@ const default40x20 = { width: 40, height: 20 };
|
|
13
13
|
const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[] = [
|
14
14
|
{
|
15
15
|
defaultValue: 'https://pdfme.com/',
|
16
|
-
defaultSchema: {
|
16
|
+
defaultSchema: {
|
17
|
+
type: 'qrcode',
|
18
|
+
position,
|
19
|
+
...defaultColors,
|
20
|
+
width: 30,
|
21
|
+
height: 30,
|
22
|
+
rotate: 0,
|
23
|
+
},
|
17
24
|
},
|
18
25
|
{
|
19
26
|
defaultValue: '6540123789-A-K-Z',
|
@@ -24,6 +31,7 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
24
31
|
...defaultTextColors,
|
25
32
|
width: 80,
|
26
33
|
height: 7.2,
|
34
|
+
rotate: 0,
|
27
35
|
},
|
28
36
|
},
|
29
37
|
{
|
@@ -35,6 +43,7 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
35
43
|
...defaultTextColors,
|
36
44
|
...default40x20,
|
37
45
|
height: 16,
|
46
|
+
rotate: 0,
|
38
47
|
},
|
39
48
|
},
|
40
49
|
{
|
@@ -45,6 +54,7 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
45
54
|
...defaultColors,
|
46
55
|
...defaultTextColors,
|
47
56
|
...default40x20,
|
57
|
+
rotate: 0,
|
48
58
|
},
|
49
59
|
},
|
50
60
|
{
|
@@ -65,6 +75,7 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
65
75
|
...defaultColors,
|
66
76
|
...defaultTextColors,
|
67
77
|
...default40x20,
|
78
|
+
rotate: 0,
|
68
79
|
},
|
69
80
|
},
|
70
81
|
{
|
@@ -75,6 +86,7 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
75
86
|
...defaultColors,
|
76
87
|
...defaultTextColors,
|
77
88
|
...default40x20,
|
89
|
+
rotate: 0,
|
78
90
|
},
|
79
91
|
},
|
80
92
|
{
|
@@ -86,6 +98,7 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
86
98
|
...defaultTextColors,
|
87
99
|
...default40x20,
|
88
100
|
height: 12,
|
101
|
+
rotate: 0,
|
89
102
|
},
|
90
103
|
},
|
91
104
|
{
|
@@ -97,6 +110,7 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
97
110
|
...defaultTextColors,
|
98
111
|
...default40x20,
|
99
112
|
height: 16,
|
113
|
+
rotate: 0,
|
100
114
|
},
|
101
115
|
},
|
102
116
|
{
|
@@ -107,11 +121,19 @@ const barcodeDefaults: { defaultValue: string; defaultSchema: BarcodeSchema }[]
|
|
107
121
|
...defaultColors,
|
108
122
|
...defaultTextColors,
|
109
123
|
...default40x20,
|
124
|
+
rotate: 0,
|
110
125
|
},
|
111
126
|
},
|
112
127
|
{
|
113
128
|
defaultValue: '(01)03453120000011(17)191125(10)ABCD1234',
|
114
|
-
defaultSchema: {
|
129
|
+
defaultSchema: {
|
130
|
+
type: 'gs1datamatrix',
|
131
|
+
position,
|
132
|
+
...defaultColors,
|
133
|
+
width: 30,
|
134
|
+
height: 30,
|
135
|
+
rotate: 0,
|
136
|
+
},
|
115
137
|
},
|
116
138
|
];
|
117
139
|
|
@@ -127,7 +149,7 @@ export const getPropPanelByBarcodeType = (barcodeType: string): PropPanel<Barcod
|
|
127
149
|
};
|
128
150
|
const defaults = barcodeDefaults.find(({ defaultSchema }) => defaultSchema.type === barcodeType);
|
129
151
|
|
130
|
-
if (!defaults) throw new Error(`No default for barcode type ${barcodeType}`);
|
152
|
+
if (!defaults) throw new Error(`[@pdfme/schemas] No default for barcode type ${barcodeType}`);
|
131
153
|
|
132
154
|
return { propPanelSchema: schema, ...defaults };
|
133
155
|
};
|
package/src/barcodes/uiRender.ts
CHANGED
@@ -72,8 +72,8 @@ export const uiRender = async (arg: UIRenderProps<BarcodeSchema>) => {
|
|
72
72
|
};
|
73
73
|
Object.assign(container.style, containerStyle);
|
74
74
|
rootElement.appendChild(container);
|
75
|
-
const
|
76
|
-
if (
|
75
|
+
const editable = mode === 'form' || mode === 'designer';
|
76
|
+
if (editable) {
|
77
77
|
const input = document.createElement('input');
|
78
78
|
const inputStyle: CSS.Properties = {
|
79
79
|
...fullSize,
|
@@ -81,7 +81,7 @@ export const uiRender = async (arg: UIRenderProps<BarcodeSchema>) => {
|
|
81
81
|
textAlign: 'center',
|
82
82
|
fontSize: '1rem',
|
83
83
|
color: '#000',
|
84
|
-
backgroundColor:
|
84
|
+
backgroundColor: editable || value ? 'rgb(242 244 255 / 75%)' : 'none',
|
85
85
|
border: 'none',
|
86
86
|
display: 'flex',
|
87
87
|
alignItems: 'center',
|
@@ -99,17 +99,20 @@ export const uiRender = async (arg: UIRenderProps<BarcodeSchema>) => {
|
|
99
99
|
stopEditing && stopEditing();
|
100
100
|
});
|
101
101
|
container.appendChild(input);
|
102
|
-
|
103
|
-
|
102
|
+
if (mode === 'designer') {
|
103
|
+
input.setSelectionRange(value.length, value.length);
|
104
|
+
input.focus();
|
105
|
+
}
|
104
106
|
}
|
105
107
|
|
106
108
|
if (!value) return;
|
107
109
|
try {
|
108
|
-
if (!validateBarcodeInput(schema.type, value))
|
110
|
+
if (!validateBarcodeInput(schema.type, value))
|
111
|
+
throw new Error('[@pdfme/schemas] Invalid barcode input');
|
109
112
|
const imgElm = await createBarcodeImageElm(schema, value);
|
110
113
|
container.appendChild(imgElm);
|
111
114
|
} catch (err) {
|
112
|
-
console.error(err);
|
115
|
+
console.error(`[@pdfme/ui] ${err}`);
|
113
116
|
const errorBarcodeElm = createErrorBarcodeElm();
|
114
117
|
container.appendChild(errorBarcodeElm);
|
115
118
|
}
|
package/src/image/pdfRender.ts
CHANGED
@@ -1,18 +1,10 @@
|
|
1
1
|
import type { PDFRenderProps } from '@pdfme/common';
|
2
2
|
import type { ImageSchema } from './types';
|
3
|
-
import {
|
3
|
+
import { getCacheKey, convertForPdfLayoutProps } from '../renderUtils';
|
4
4
|
|
5
5
|
export const pdfRender = async (arg: PDFRenderProps<ImageSchema>) => {
|
6
6
|
const { value, schema, pdfDoc, page, _cache } = arg;
|
7
7
|
|
8
|
-
const { width, height, rotate } = convertSchemaDimensionsToPt(schema);
|
9
|
-
const opt = {
|
10
|
-
x: calcX(schema.position.x, 'left', width, width),
|
11
|
-
y: calcY(schema.position.y, page.getHeight(), height),
|
12
|
-
rotate,
|
13
|
-
width,
|
14
|
-
height,
|
15
|
-
};
|
16
8
|
const inputImageCacheKey = getCacheKey(schema, value);
|
17
9
|
let image = _cache.get(inputImageCacheKey);
|
18
10
|
if (!image) {
|
@@ -20,5 +12,14 @@ export const pdfRender = async (arg: PDFRenderProps<ImageSchema>) => {
|
|
20
12
|
image = await (isPng ? pdfDoc.embedPng(value) : pdfDoc.embedJpg(value));
|
21
13
|
_cache.set(inputImageCacheKey, image);
|
22
14
|
}
|
23
|
-
|
15
|
+
|
16
|
+
const pageHeight = page.getHeight();
|
17
|
+
const {
|
18
|
+
width,
|
19
|
+
height,
|
20
|
+
rotate,
|
21
|
+
position: { x, y },
|
22
|
+
} = convertForPdfLayoutProps({ schema, pageHeight });
|
23
|
+
|
24
|
+
page.drawImage(image, { x, y, rotate, width, height });
|
24
25
|
};
|
package/src/image/propPanel.ts
CHANGED
@@ -4,5 +4,13 @@ export const propPanel: PropPanel<ImageSchema> = {
|
|
4
4
|
propPanelSchema: {},
|
5
5
|
defaultValue:
|
6
6
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu4AAALuAQMAAADL0wGJAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAGUExURbzAw+rv8fKruy0AAAPoSURBVHja7dwxbtwwEEBRCkKwRQodYftcYk+ROkcJz5NTsEuZK/AIKlIQAUEnke0VqQ0pA5zxWvFnZcD2s0CNuENxPOZBc0QDDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PD/+P8bkxvnTzzjTG0M2b5rh08rHNT518aPOnTt63+aGTd23edPJ2h//ax+/oO6Gzx6c78+cuPu7x01vmwx5/6uLnO/PjO+b/rGifvtWH3VnT9vmh/e3eqx/bc9d79af2YwEPDw8P/6r8r1GVt5VcUoZPtXxGhp9rGYEM72vbEBne1hJ5Gb6ayIvwqZppi/CxmgqL8KGaTIrwc3WDfATeV/ffIryr7r+PwNvq/vsIfH17D5/qbw/gb/mLKp/OqnycFPj14yqcJPm4vfp5VJ0cP6jyzqjy9rr2q/Dr4qzBp3V5E1wxp/V3rl8LXn32qF6fAY31Psv2NXi/5lQaH+Vuzak0eLsmPRppVPa3FPiU3QiFFDZmD4FCAh6yxV+Bn7O9isLmx2d/TGHr5rI7obDxtFkcKWyb89M2+U1/sX7Kv7Io8gaxFy5l2D/faRk++3x6PgsalV52+fwUW4j/+eGhDPuneyH/otHmp9jyfHHMLM6n4phZnI/FObA4H4qDWnF+Lo46xXlflG+I864o3xDnbVG+Ic6X9RXSfCrrK6T5WBZASPOhrFCQ5ufyjF+a92XdkjTvyrolad6WdUvS/KawSI7/sQn7JfDl+O+bsF8CX44fN2FvHnNnIT4Nm7BfAl+ON5uwXwJfjA/LCuk2BXvCvN0U7InxflmAtxV1gvx0U2N3luPtdZOlwj/FoR5vbuq85Pi48F6LD0scOk3+sq1cleP9ohlNfkpq/N9pGaMuH7T4ZVqGWYt/nBavyxstftblfaVIWJU/y72yODCfzJH5oMvPh+adLm+PzCdzZD5U/61ClT9Lnvyo8e7QvFHlE3ydj0zOu5ucickhcpgcJofJedUVk8j5b/nGgIeHh4d/I3x/RwLlhgfNH3DavSwGXf7YjT76uqCEQ/P37p9z7uKVmwspd17S7hul3PXK6fbsmnvWhO6GZlMnn3ri8gXN5GzHnX0B35ydj91814CHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHl+d/A9cKjmiL040TAAAAAElFTkSuQmCC',
|
7
|
-
defaultSchema: {
|
7
|
+
defaultSchema: {
|
8
|
+
type: 'image',
|
9
|
+
position: { x: 0, y: 0 },
|
10
|
+
width: 40,
|
11
|
+
height: 40,
|
12
|
+
// If the value of "rotate" is set to undefined or not set at all, rotation will be disabled in the UI.
|
13
|
+
// Check this document: https://pdfme.com//docs/custom-schemas#learning-how-to-create-from-pdfmeschemas-code
|
14
|
+
rotate: 0,
|
15
|
+
},
|
8
16
|
};
|
package/src/image/uiRender.ts
CHANGED
@@ -16,7 +16,7 @@ const readFile = (input: File | FileList | null): Promise<string | ArrayBuffer>
|
|
16
16
|
};
|
17
17
|
|
18
18
|
fileReader.onerror = (e) => {
|
19
|
-
reject(new Error('File reading failed'));
|
19
|
+
reject(new Error('[@pdfme/schemas] File reading failed'));
|
20
20
|
};
|
21
21
|
|
22
22
|
let file: File | null = null;
|
@@ -29,13 +29,13 @@ const readFile = (input: File | FileList | null): Promise<string | ArrayBuffer>
|
|
29
29
|
if (file) {
|
30
30
|
fileReader.readAsDataURL(file);
|
31
31
|
} else {
|
32
|
-
reject(new Error('No files provided'));
|
32
|
+
reject(new Error('[@pdfme/schemas] No files provided'));
|
33
33
|
}
|
34
34
|
});
|
35
35
|
|
36
36
|
export const uiRender = async (arg: UIRenderProps<ImageSchema>) => {
|
37
37
|
const { value, rootElement, mode, onChange, stopEditing, tabIndex, placeholder, schema } = arg;
|
38
|
-
const
|
38
|
+
const editable = mode === 'form' || mode === 'designer';
|
39
39
|
|
40
40
|
const size = { width: schema.width * ZOOM, height: schema.height * ZOOM };
|
41
41
|
|
@@ -47,7 +47,7 @@ export const uiRender = async (arg: UIRenderProps<ImageSchema>) => {
|
|
47
47
|
};
|
48
48
|
Object.assign(container.style, containerStyle);
|
49
49
|
container.addEventListener('click', (e) => {
|
50
|
-
if (
|
50
|
+
if (editable) {
|
51
51
|
e.stopPropagation();
|
52
52
|
}
|
53
53
|
});
|
@@ -63,7 +63,7 @@ export const uiRender = async (arg: UIRenderProps<ImageSchema>) => {
|
|
63
63
|
}
|
64
64
|
|
65
65
|
// remove button
|
66
|
-
if (value &&
|
66
|
+
if (value && editable) {
|
67
67
|
const button = document.createElement('button');
|
68
68
|
button.textContent = 'x';
|
69
69
|
const buttonStyle: CSS.Properties = {
|
@@ -90,14 +90,14 @@ export const uiRender = async (arg: UIRenderProps<ImageSchema>) => {
|
|
90
90
|
}
|
91
91
|
|
92
92
|
// file input
|
93
|
-
if (!value &&
|
93
|
+
if (!value && editable) {
|
94
94
|
const label = document.createElement('label');
|
95
95
|
const labelStyle: CSS.Properties = {
|
96
96
|
...fullSize,
|
97
|
-
display:
|
97
|
+
display: editable ? 'flex' : 'none',
|
98
98
|
position: 'absolute',
|
99
99
|
top: 0,
|
100
|
-
backgroundColor:
|
100
|
+
backgroundColor: editable || value ? 'rgb(242 244 255 / 50%)' : 'none',
|
101
101
|
cursor: 'pointer',
|
102
102
|
};
|
103
103
|
Object.assign(label.style, labelStyle);
|
package/src/index.ts
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
import
|
1
|
+
import text from './text';
|
2
|
+
import image from './image';
|
3
|
+
import barcodes from './barcodes';
|
4
4
|
|
5
|
-
export
|
6
|
-
export const image = imageSchema;
|
7
|
-
export const barcodes = barcodesSchemaObj;
|
5
|
+
export { text, image, barcodes };
|
package/src/renderUtils.ts
CHANGED
@@ -1,74 +1,64 @@
|
|
1
|
-
import {
|
1
|
+
import { degrees, degreesToRadians } from '@pdfme/pdf-lib';
|
2
2
|
import { Schema, mm2pt } from '@pdfme/common';
|
3
3
|
|
4
|
-
const
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
return [hex.slice(0, 2), hex.slice(2, 4), hex.slice(4, 6)].map((str) => parseInt(str, 16));
|
16
|
-
};
|
17
|
-
|
18
|
-
export const hex2RgbColor = (hexString: string | undefined) => {
|
19
|
-
if (hexString) {
|
20
|
-
const [r, g, b] = hex2rgb(hexString);
|
21
|
-
|
22
|
-
return rgb(r / 255, g / 255, b / 255);
|
23
|
-
}
|
4
|
+
export const convertForPdfLayoutProps = ({
|
5
|
+
schema,
|
6
|
+
pageHeight,
|
7
|
+
applyRotateTranslate = true,
|
8
|
+
}: {
|
9
|
+
schema: Schema;
|
10
|
+
pageHeight: number;
|
11
|
+
applyRotateTranslate?: boolean;
|
12
|
+
}) => {
|
13
|
+
const { width: mmWidth, height: mmHeight, position, rotate } = schema;
|
14
|
+
const { x: mmX, y: mmY } = position;
|
24
15
|
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
const rotateDegrees = rotate ? -rotate : 0;
|
17
|
+
const width = mm2pt(mmWidth);
|
18
|
+
const height = mm2pt(mmHeight);
|
19
|
+
let x = mm2pt(mmX);
|
20
|
+
// PDF coordinate system is from bottom left, UI is top left, so we need to flip the y axis
|
21
|
+
let y = pageHeight - mm2pt(mmY) - height;
|
28
22
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
} else if (alignment === 'right') {
|
39
|
-
addition = boxWidth - textWidth;
|
23
|
+
if (rotateDegrees && applyRotateTranslate) {
|
24
|
+
// If rotating we must pivot around the same point as the UI performs its rotation.
|
25
|
+
// The UI performs rotation around the objects center point (the pivot point below),
|
26
|
+
// pdflib rotates around the bottom left corner of the object.
|
27
|
+
// We must therefore adjust the X and Y by rotating the bottom left corner by this pivot point.
|
28
|
+
const pivotPoint = { x: x + width / 2, y: pageHeight - mm2pt(mmY) - height / 2 };
|
29
|
+
const rotatedPoint = rotatePoint({ x, y }, pivotPoint, rotateDegrees);
|
30
|
+
x = rotatedPoint.x;
|
31
|
+
y = rotatedPoint.y;
|
40
32
|
}
|
41
33
|
|
42
|
-
return
|
34
|
+
return {
|
35
|
+
position: {
|
36
|
+
x: x,
|
37
|
+
y: y,
|
38
|
+
},
|
39
|
+
height: height,
|
40
|
+
width: width,
|
41
|
+
rotate: degrees(rotateDegrees),
|
42
|
+
};
|
43
43
|
};
|
44
44
|
|
45
|
-
export const
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
pageHeight: number;
|
52
|
-
}) => {
|
53
|
-
const { schema, page, pageHeight } = arg;
|
54
|
-
if (!schema.backgroundColor) return;
|
55
|
-
const { width, height } = convertSchemaDimensionsToPt(schema);
|
56
|
-
const color = hex2RgbColor(schema.backgroundColor as string);
|
57
|
-
page.drawRectangle({
|
58
|
-
x: calcX(schema.position.x, 'left', width, width),
|
59
|
-
y: calcY(schema.position.y, pageHeight, height),
|
60
|
-
width,
|
61
|
-
height,
|
62
|
-
color,
|
63
|
-
});
|
64
|
-
};
|
45
|
+
export const rotatePoint = (
|
46
|
+
point: { x: number; y: number },
|
47
|
+
pivot: { x: number; y: number },
|
48
|
+
angleDegrees: number
|
49
|
+
): { x: number; y: number } => {
|
50
|
+
const angleRadians = degreesToRadians(angleDegrees);
|
65
51
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
52
|
+
const x =
|
53
|
+
Math.cos(angleRadians) * (point.x - pivot.x) -
|
54
|
+
Math.sin(angleRadians) * (point.y - pivot.y) +
|
55
|
+
pivot.x;
|
56
|
+
const y =
|
57
|
+
Math.sin(angleRadians) * (point.x - pivot.x) +
|
58
|
+
Math.cos(angleRadians) * (point.y - pivot.y) +
|
59
|
+
pivot.y;
|
70
60
|
|
71
|
-
return {
|
61
|
+
return { x, y };
|
72
62
|
};
|
73
63
|
|
74
64
|
export const getCacheKey = (schema: Schema, input: string) => `${schema.type}${input}`;
|
package/src/text/pdfRender.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { PDFFont, PDFDocument } from '@pdfme/pdf-lib';
|
2
|
-
import { PDFRenderProps, Font, getDefaultFont, getFallbackFontName } from '@pdfme/common';
|
1
|
+
import { PDFFont, PDFDocument, rgb } from '@pdfme/pdf-lib';
|
2
|
+
import { PDFRenderProps, Font, getDefaultFont, getFallbackFontName, mm2pt } from '@pdfme/common';
|
3
3
|
import type { TextSchema, FontWidthCalcValues } from './types';
|
4
4
|
import {
|
5
5
|
VERTICAL_ALIGN_TOP,
|
@@ -20,13 +20,31 @@ import {
|
|
20
20
|
getSplittedLines,
|
21
21
|
widthOfTextAtSize,
|
22
22
|
} from './helper';
|
23
|
-
import {
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
import { convertForPdfLayoutProps, rotatePoint } from '../renderUtils';
|
24
|
+
|
25
|
+
const hex2rgb = (hex: string) => {
|
26
|
+
if (hex.slice(0, 1) === '#') hex = hex.slice(1);
|
27
|
+
if (hex.length === 3)
|
28
|
+
hex =
|
29
|
+
hex.slice(0, 1) +
|
30
|
+
hex.slice(0, 1) +
|
31
|
+
hex.slice(1, 2) +
|
32
|
+
hex.slice(1, 2) +
|
33
|
+
hex.slice(2, 3) +
|
34
|
+
hex.slice(2, 3);
|
35
|
+
|
36
|
+
return [hex.slice(0, 2), hex.slice(2, 4), hex.slice(4, 6)].map((str) => parseInt(str, 16));
|
37
|
+
};
|
38
|
+
|
39
|
+
const hex2RgbColor = (hexString: string | undefined) => {
|
40
|
+
if (hexString) {
|
41
|
+
const [r, g, b] = hex2rgb(hexString);
|
42
|
+
|
43
|
+
return rgb(r / 255, g / 255, b / 255);
|
44
|
+
}
|
45
|
+
|
46
|
+
return undefined;
|
47
|
+
};
|
30
48
|
|
31
49
|
const embedAndGetFontObjCache = new WeakMap();
|
32
50
|
const embedAndGetFontObj = async (arg: { pdfDoc: PDFDocument; font: Font }) => {
|
@@ -99,9 +117,17 @@ export const pdfRender = async (arg: PDFRenderProps<TextSchema>) => {
|
|
99
117
|
const pdfFontValue = pdfFontObj[fontName];
|
100
118
|
|
101
119
|
const pageHeight = page.getHeight();
|
102
|
-
|
103
|
-
|
104
|
-
|
120
|
+
const {
|
121
|
+
width,
|
122
|
+
height,
|
123
|
+
rotate,
|
124
|
+
position: { x, y },
|
125
|
+
} = convertForPdfLayoutProps({ schema, pageHeight, applyRotateTranslate: false });
|
126
|
+
|
127
|
+
if (schema.backgroundColor) {
|
128
|
+
const color = hex2RgbColor(schema.backgroundColor as string);
|
129
|
+
page.drawRectangle({ x, y, width, height, rotate, color });
|
130
|
+
}
|
105
131
|
|
106
132
|
page.pushOperators(pdfLib.setCharacterSpacing(characterSpacing ?? DEFAULT_CHARACTER_SPACING));
|
107
133
|
|
@@ -117,7 +143,7 @@ export const pdfRender = async (arg: PDFRenderProps<TextSchema>) => {
|
|
117
143
|
};
|
118
144
|
|
119
145
|
let lines: string[] = [];
|
120
|
-
value.split(/\r|\n|\r\n/g).forEach((line) => {
|
146
|
+
value.split(/\r|\n|\r\n/g).forEach((line: string) => {
|
121
147
|
lines = lines.concat(getSplittedLines(line, fontWidthCalcValues));
|
122
148
|
});
|
123
149
|
|
@@ -136,13 +162,32 @@ export const pdfRender = async (arg: PDFRenderProps<TextSchema>) => {
|
|
136
162
|
}
|
137
163
|
}
|
138
164
|
|
165
|
+
const pivotPoint = { x: x + width / 2, y: pageHeight - mm2pt(schema.position.y) - height / 2 };
|
166
|
+
|
139
167
|
lines.forEach((line, rowIndex) => {
|
140
168
|
const textWidth = widthOfTextAtSize(line, fontKitFont, fontSize, characterSpacing);
|
141
169
|
const rowYOffset = lineHeight * fontSize * rowIndex;
|
142
170
|
|
171
|
+
let xLine = x;
|
172
|
+
if (alignment === 'center') {
|
173
|
+
xLine += (width - textWidth) / 2;
|
174
|
+
} else if (alignment === 'right') {
|
175
|
+
xLine += width - textWidth;
|
176
|
+
}
|
177
|
+
|
178
|
+
let yLine = pageHeight - mm2pt(schema.position.y) - yOffset - rowYOffset;
|
179
|
+
|
180
|
+
if (rotate.angle !== 0) {
|
181
|
+
// As we draw each line individually from different points, we must translate each lines position
|
182
|
+
// relative to the UI rotation pivot point. see comments in convertForPdfLayoutProps() for more info.
|
183
|
+
const rotatedPoint = rotatePoint({ x: xLine, y: yLine }, pivotPoint, rotate.angle);
|
184
|
+
xLine = rotatedPoint.x;
|
185
|
+
yLine = rotatedPoint.y;
|
186
|
+
}
|
187
|
+
|
143
188
|
page.drawText(line, {
|
144
|
-
x:
|
145
|
-
y:
|
189
|
+
x: xLine,
|
190
|
+
y: yLine,
|
146
191
|
rotate,
|
147
192
|
size: fontSize,
|
148
193
|
color,
|
package/src/text/propPanel.ts
CHANGED
@@ -63,8 +63,16 @@ export const propPanel: PropPanel<TextSchema> = {
|
|
63
63
|
widget: 'select',
|
64
64
|
default: fallbackFontName,
|
65
65
|
props: { options: fontNames.map((name) => ({ label: name, value: name })) },
|
66
|
-
span:
|
66
|
+
span: 12,
|
67
|
+
},
|
68
|
+
fontSize: {
|
69
|
+
title: 'Size',
|
70
|
+
type: 'number',
|
71
|
+
widget: 'inputNumber',
|
72
|
+
span: 6,
|
73
|
+
disabled: enableDynamicFont,
|
67
74
|
},
|
75
|
+
characterSpacing: { title: 'Spacing', type: 'number', widget: 'inputNumber', span: 6 },
|
68
76
|
alignment: {
|
69
77
|
title: 'Text Align',
|
70
78
|
type: 'string',
|
@@ -91,15 +99,7 @@ export const propPanel: PropPanel<TextSchema> = {
|
|
91
99
|
},
|
92
100
|
span: 8,
|
93
101
|
},
|
94
|
-
fontSize: {
|
95
|
-
title: 'Font Size',
|
96
|
-
type: 'number',
|
97
|
-
widget: 'inputNumber',
|
98
|
-
span: 8,
|
99
|
-
disabled: enableDynamicFont,
|
100
|
-
},
|
101
102
|
lineHeight: { title: 'Line Height', type: 'number', widget: 'inputNumber', span: 8 },
|
102
|
-
characterSpacing: { title: 'Char Spc', type: 'number', widget: 'inputNumber', span: 8 },
|
103
103
|
useDynamicFontSize: { type: 'boolean', widget: 'UseDynamicFontSize', bind: false },
|
104
104
|
dynamicFontSize: {
|
105
105
|
type: 'object',
|
@@ -135,6 +135,9 @@ export const propPanel: PropPanel<TextSchema> = {
|
|
135
135
|
position: { x: 0, y: 0 },
|
136
136
|
width: 45,
|
137
137
|
height: 10,
|
138
|
+
// If the value of "rotate" is set to undefined or not set at all, rotation will be disabled in the UI.
|
139
|
+
// Check this document: https://pdfme.com//docs/custom-schemas#learning-how-to-create-from-pdfmeschemas-code
|
140
|
+
rotate: 0,
|
138
141
|
alignment: DEFAULT_ALIGNMENT,
|
139
142
|
verticalAlignment: DEFAULT_VERTICAL_ALIGNMENT,
|
140
143
|
fontSize: DEFAULT_FONT_SIZE,
|
package/src/text/uiRender.ts
CHANGED
@@ -30,6 +30,20 @@ const mapVerticalAlignToFlex = (verticalAlignmentValue: string | undefined) => {
|
|
30
30
|
return 'flex-start';
|
31
31
|
};
|
32
32
|
|
33
|
+
const getBackgroundColor = (
|
34
|
+
mode: 'form' | 'viewer' | 'designer',
|
35
|
+
value: string,
|
36
|
+
schema: Schema
|
37
|
+
) => {
|
38
|
+
if ((mode === 'form' || mode === 'designer') && value && schema.backgroundColor) {
|
39
|
+
return schema.backgroundColor as string;
|
40
|
+
} else if (mode === 'viewer') {
|
41
|
+
return (schema.backgroundColor as string) ?? 'transparent';
|
42
|
+
} else {
|
43
|
+
return 'rgb(242 244 255 / 75%)';
|
44
|
+
}
|
45
|
+
};
|
46
|
+
|
33
47
|
export const uiRender = async (arg: UIRenderProps<TextSchema>) => {
|
34
48
|
const {
|
35
49
|
value,
|
@@ -68,15 +82,6 @@ export const uiRender = async (arg: UIRenderProps<TextSchema>) => {
|
|
68
82
|
const bottomAdjustment = bottomAdj;
|
69
83
|
|
70
84
|
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
85
|
|
81
86
|
const containerStyle: CSS.Properties = {
|
82
87
|
padding: 0,
|
@@ -104,7 +109,7 @@ export const uiRender = async (arg: UIRenderProps<TextSchema>) => {
|
|
104
109
|
wordBreak: 'break-word',
|
105
110
|
};
|
106
111
|
|
107
|
-
if (mode === 'form') {
|
112
|
+
if (mode === 'form' || mode === 'designer') {
|
108
113
|
const textarea = document.createElement('textarea');
|
109
114
|
const textareaStyle: CSS.Properties = {
|
110
115
|
padding: 0,
|
@@ -128,8 +133,10 @@ export const uiRender = async (arg: UIRenderProps<TextSchema>) => {
|
|
128
133
|
textarea.addEventListener('blur', () => stopEditing && stopEditing());
|
129
134
|
textarea.value = value;
|
130
135
|
container.appendChild(textarea);
|
131
|
-
|
132
|
-
|
136
|
+
if (mode === 'designer') {
|
137
|
+
textarea.setSelectionRange(value.length, value.length);
|
138
|
+
textarea.focus();
|
139
|
+
}
|
133
140
|
} else {
|
134
141
|
const div = document.createElement('div');
|
135
142
|
const divStyle: CSS.Properties = {
|