@hellkite/pipkin 0.6.2 → 0.7.0
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/build/src/lib/template.d.ts +9 -10
- package/build/src/lib/template.js +75 -40
- package/build/src/lib/template.js.map +1 -1
- package/build/src/lib/types/containers.d.ts +14 -2
- package/build/src/lib/types/containers.js.map +1 -1
- package/build/src/lib/types/hypernode.d.ts +2 -0
- package/build/src/lib/types/hypernode.js +3 -0
- package/build/src/lib/types/hypernode.js.map +1 -0
- package/build/src/lib/types/image.d.ts +5 -0
- package/build/src/lib/types/image.js.map +1 -1
- package/build/src/lib/types/index.d.ts +1 -0
- package/build/src/lib/types/index.js +1 -0
- package/build/src/lib/types/index.js.map +1 -1
- package/build/src/lib/types/text.d.ts +7 -2
- package/build/src/lib/types/text.js +1 -1
- package/build/src/lib/types/text.js.map +1 -1
- package/build/src/lib/utils/container.js +10 -45
- package/build/src/lib/utils/container.js.map +1 -1
- package/build/src/lib/utils/htmlToImage.d.ts +2 -3
- package/build/src/lib/utils/htmlToImage.js.map +1 -1
- package/build/src/lib/utils/index.d.ts +2 -2
- package/build/src/lib/utils/index.js +2 -2
- package/build/src/lib/utils/index.js.map +1 -1
- package/build/src/lib/utils/placeBoundingBox.d.ts +2 -0
- package/build/src/lib/utils/placeBoundingBox.js +21 -0
- package/build/src/lib/utils/placeBoundingBox.js.map +1 -0
- package/build/src/lib/utils/placeImage.d.ts +7 -6
- package/build/src/lib/utils/placeImage.js +20 -20
- package/build/src/lib/utils/placeImage.js.map +1 -1
- package/build/src/lib/utils/placeText.d.ts +14 -0
- package/build/src/lib/utils/placeText.js +69 -0
- package/build/src/lib/utils/placeText.js.map +1 -0
- package/build/src/test.js +28 -10
- package/build/src/test.js.map +1 -1
- package/package.json +3 -1
- package/roadmap.md +2 -1
- package/src/lib/template.ts +100 -70
- package/src/lib/types/containers.ts +22 -4
- package/src/lib/types/hypernode.ts +4 -0
- package/src/lib/types/image.ts +6 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/types/text.ts +10 -5
- package/src/lib/utils/container.ts +13 -70
- package/src/lib/utils/htmlToImage.ts +3 -3
- package/src/lib/utils/index.ts +2 -2
- package/src/lib/utils/{drawBoundingBox.ts → placeBoundingBox.ts} +3 -9
- package/src/lib/utils/placeImage.ts +34 -27
- package/src/lib/utils/placeText.ts +110 -0
- package/src/lib/utils/renderText.ts +0 -107
package/src/lib/types/image.ts
CHANGED
|
@@ -5,6 +5,10 @@ import { RequiredDeep } from 'type-fest';
|
|
|
5
5
|
|
|
6
6
|
export type ImageType = JimpInstance;
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Static images -> `buffer`, `path`, `absolutePath`
|
|
10
|
+
* Dynamic images -> `key`, `pathFn`
|
|
11
|
+
*/
|
|
8
12
|
export type ImageRef<EntryType extends Record<string, string>> =
|
|
9
13
|
| { buffer: Buffer }
|
|
10
14
|
| { path: string }
|
|
@@ -36,6 +40,8 @@ export const DEFAULT_IMAGE_LAYER_OPTIONS: RequiredDeep<ImageLayerOptions<Record<
|
|
|
36
40
|
assetsPath: ''
|
|
37
41
|
};
|
|
38
42
|
|
|
43
|
+
export type ImageLayerSpecificOptions<EntryType extends Record<string, string>> = Omit<ImageLayerOptions<EntryType>, keyof LayerOptions<EntryType>>;
|
|
44
|
+
|
|
39
45
|
export type Alignment = 'start' | 'center' | 'end';
|
|
40
46
|
|
|
41
47
|
export const DEFAULT_IMAGE_ALIGNMENT: Alignment = 'center';
|
package/src/lib/types/index.ts
CHANGED
package/src/lib/types/text.ts
CHANGED
|
@@ -2,9 +2,13 @@ import type { RequiredDeep } from 'type-fest';
|
|
|
2
2
|
import { LayerOptions } from './layer';
|
|
3
3
|
import { ReplacementMap } from './replacement';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Static text -> `text`
|
|
7
|
+
* Dynamic text -> `key`, `textFn`
|
|
8
|
+
*/
|
|
5
9
|
export type TextRef<EntryType> =
|
|
6
|
-
| { key: string }
|
|
7
10
|
| { text: string }
|
|
11
|
+
| { key: string }
|
|
8
12
|
| { textFn: (entry: EntryType) => string };
|
|
9
13
|
|
|
10
14
|
export type FontOptions = {
|
|
@@ -42,15 +46,16 @@ export type TextLayerOptions<EntryType extends Record<string, string>> =
|
|
|
42
46
|
replacement?: ReplacementMap;
|
|
43
47
|
};
|
|
44
48
|
|
|
45
|
-
export const DEFAULT_FONT: Required<
|
|
46
|
-
FontOptions
|
|
47
|
-
> = {
|
|
49
|
+
export const DEFAULT_FONT: Required<FontOptions> = {
|
|
48
50
|
size: 28,
|
|
49
51
|
family: 'Arial',
|
|
50
52
|
bold: false,
|
|
51
53
|
italic: false,
|
|
52
54
|
};
|
|
53
55
|
|
|
56
|
+
export type TextLayerSpecificOptions<EntryType extends Record<string, string>> =
|
|
57
|
+
Omit<TextLayerOptions<EntryType>, keyof LayerOptions<EntryType>>;
|
|
58
|
+
|
|
54
59
|
export const DEFAULT_TEXT_LAYER_OPTIONS: RequiredDeep<
|
|
55
60
|
TextLayerOptions<Record<string, string>>
|
|
56
61
|
> = {
|
|
@@ -63,5 +68,5 @@ export const DEFAULT_TEXT_LAYER_OPTIONS: RequiredDeep<
|
|
|
63
68
|
border: {
|
|
64
69
|
width: 0,
|
|
65
70
|
color: 'black',
|
|
66
|
-
}
|
|
71
|
+
},
|
|
67
72
|
};
|
|
@@ -2,28 +2,25 @@ import { h } from 'virtual-dom';
|
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_DIRECTION_CONTAINER_OPTIONS,
|
|
4
4
|
DirectionContainerOptions,
|
|
5
|
-
ImageType,
|
|
6
5
|
PackingFn,
|
|
7
6
|
BoundingBox,
|
|
8
7
|
SCALE_MODE_TO_OBJECT_FIT,
|
|
9
|
-
Size,
|
|
10
8
|
GridContainerOptions,
|
|
11
9
|
DEFAULT_GRID_CONTAINER_OPTIONS,
|
|
10
|
+
HyperNode,
|
|
12
11
|
} from '../types';
|
|
13
12
|
import { boundingBoxToPx, toPx } from './toPx';
|
|
14
13
|
import merge from 'lodash.merge';
|
|
15
|
-
import { htmlToImage } from './htmlToImage';
|
|
16
14
|
import chunk from 'lodash.chunk';
|
|
17
15
|
|
|
18
16
|
export const vboxPackingFn =
|
|
19
17
|
<EntryType extends Record<string, string>>(
|
|
20
18
|
options?: DirectionContainerOptions<EntryType>,
|
|
21
19
|
): PackingFn =>
|
|
22
|
-
(box: BoundingBox,
|
|
20
|
+
(box: BoundingBox, elements: Array<HyperNode>) =>
|
|
23
21
|
directionalPackingFn({
|
|
24
22
|
isVertical: true,
|
|
25
|
-
|
|
26
|
-
images,
|
|
23
|
+
elements,
|
|
27
24
|
box,
|
|
28
25
|
options,
|
|
29
26
|
});
|
|
@@ -32,11 +29,10 @@ export const hboxPackingFn =
|
|
|
32
29
|
<EntryType extends Record<string, string>>(
|
|
33
30
|
options?: DirectionContainerOptions<EntryType>,
|
|
34
31
|
): PackingFn =>
|
|
35
|
-
(box: BoundingBox,
|
|
32
|
+
(box: BoundingBox, elements: Array<HyperNode>) =>
|
|
36
33
|
directionalPackingFn({
|
|
37
34
|
isVertical: false,
|
|
38
|
-
|
|
39
|
-
images,
|
|
35
|
+
elements,
|
|
40
36
|
box,
|
|
41
37
|
options,
|
|
42
38
|
});
|
|
@@ -45,41 +41,15 @@ export const gridPackingFn =
|
|
|
45
41
|
<EntryType extends Record<string, string>>(
|
|
46
42
|
options?: GridContainerOptions<EntryType>,
|
|
47
43
|
): PackingFn =>
|
|
48
|
-
async (
|
|
49
|
-
box: BoundingBox,
|
|
50
|
-
background: ImageType,
|
|
51
|
-
images: Array<ImageType>,
|
|
52
|
-
) => {
|
|
44
|
+
async (box: BoundingBox, elements: Array<HyperNode>) => {
|
|
53
45
|
const mergedOptions = merge(
|
|
54
46
|
{},
|
|
55
47
|
DEFAULT_GRID_CONTAINER_OPTIONS,
|
|
56
48
|
options,
|
|
57
49
|
);
|
|
58
|
-
const objectFit = SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
|
|
59
|
-
|
|
60
|
-
const children = await Promise.all(
|
|
61
|
-
images.map(async image => {
|
|
62
|
-
const imageBase64 = await image.getBase64('image/png');
|
|
63
|
-
return h(
|
|
64
|
-
'img',
|
|
65
|
-
{
|
|
66
|
-
style: {
|
|
67
|
-
objectFit,
|
|
68
|
-
flex: '1 1 auto',
|
|
69
|
-
minWidth: 0,
|
|
70
|
-
minHeight: 0,
|
|
71
|
-
maxWidth: '100%',
|
|
72
|
-
maxHeight: '100%',
|
|
73
|
-
},
|
|
74
|
-
src: imageBase64,
|
|
75
|
-
},
|
|
76
|
-
[],
|
|
77
|
-
);
|
|
78
|
-
}),
|
|
79
|
-
);
|
|
80
50
|
|
|
81
51
|
const items = [];
|
|
82
|
-
for (const subset of chunk(
|
|
52
|
+
for (const subset of chunk(elements)) {
|
|
83
53
|
items.push(
|
|
84
54
|
h(
|
|
85
55
|
'div',
|
|
@@ -98,7 +68,7 @@ export const gridPackingFn =
|
|
|
98
68
|
),
|
|
99
69
|
);
|
|
100
70
|
}
|
|
101
|
-
|
|
71
|
+
return h(
|
|
102
72
|
'div',
|
|
103
73
|
{
|
|
104
74
|
style: {
|
|
@@ -112,51 +82,26 @@ export const gridPackingFn =
|
|
|
112
82
|
},
|
|
113
83
|
items,
|
|
114
84
|
);
|
|
115
|
-
|
|
116
|
-
return htmlToImage(document, background);
|
|
117
85
|
};
|
|
118
86
|
|
|
119
87
|
const directionalPackingFn = async <EntryType extends Record<string, string>>({
|
|
120
88
|
isVertical,
|
|
121
|
-
|
|
122
|
-
images,
|
|
89
|
+
elements,
|
|
123
90
|
box,
|
|
124
91
|
options,
|
|
125
92
|
}: {
|
|
126
93
|
isVertical: boolean;
|
|
127
|
-
|
|
128
|
-
images: Array<ImageType>;
|
|
94
|
+
elements: Array<HyperNode>;
|
|
129
95
|
box: BoundingBox;
|
|
130
96
|
options?: DirectionContainerOptions<EntryType>;
|
|
131
|
-
}): Promise<
|
|
97
|
+
}): Promise<HyperNode> => {
|
|
132
98
|
const mergedOptions = merge(
|
|
133
99
|
{},
|
|
134
100
|
DEFAULT_DIRECTION_CONTAINER_OPTIONS,
|
|
135
101
|
options,
|
|
136
102
|
);
|
|
137
|
-
const objectFit = SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
|
|
138
103
|
|
|
139
|
-
|
|
140
|
-
images.map(async image => {
|
|
141
|
-
const imageBase64 = await image.getBase64('image/png');
|
|
142
|
-
return h(
|
|
143
|
-
'img',
|
|
144
|
-
{
|
|
145
|
-
style: {
|
|
146
|
-
objectFit,
|
|
147
|
-
flex: '1 1 auto',
|
|
148
|
-
minWidth: 0,
|
|
149
|
-
minHeight: 0,
|
|
150
|
-
maxWidth: '100%',
|
|
151
|
-
maxHeight: '100%',
|
|
152
|
-
},
|
|
153
|
-
src: imageBase64,
|
|
154
|
-
},
|
|
155
|
-
[],
|
|
156
|
-
);
|
|
157
|
-
}),
|
|
158
|
-
);
|
|
159
|
-
const document = h(
|
|
104
|
+
return h(
|
|
160
105
|
'div',
|
|
161
106
|
{
|
|
162
107
|
style: {
|
|
@@ -172,8 +117,6 @@ const directionalPackingFn = async <EntryType extends Record<string, string>>({
|
|
|
172
117
|
...boundingBoxToPx(box),
|
|
173
118
|
},
|
|
174
119
|
},
|
|
175
|
-
|
|
120
|
+
elements,
|
|
176
121
|
);
|
|
177
|
-
|
|
178
|
-
return htmlToImage(document, background);
|
|
179
122
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { create as createElement
|
|
1
|
+
import { create as createElement } from 'virtual-dom';
|
|
2
2
|
import nodeHtmlToImage from 'node-html-to-image';
|
|
3
3
|
import { Jimp } from 'jimp';
|
|
4
|
-
import { ImageType, Size } from '../types';
|
|
4
|
+
import { HyperNode, ImageType, Size } from '../types';
|
|
5
5
|
|
|
6
6
|
export const htmlToImage = async (
|
|
7
|
-
document:
|
|
7
|
+
document: HyperNode,
|
|
8
8
|
backgroundSize: Size,
|
|
9
9
|
): Promise<ImageType> => {
|
|
10
10
|
const html = createElement(document).toString();
|
package/src/lib/utils/index.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { h } from 'virtual-dom';
|
|
2
|
-
import { htmlToImage } from './htmlToImage';
|
|
3
2
|
import { boundingBoxToPx } from './toPx';
|
|
4
|
-
import {
|
|
3
|
+
import { BoundingBox, HyperNode } from '../types';
|
|
5
4
|
|
|
6
|
-
export const
|
|
7
|
-
|
|
8
|
-
imageSize: Size,
|
|
9
|
-
): Promise<ImageType> => {
|
|
10
|
-
const document = h(
|
|
5
|
+
export const placeBoundingBox = async (box: BoundingBox): Promise<HyperNode> => {
|
|
6
|
+
return h(
|
|
11
7
|
'div',
|
|
12
8
|
{
|
|
13
9
|
style: {
|
|
@@ -20,6 +16,4 @@ export const drawBoundingBox = async (
|
|
|
20
16
|
},
|
|
21
17
|
[],
|
|
22
18
|
);
|
|
23
|
-
|
|
24
|
-
return htmlToImage(document, imageSize);
|
|
25
19
|
};
|
|
@@ -5,27 +5,23 @@ import {
|
|
|
5
5
|
ImageType,
|
|
6
6
|
BoundingBox,
|
|
7
7
|
SCALE_MODE_TO_OBJECT_FIT,
|
|
8
|
+
HyperNode,
|
|
9
|
+
ImageLayerSpecificOptions,
|
|
8
10
|
} from '../types';
|
|
9
|
-
import { htmlToImage } from './htmlToImage';
|
|
10
11
|
import { RequiredDeep } from 'type-fest';
|
|
11
12
|
|
|
12
13
|
type PlaceImageProps<EntryType extends Record<string, string>> = {
|
|
13
14
|
image: ImageType;
|
|
14
15
|
box: BoundingBox;
|
|
15
|
-
backgroundSize: { width: number; height: number };
|
|
16
16
|
options: RequiredDeep<ImageLayerOptions<EntryType>>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export const placeImage = async <EntryType extends Record<string, string>>({
|
|
20
20
|
image,
|
|
21
21
|
box,
|
|
22
|
-
backgroundSize,
|
|
23
22
|
options,
|
|
24
|
-
}: PlaceImageProps<EntryType>): Promise<
|
|
25
|
-
|
|
26
|
-
const objectFit = SCALE_MODE_TO_OBJECT_FIT[options.scale];
|
|
27
|
-
|
|
28
|
-
const document = h(
|
|
23
|
+
}: PlaceImageProps<EntryType>): Promise<HyperNode> => {
|
|
24
|
+
return h(
|
|
29
25
|
'div',
|
|
30
26
|
{
|
|
31
27
|
style: {
|
|
@@ -39,24 +35,35 @@ export const placeImage = async <EntryType extends Record<string, string>>({
|
|
|
39
35
|
...boundingBoxToPx(box),
|
|
40
36
|
},
|
|
41
37
|
},
|
|
42
|
-
[
|
|
43
|
-
h(
|
|
44
|
-
'img',
|
|
45
|
-
{
|
|
46
|
-
style: {
|
|
47
|
-
objectFit,
|
|
48
|
-
flex: '1 1 auto',
|
|
49
|
-
minWidth: 0,
|
|
50
|
-
minHeight: 0,
|
|
51
|
-
maxWidth: '100%',
|
|
52
|
-
maxHeight: '100%',
|
|
53
|
-
},
|
|
54
|
-
src: imageBase64,
|
|
55
|
-
},
|
|
56
|
-
[],
|
|
57
|
-
),
|
|
58
|
-
],
|
|
38
|
+
[await prepareImage({ image, options })],
|
|
59
39
|
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type PrepareImageProps<EntryType extends Record<string, string>> = {
|
|
43
|
+
image: ImageType;
|
|
44
|
+
options: RequiredDeep<ImageLayerSpecificOptions<EntryType>>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const prepareImage = async <EntryType extends Record<string, string>>({
|
|
48
|
+
image,
|
|
49
|
+
options,
|
|
50
|
+
}: PrepareImageProps<EntryType>): Promise<HyperNode> => {
|
|
51
|
+
const imageBase64 = await image.getBase64('image/png');
|
|
52
|
+
const objectFit = SCALE_MODE_TO_OBJECT_FIT[options.scale];
|
|
60
53
|
|
|
61
|
-
return
|
|
62
|
-
|
|
54
|
+
return h(
|
|
55
|
+
'img',
|
|
56
|
+
{
|
|
57
|
+
style: {
|
|
58
|
+
objectFit,
|
|
59
|
+
flex: '1 1 auto',
|
|
60
|
+
minWidth: 0,
|
|
61
|
+
minHeight: 0,
|
|
62
|
+
maxWidth: '100%',
|
|
63
|
+
maxHeight: '100%',
|
|
64
|
+
},
|
|
65
|
+
src: imageBase64,
|
|
66
|
+
},
|
|
67
|
+
[],
|
|
68
|
+
);
|
|
69
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { h } from 'virtual-dom';
|
|
2
|
+
import {
|
|
3
|
+
BoundingBox,
|
|
4
|
+
TextLayerOptions,
|
|
5
|
+
HyperNode,
|
|
6
|
+
TextLayerSpecificOptions,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import { boundingBoxToPx, toPx } from './toPx';
|
|
9
|
+
import { RequiredDeep } from 'type-fest';
|
|
10
|
+
|
|
11
|
+
type PlaceTextProps<EntryType extends Record<string, string>> = {
|
|
12
|
+
text: string;
|
|
13
|
+
box: BoundingBox;
|
|
14
|
+
options: RequiredDeep<TextLayerOptions<EntryType>>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const placeText = async <EntryType extends Record<string, string>>({
|
|
18
|
+
text,
|
|
19
|
+
box,
|
|
20
|
+
options,
|
|
21
|
+
}: PlaceTextProps<EntryType>): Promise<HyperNode> => {
|
|
22
|
+
return h(
|
|
23
|
+
'div',
|
|
24
|
+
{
|
|
25
|
+
style: {
|
|
26
|
+
display: 'flex',
|
|
27
|
+
overflow: 'visible',
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
|
|
30
|
+
justifyContent: options.justifyContent,
|
|
31
|
+
alignItems: options.alignItems,
|
|
32
|
+
|
|
33
|
+
...boundingBoxToPx(box),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
[
|
|
37
|
+
await prepareText({
|
|
38
|
+
text,
|
|
39
|
+
options,
|
|
40
|
+
}),
|
|
41
|
+
],
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type PrepareTextProps<EntryType extends Record<string, string>> = {
|
|
46
|
+
text: string;
|
|
47
|
+
options: RequiredDeep<TextLayerSpecificOptions<EntryType>>;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const prepareText = async <EntryType extends Record<string, string>>({
|
|
51
|
+
text,
|
|
52
|
+
options,
|
|
53
|
+
}: PrepareTextProps<EntryType>): Promise<HyperNode> => {
|
|
54
|
+
let textChildren: Array<string | HyperNode> = [text];
|
|
55
|
+
for (const [word, image] of Object.entries(options.replacement)) {
|
|
56
|
+
const regex = new RegExp(word, 'gi');
|
|
57
|
+
const imageBase64 = await image.getBase64('image/png');
|
|
58
|
+
|
|
59
|
+
let tmpChildren: Array<string | HyperNode> = [];
|
|
60
|
+
for (const textSegment of textChildren) {
|
|
61
|
+
if (typeof textSegment !== 'string') {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const parts = (textSegment as string).split(regex);
|
|
66
|
+
for (let index = 0; index < parts.length; index++) {
|
|
67
|
+
if (index > 0) {
|
|
68
|
+
tmpChildren.push(
|
|
69
|
+
h(
|
|
70
|
+
'img',
|
|
71
|
+
{
|
|
72
|
+
style: {
|
|
73
|
+
display: 'inline',
|
|
74
|
+
verticalAlign: 'middle',
|
|
75
|
+
height: toPx(options.font.size),
|
|
76
|
+
width: 'auto',
|
|
77
|
+
},
|
|
78
|
+
src: imageBase64,
|
|
79
|
+
},
|
|
80
|
+
[],
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
tmpChildren.push(parts[index]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
textChildren = tmpChildren;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return h(
|
|
92
|
+
'div',
|
|
93
|
+
{
|
|
94
|
+
style: {
|
|
95
|
+
overflow: 'visible',
|
|
96
|
+
overflowWrap: 'word-wrap',
|
|
97
|
+
whiteSpace: 'normal',
|
|
98
|
+
|
|
99
|
+
color: options.color,
|
|
100
|
+
fontFamily: options.font.family,
|
|
101
|
+
fontSize: options.font.size,
|
|
102
|
+
fontStyle: options.font.italic ? 'italic' : undefined,
|
|
103
|
+
fontWeight: options.font.bold ? 'bold' : undefined,
|
|
104
|
+
|
|
105
|
+
'-webkit-text-stroke': `${options.border.width}px ${options.border.color}`,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
textChildren,
|
|
109
|
+
);
|
|
110
|
+
};
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { h, VNode } from 'virtual-dom';
|
|
2
|
-
import { ImageType, BoundingBox, Size, TextLayerOptions } from '../types';
|
|
3
|
-
import { boundingBoxToPx, toPx } from './toPx';
|
|
4
|
-
import { htmlToImage } from './htmlToImage';
|
|
5
|
-
import { RequiredDeep } from 'type-fest';
|
|
6
|
-
|
|
7
|
-
export const renderText = async <EntryType extends Record<string, string>>(
|
|
8
|
-
text: string,
|
|
9
|
-
box: BoundingBox,
|
|
10
|
-
backgroundSize: Size,
|
|
11
|
-
options: RequiredDeep<TextLayerOptions<EntryType>>,
|
|
12
|
-
fonts: Record<string, string>,
|
|
13
|
-
): Promise<ImageType> => {
|
|
14
|
-
let textChildren: Array<string | VNode> = [text];
|
|
15
|
-
for (const [word, image] of Object.entries(options.replacement)) {
|
|
16
|
-
const regex = new RegExp(word, 'gi');
|
|
17
|
-
const imageBase64 = await image.getBase64('image/png');
|
|
18
|
-
|
|
19
|
-
let tmpChildren: Array<string | VNode> = [];
|
|
20
|
-
for (const textSegment of textChildren) {
|
|
21
|
-
if (typeof textSegment !== 'string') {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const parts = (textSegment as string).split(regex);
|
|
26
|
-
for (let index = 0; index < parts.length; index++) {
|
|
27
|
-
if (index > 0) {
|
|
28
|
-
tmpChildren.push(
|
|
29
|
-
h(
|
|
30
|
-
'img',
|
|
31
|
-
{
|
|
32
|
-
style: {
|
|
33
|
-
display: 'inline',
|
|
34
|
-
verticalAlign: 'middle',
|
|
35
|
-
height: toPx(options.font.size),
|
|
36
|
-
width: 'auto',
|
|
37
|
-
},
|
|
38
|
-
src: imageBase64,
|
|
39
|
-
},
|
|
40
|
-
[],
|
|
41
|
-
),
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
tmpChildren.push(parts[index]);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
textChildren = tmpChildren;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const content = h(
|
|
52
|
-
'div',
|
|
53
|
-
{
|
|
54
|
-
style: {
|
|
55
|
-
display: 'flex',
|
|
56
|
-
overflow: 'visible',
|
|
57
|
-
position: 'absolute',
|
|
58
|
-
|
|
59
|
-
justifyContent: options.justifyContent,
|
|
60
|
-
alignItems: options.alignItems,
|
|
61
|
-
|
|
62
|
-
...boundingBoxToPx(box),
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
[
|
|
66
|
-
h(
|
|
67
|
-
'div',
|
|
68
|
-
{
|
|
69
|
-
style: {
|
|
70
|
-
overflow: 'visible',
|
|
71
|
-
overflowWrap: 'word-wrap',
|
|
72
|
-
whiteSpace: 'normal',
|
|
73
|
-
|
|
74
|
-
color: options.color,
|
|
75
|
-
fontFamily: options.font.family,
|
|
76
|
-
fontSize: options.font.size,
|
|
77
|
-
fontStyle: options.font.italic ? 'italic' : undefined,
|
|
78
|
-
fontWeight: options.font.bold ? 'bold' : undefined,
|
|
79
|
-
|
|
80
|
-
'-webkit-text-stroke': `${options.border.width}px ${options.border.color}`
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
textChildren,
|
|
84
|
-
),
|
|
85
|
-
],
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
const document = h('html', [
|
|
89
|
-
h('head', [
|
|
90
|
-
h(
|
|
91
|
-
'style',
|
|
92
|
-
Object.entries(fonts)
|
|
93
|
-
.map(
|
|
94
|
-
([name, data]) =>
|
|
95
|
-
`@font-face {
|
|
96
|
-
font-family: '${name}';
|
|
97
|
-
src: url(data:font/ttf;base64,${data}) format('truetype');
|
|
98
|
-
}`,
|
|
99
|
-
)
|
|
100
|
-
.join('\n'),
|
|
101
|
-
),
|
|
102
|
-
]),
|
|
103
|
-
h('body', [content]),
|
|
104
|
-
]);
|
|
105
|
-
|
|
106
|
-
return htmlToImage(document, backgroundSize);
|
|
107
|
-
};
|