@hellkite/pipkin 0.5.0 → 0.5.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/build/src/lib/bundler.d.ts +1 -2
- package/build/src/lib/bundler.js.map +1 -1
- package/build/src/lib/replacement.d.ts +1 -2
- package/build/src/lib/replacement.js.map +1 -1
- package/build/src/lib/template.d.ts +13 -14
- package/build/src/lib/template.js +102 -37
- package/build/src/lib/template.js.map +1 -1
- package/build/src/lib/types/2d.d.ts +4 -5
- package/build/src/lib/types/boundingBox.d.ts +19 -0
- package/build/src/lib/types/boundingBox.js +3 -0
- package/build/src/lib/types/boundingBox.js.map +1 -0
- package/build/src/lib/types/containers.d.ts +13 -27
- package/build/src/lib/types/containers.js +3 -2
- package/build/src/lib/types/containers.js.map +1 -1
- package/build/src/lib/types/css.d.ts +4 -1
- package/build/src/lib/types/css.js +6 -3
- package/build/src/lib/types/css.js.map +1 -1
- package/build/src/lib/types/image.d.ts +17 -38
- package/build/src/lib/types/image.js +8 -2
- package/build/src/lib/types/image.js.map +1 -1
- package/build/src/lib/types/index.d.ts +3 -2
- package/build/src/lib/types/index.js +3 -2
- package/build/src/lib/types/index.js.map +1 -1
- package/build/src/lib/types/layer.d.ts +15 -0
- package/build/src/lib/types/layer.js +3 -0
- package/build/src/lib/types/layer.js.map +1 -0
- package/build/src/lib/types/position.d.ts +21 -0
- package/build/src/lib/types/position.js +42 -0
- package/build/src/lib/types/position.js.map +1 -0
- package/build/src/lib/types/scale.d.ts +9 -1
- package/build/src/lib/types/scale.js +2 -0
- package/build/src/lib/types/scale.js.map +1 -1
- package/build/src/lib/types/size.d.ts +4 -0
- package/build/src/lib/types/size.js +3 -0
- package/build/src/lib/types/size.js.map +1 -0
- package/build/src/lib/types/text.d.ts +29 -33
- package/build/src/lib/types/text.js +13 -4
- package/build/src/lib/types/text.js.map +1 -1
- package/build/src/lib/utils/buildFontString.d.ts +2 -2
- package/build/src/lib/utils/buildFontString.js.map +1 -1
- package/build/src/lib/utils/canvasToImage.d.ts +1 -1
- package/build/src/lib/utils/container.d.ts +4 -4
- package/build/src/lib/utils/container.js +52 -46
- package/build/src/lib/utils/container.js.map +1 -1
- package/build/src/lib/utils/drawBoundingBox.d.ts +1 -2
- package/build/src/lib/utils/drawBoundingBox.js +7 -47
- package/build/src/lib/utils/drawBoundingBox.js.map +1 -1
- package/build/src/lib/utils/htmlToImage.d.ts +3 -0
- package/build/src/lib/utils/htmlToImage.js +36 -0
- package/build/src/lib/utils/htmlToImage.js.map +1 -0
- package/build/src/lib/utils/index.d.ts +2 -1
- package/build/src/lib/utils/index.js +2 -1
- package/build/src/lib/utils/index.js.map +1 -1
- package/build/src/lib/utils/placeImage.d.ts +10 -5
- package/build/src/lib/utils/placeImage.js +25 -36
- package/build/src/lib/utils/placeImage.js.map +1 -1
- package/build/src/lib/utils/renderText.d.ts +3 -2
- package/build/src/lib/utils/renderText.js +12 -39
- package/build/src/lib/utils/renderText.js.map +1 -1
- package/build/src/lib/utils/toPx.d.ts +3 -0
- package/build/src/lib/utils/toPx.js +12 -0
- package/build/src/lib/utils/toPx.js.map +1 -0
- package/build/src/lib/utils/vNodeToImage.d.ts +4 -0
- package/build/src/lib/utils/vNodeToImage.js +36 -0
- package/build/src/lib/utils/vNodeToImage.js.map +1 -0
- package/build/src/test.js +55 -69
- package/build/src/test.js.map +1 -1
- package/package.json +6 -3
- package/roadmap.md +0 -1
- package/src/lib/bundler.ts +1 -2
- package/src/lib/replacement.ts +1 -2
- package/src/lib/template.ts +190 -68
- package/src/lib/types/boundingBox.ts +28 -0
- package/src/lib/types/containers.ts +55 -60
- package/src/lib/types/css.ts +32 -1
- package/src/lib/types/image.ts +32 -57
- package/src/lib/types/index.ts +3 -2
- package/src/lib/types/layer.ts +18 -0
- package/src/lib/types/scale.ts +11 -0
- package/src/lib/types/size.ts +4 -0
- package/src/lib/types/text.ts +47 -56
- package/src/lib/utils/buildFontString.ts +2 -2
- package/src/lib/utils/container.ts +118 -57
- package/src/lib/utils/drawBoundingBox.ts +19 -16
- package/src/lib/utils/htmlToImage.ts +24 -0
- package/src/lib/utils/index.ts +2 -1
- package/src/lib/utils/placeImage.ts +55 -58
- package/src/lib/utils/renderText.ts +19 -43
- package/src/lib/utils/toPx.ts +11 -0
- package/src/lib/types/2d.ts +0 -11
- package/src/lib/types/omitArgument.ts +0 -17
- package/src/lib/utils/canvasToImage.ts +0 -19
package/src/lib/types/text.ts
CHANGED
|
@@ -1,67 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RequiredDeep } from 'type-fest';
|
|
2
|
+
import { LayerOptions } from './layer';
|
|
2
3
|
import { ReplacementMap } from './replacement';
|
|
3
4
|
|
|
4
|
-
export type
|
|
5
|
-
key:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type TextLayerOptions = {
|
|
11
|
-
font?: {
|
|
12
|
-
/**
|
|
13
|
-
* Size of font is represented in pixels
|
|
14
|
-
*/
|
|
15
|
-
size?: number;
|
|
16
|
-
/**
|
|
17
|
-
* Either use one supported by canvas
|
|
18
|
-
* or load a custom one before rendering
|
|
19
|
-
*/
|
|
20
|
-
family?: string;
|
|
21
|
-
/**
|
|
22
|
-
* Warning: Not supported by all fonts
|
|
23
|
-
*/
|
|
24
|
-
bold?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Warning: Not supported by all fonts
|
|
27
|
-
*/
|
|
28
|
-
italic?: boolean;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
color?: string;
|
|
32
|
-
|
|
33
|
-
// TODO: processor fn
|
|
34
|
-
replacement?: ReplacementMap;
|
|
5
|
+
export type TextRef<EntryType> =
|
|
6
|
+
| { key: string }
|
|
7
|
+
| { text: string }
|
|
8
|
+
| { textFn: (entry: EntryType) => string };
|
|
35
9
|
|
|
10
|
+
export type FontOptions = {
|
|
36
11
|
/**
|
|
37
|
-
*
|
|
12
|
+
* Size of font is represented in pixels
|
|
38
13
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
| 'justify-center'
|
|
46
|
-
| 'justify-right';
|
|
47
|
-
|
|
14
|
+
size?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Either use one supported by canvas
|
|
17
|
+
* or load a custom one before rendering
|
|
18
|
+
*/
|
|
19
|
+
family?: string;
|
|
48
20
|
/**
|
|
49
|
-
*
|
|
21
|
+
* Warning: Not supported by all fonts
|
|
50
22
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
| 'justify-left'
|
|
57
|
-
| 'justify-center'
|
|
58
|
-
| 'justify-right';
|
|
23
|
+
bold?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Warning: Not supported by all fonts
|
|
26
|
+
*/
|
|
27
|
+
italic?: boolean;
|
|
59
28
|
};
|
|
60
29
|
|
|
61
|
-
export type
|
|
30
|
+
export type TextLayerOptions<EntryType extends Record<string, string>> =
|
|
31
|
+
LayerOptions<EntryType> & {
|
|
32
|
+
font?: FontOptions;
|
|
33
|
+
|
|
34
|
+
color?: string;
|
|
35
|
+
|
|
36
|
+
// TODO: processor fn
|
|
37
|
+
replacement?: ReplacementMap;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const DEFAULT_FONT: Required<
|
|
41
|
+
FontOptions
|
|
42
|
+
> = {
|
|
43
|
+
size: 28,
|
|
44
|
+
family: 'Arial',
|
|
45
|
+
bold: false,
|
|
46
|
+
italic: false,
|
|
47
|
+
};
|
|
62
48
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
49
|
+
export const DEFAULT_TEXT_LAYER_OPTIONS: RequiredDeep<
|
|
50
|
+
TextLayerOptions<Record<string, string>>
|
|
51
|
+
> = {
|
|
52
|
+
justifyContent: 'center',
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
font: DEFAULT_FONT,
|
|
55
|
+
color: 'black',
|
|
56
|
+
replacement: {},
|
|
57
|
+
skip: false,
|
|
67
58
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FontOptions } from "../types";
|
|
2
2
|
|
|
3
3
|
export const buildFontString = (
|
|
4
|
-
font:
|
|
4
|
+
font: FontOptions,
|
|
5
5
|
defaultFontFamily?: string,
|
|
6
6
|
): string => {
|
|
7
7
|
const fragments: string[] = [];
|
|
@@ -1,52 +1,141 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { h } from 'virtual-dom';
|
|
2
2
|
import {
|
|
3
|
-
DEFAULT_CONTAINER_OPTIONS,
|
|
4
3
|
DEFAULT_DIRECTION_CONTAINER_OPTIONS,
|
|
5
4
|
DirectionContainerOptions,
|
|
5
|
+
ImageType,
|
|
6
6
|
PackingFn,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
BoundingBox,
|
|
8
|
+
SCALE_MODE_TO_OBJECT_FIT,
|
|
9
|
+
Size,
|
|
10
|
+
GridContainerOptions,
|
|
11
|
+
DEFAULT_GRID_CONTAINER_OPTIONS,
|
|
12
|
+
} from '../types';
|
|
13
|
+
import { boundingBoxToPx, toPx } from './toPx';
|
|
14
|
+
import merge from 'lodash.merge';
|
|
15
|
+
import { htmlToImage } from './htmlToImage';
|
|
16
|
+
import chunk from 'lodash.chunk';
|
|
13
17
|
|
|
14
18
|
export const vboxPackingFn =
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
<EntryType extends Record<string, string>>(
|
|
20
|
+
options?: DirectionContainerOptions<EntryType>,
|
|
21
|
+
): PackingFn =>
|
|
22
|
+
(box: BoundingBox, background: ImageType, images: Array<ImageType>) =>
|
|
17
23
|
directionalPackingFn({
|
|
18
24
|
isVertical: true,
|
|
19
|
-
background,
|
|
25
|
+
backgroundSize: background,
|
|
20
26
|
images,
|
|
21
|
-
|
|
27
|
+
box,
|
|
22
28
|
options,
|
|
23
29
|
});
|
|
24
30
|
|
|
25
31
|
export const hboxPackingFn =
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
<EntryType extends Record<string, string>>(
|
|
33
|
+
options?: DirectionContainerOptions<EntryType>,
|
|
34
|
+
): PackingFn =>
|
|
35
|
+
(box: BoundingBox, background: ImageType, images: Array<ImageType>) =>
|
|
28
36
|
directionalPackingFn({
|
|
29
37
|
isVertical: false,
|
|
30
|
-
background,
|
|
38
|
+
backgroundSize: background,
|
|
31
39
|
images,
|
|
32
|
-
|
|
40
|
+
box,
|
|
33
41
|
options,
|
|
34
42
|
});
|
|
35
43
|
|
|
36
|
-
const
|
|
44
|
+
export const gridPackingFn =
|
|
45
|
+
<EntryType extends Record<string, string>>(
|
|
46
|
+
options?: GridContainerOptions<EntryType>,
|
|
47
|
+
): PackingFn =>
|
|
48
|
+
async (
|
|
49
|
+
box: BoundingBox,
|
|
50
|
+
background: ImageType,
|
|
51
|
+
images: Array<ImageType>,
|
|
52
|
+
) => {
|
|
53
|
+
const mergedOptions = merge(
|
|
54
|
+
{},
|
|
55
|
+
DEFAULT_GRID_CONTAINER_OPTIONS,
|
|
56
|
+
options,
|
|
57
|
+
);
|
|
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
|
+
|
|
81
|
+
const items = [];
|
|
82
|
+
for (const subset of chunk(children)) {
|
|
83
|
+
items.push(
|
|
84
|
+
h(
|
|
85
|
+
'div',
|
|
86
|
+
{
|
|
87
|
+
style: {
|
|
88
|
+
display: 'flex',
|
|
89
|
+
flexDirection: 'row',
|
|
90
|
+
overflow: 'hidden',
|
|
91
|
+
|
|
92
|
+
gap: toPx(mergedOptions.gap),
|
|
93
|
+
justifyContent: mergedOptions.justifyContent,
|
|
94
|
+
alignItems: mergedOptions.alignItems,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
subset,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const document = h(
|
|
102
|
+
'div',
|
|
103
|
+
{
|
|
104
|
+
style: {
|
|
105
|
+
display: 'grid',
|
|
106
|
+
gridTemplateColumns: `repeat(${mergedOptions.size}, 1fr)`,
|
|
107
|
+
position: 'absolute',
|
|
108
|
+
|
|
109
|
+
gap: toPx(mergedOptions.gap),
|
|
110
|
+
...boundingBoxToPx(box),
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
items,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
return htmlToImage(document, background);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const directionalPackingFn = async <EntryType extends Record<string, string>>({
|
|
37
120
|
isVertical,
|
|
38
|
-
background,
|
|
121
|
+
backgroundSize: background,
|
|
39
122
|
images,
|
|
40
|
-
|
|
123
|
+
box,
|
|
41
124
|
options,
|
|
42
125
|
}: {
|
|
43
126
|
isVertical: boolean;
|
|
44
|
-
|
|
127
|
+
backgroundSize: Size;
|
|
45
128
|
images: Array<ImageType>;
|
|
46
|
-
|
|
47
|
-
options?: DirectionContainerOptions
|
|
129
|
+
box: BoundingBox;
|
|
130
|
+
options?: DirectionContainerOptions<EntryType>;
|
|
48
131
|
}): Promise<ImageType> => {
|
|
49
|
-
const
|
|
132
|
+
const mergedOptions = merge(
|
|
133
|
+
{},
|
|
134
|
+
DEFAULT_DIRECTION_CONTAINER_OPTIONS,
|
|
135
|
+
options,
|
|
136
|
+
);
|
|
137
|
+
const objectFit = SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
|
|
138
|
+
|
|
50
139
|
const children = await Promise.all(
|
|
51
140
|
images.map(async image => {
|
|
52
141
|
const imageBase64 = await image.getBase64('image/png');
|
|
@@ -75,44 +164,16 @@ const directionalPackingFn = async ({
|
|
|
75
164
|
position: 'absolute',
|
|
76
165
|
scale: 1,
|
|
77
166
|
|
|
78
|
-
flexDirection: `${isVertical ? 'column' : 'row'}${
|
|
79
|
-
gap: toPx(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
options?.justifyContent ??
|
|
83
|
-
DEFAULT_DIRECTION_CONTAINER_OPTIONS.justifyContent,
|
|
84
|
-
alignItems:
|
|
85
|
-
options?.alignItems ??
|
|
86
|
-
DEFAULT_DIRECTION_CONTAINER_OPTIONS.alignItems,
|
|
167
|
+
flexDirection: `${isVertical ? 'column' : 'row'}${mergedOptions.reversed ? '-reversed' : ''}`,
|
|
168
|
+
gap: toPx(mergedOptions.gap),
|
|
169
|
+
justifyContent: mergedOptions.justifyContent,
|
|
170
|
+
alignItems: mergedOptions.alignItems,
|
|
87
171
|
|
|
88
|
-
|
|
89
|
-
left: toPx(position.x),
|
|
90
|
-
height: toPx(position.height),
|
|
91
|
-
width: toPx(position.width),
|
|
172
|
+
...boundingBoxToPx(box),
|
|
92
173
|
},
|
|
93
174
|
},
|
|
94
175
|
children,
|
|
95
176
|
);
|
|
96
177
|
|
|
97
|
-
|
|
98
|
-
// TODO: extract this in a dif function
|
|
99
|
-
const image = await nodeHtmlToImage({
|
|
100
|
-
html: rootNode.toString(),
|
|
101
|
-
transparent: true,
|
|
102
|
-
type: 'png',
|
|
103
|
-
puppeteerArgs: {
|
|
104
|
-
defaultViewport: {
|
|
105
|
-
width: background.width,
|
|
106
|
-
height: background.height,
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
return Jimp.read(image as Buffer) as Promise<ImageType>;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const SCALE_MODE_TO_OBJECT_FIT: Record<ScaleMode, string> = {
|
|
115
|
-
'keep-ratio': 'contain',
|
|
116
|
-
stretch: 'fill',
|
|
117
|
-
none: 'none',
|
|
178
|
+
return htmlToImage(document, background);
|
|
118
179
|
};
|
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { ImageType } from '../types
|
|
1
|
+
import { h } from 'virtual-dom';
|
|
2
|
+
import { htmlToImage } from './htmlToImage';
|
|
3
|
+
import { boundingBoxToPx } from './toPx';
|
|
4
|
+
import { ImageType, Size, BoundingBox } from '../types';
|
|
5
5
|
|
|
6
6
|
export const drawBoundingBox = async (
|
|
7
7
|
box: BoundingBox,
|
|
8
8
|
imageSize: Size,
|
|
9
9
|
): Promise<ImageType> => {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
const document = h(
|
|
11
|
+
'div',
|
|
12
|
+
{
|
|
13
|
+
style: {
|
|
14
|
+
position: 'absolute',
|
|
15
|
+
border: '2px solid red',
|
|
16
|
+
background: 'transparent',
|
|
17
|
+
boxSizing: 'border-box',
|
|
18
|
+
...boundingBoxToPx(box),
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
[],
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return htmlToImage(document, imageSize);
|
|
22
25
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { create as createElement, VNode } from 'virtual-dom';
|
|
2
|
+
import nodeHtmlToImage from 'node-html-to-image';
|
|
3
|
+
import { Jimp } from 'jimp';
|
|
4
|
+
import { ImageType, Size } from '../types';
|
|
5
|
+
|
|
6
|
+
export const htmlToImage = async (
|
|
7
|
+
document: VNode,
|
|
8
|
+
backgroundSize: Size,
|
|
9
|
+
): Promise<ImageType> => {
|
|
10
|
+
const html = createElement(document).toString();
|
|
11
|
+
const image = await nodeHtmlToImage({
|
|
12
|
+
html,
|
|
13
|
+
transparent: true,
|
|
14
|
+
type: 'png',
|
|
15
|
+
puppeteerArgs: {
|
|
16
|
+
defaultViewport: {
|
|
17
|
+
width: backgroundSize.width,
|
|
18
|
+
height: backgroundSize.height,
|
|
19
|
+
deviceScaleFactor: 1,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
return Jimp.read(image as Buffer) as Promise<ImageType>;
|
|
24
|
+
};
|
package/src/lib/utils/index.ts
CHANGED
|
@@ -1,65 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { h } from 'virtual-dom';
|
|
2
|
+
import { boundingBoxToPx } from './toPx';
|
|
3
3
|
import {
|
|
4
|
-
Alignment,
|
|
5
|
-
ImageType,
|
|
6
|
-
ImagePosition,
|
|
7
4
|
ImageLayerOptions,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
size: number,
|
|
15
|
-
boxSize: number,
|
|
16
|
-
): number {
|
|
17
|
-
if (alignment === 'start') {
|
|
18
|
-
return 0;
|
|
19
|
-
} else if (alignment === 'center') {
|
|
20
|
-
return Math.floor((boxSize - size) / 2);
|
|
21
|
-
} else {
|
|
22
|
-
return -(boxSize - size);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
5
|
+
ImageType,
|
|
6
|
+
BoundingBox,
|
|
7
|
+
SCALE_MODE_TO_OBJECT_FIT,
|
|
8
|
+
} from '../types';
|
|
9
|
+
import { htmlToImage } from './htmlToImage';
|
|
10
|
+
import { RequiredDeep } from 'type-fest';
|
|
25
11
|
|
|
26
|
-
type PlaceImageProps = {
|
|
27
|
-
background: ImageType;
|
|
12
|
+
type PlaceImageProps<EntryType extends Record<string, string>> = {
|
|
28
13
|
image: ImageType;
|
|
29
|
-
|
|
14
|
+
box: BoundingBox;
|
|
15
|
+
backgroundSize: { width: number; height: number };
|
|
16
|
+
options: RequiredDeep<ImageLayerOptions<EntryType>>;
|
|
30
17
|
};
|
|
31
18
|
|
|
32
|
-
export async
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
19
|
+
export const placeImage = async <EntryType extends Record<string, string>>({
|
|
20
|
+
image,
|
|
21
|
+
box,
|
|
22
|
+
backgroundSize,
|
|
23
|
+
options,
|
|
24
|
+
}: PlaceImageProps<EntryType>): Promise<ImageType> => {
|
|
25
|
+
const imageBase64 = await image.getBase64('image/png');
|
|
26
|
+
const objectFit = SCALE_MODE_TO_OBJECT_FIT[options.scale];
|
|
27
|
+
|
|
28
|
+
const document = h(
|
|
29
|
+
'div',
|
|
30
|
+
{
|
|
31
|
+
style: {
|
|
32
|
+
display: 'flex',
|
|
33
|
+
position: 'absolute',
|
|
34
|
+
scale: 1,
|
|
35
|
+
|
|
36
|
+
justifyContent: options.justifyContent,
|
|
37
|
+
alignItems: options.alignItems,
|
|
38
|
+
|
|
39
|
+
...boundingBoxToPx(box),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
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
|
+
],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return htmlToImage(document, backgroundSize);
|
|
65
62
|
}
|
|
@@ -1,24 +1,17 @@
|
|
|
1
|
-
import { h,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
TextLayerOptions,
|
|
7
|
-
TextPosition,
|
|
8
|
-
toPx,
|
|
9
|
-
} from '../types';
|
|
10
|
-
import nodeHtmlToImage from 'node-html-to-image';
|
|
11
|
-
import { Jimp } from 'jimp';
|
|
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';
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
export const renderText = async (
|
|
7
|
+
export const renderText = async <EntryType extends Record<string, string>>(
|
|
15
8
|
text: string,
|
|
16
|
-
|
|
9
|
+
box: BoundingBox,
|
|
17
10
|
backgroundSize: Size,
|
|
18
|
-
options
|
|
11
|
+
options: RequiredDeep<TextLayerOptions<EntryType>>,
|
|
19
12
|
): Promise<ImageType> => {
|
|
20
13
|
let textChildren: Array<string | VNode> = [text];
|
|
21
|
-
for (const [word, image] of Object.entries(options
|
|
14
|
+
for (const [word, image] of Object.entries(options.replacement)) {
|
|
22
15
|
const regex = new RegExp(word, 'gi');
|
|
23
16
|
const imageBase64 = await image.getBase64('image/png');
|
|
24
17
|
|
|
@@ -38,7 +31,7 @@ export const renderText = async (
|
|
|
38
31
|
style: {
|
|
39
32
|
display: 'inline',
|
|
40
33
|
verticalAlign: 'middle',
|
|
41
|
-
height: toPx(options
|
|
34
|
+
height: toPx(options.font.size),
|
|
42
35
|
width: 'auto',
|
|
43
36
|
},
|
|
44
37
|
src: imageBase64,
|
|
@@ -62,15 +55,10 @@ export const renderText = async (
|
|
|
62
55
|
overflow: 'visible',
|
|
63
56
|
position: 'absolute',
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
height: toPx(position.height),
|
|
68
|
-
width: toPx(position.width),
|
|
58
|
+
justifyContent: options.justifyContent,
|
|
59
|
+
alignItems: options.alignItems,
|
|
69
60
|
|
|
70
|
-
|
|
71
|
-
options?.xAlign ?? DEFAULT_TEXT_LAYER_OPTIONS.xAlign,
|
|
72
|
-
alignItems:
|
|
73
|
-
options?.yAlign ?? DEFAULT_TEXT_LAYER_OPTIONS.yAlign,
|
|
61
|
+
...boundingBoxToPx(box),
|
|
74
62
|
},
|
|
75
63
|
},
|
|
76
64
|
[
|
|
@@ -82,11 +70,11 @@ export const renderText = async (
|
|
|
82
70
|
overflowWrap: 'word-wrap',
|
|
83
71
|
whiteSpace: 'normal',
|
|
84
72
|
|
|
85
|
-
color: options
|
|
86
|
-
fontFamily: options
|
|
87
|
-
fontSize: options
|
|
88
|
-
fontStyle: options
|
|
89
|
-
fontWeight: options
|
|
73
|
+
color: options.color,
|
|
74
|
+
fontFamily: options.font.family,
|
|
75
|
+
fontSize: options.font.size,
|
|
76
|
+
fontStyle: options.font.italic ? 'italic' : undefined,
|
|
77
|
+
fontWeight: options.font.bold ? 'bold' : undefined,
|
|
90
78
|
},
|
|
91
79
|
},
|
|
92
80
|
textChildren,
|
|
@@ -94,17 +82,5 @@ export const renderText = async (
|
|
|
94
82
|
],
|
|
95
83
|
);
|
|
96
84
|
|
|
97
|
-
|
|
98
|
-
const image = await nodeHtmlToImage({
|
|
99
|
-
html: rootNode.toString(),
|
|
100
|
-
transparent: true,
|
|
101
|
-
type: 'png',
|
|
102
|
-
puppeteerArgs: {
|
|
103
|
-
defaultViewport: {
|
|
104
|
-
width: backgroundSize.width,
|
|
105
|
-
height: backgroundSize.height,
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
return Jimp.read(image as Buffer) as Promise<ImageType>;
|
|
85
|
+
return htmlToImage(document, backgroundSize);
|
|
110
86
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BoundingBox } from '../types';
|
|
2
|
+
|
|
3
|
+
export const toPx = (value: number): string => `${value}px`;
|
|
4
|
+
|
|
5
|
+
export const boundingBoxToPx = (
|
|
6
|
+
boundingBox: BoundingBox,
|
|
7
|
+
): Record<string, string> => {
|
|
8
|
+
return Object.entries(boundingBox)
|
|
9
|
+
.map(([key, value]) => [key, toPx(value)])
|
|
10
|
+
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
|
|
11
|
+
};
|