@hellkite/pipkin 0.6.1 → 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 +11 -2
- package/build/src/lib/types/text.js +4 -0
- 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 -3
- package/build/src/lib/utils/index.js +2 -3
- 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/lib/utils/renderText.js +1 -0
- package/build/src/lib/utils/renderText.js.map +1 -1
- package/build/src/test.js +36 -10
- package/build/src/test.js.map +1 -1
- package/package.json +3 -1
- package/roadmap.md +3 -2
- 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 +18 -4
- package/src/lib/utils/container.ts +13 -70
- package/src/lib/utils/htmlToImage.ts +3 -3
- package/src/lib/utils/index.ts +2 -3
- 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/buildFontString.ts +0 -17
- package/src/lib/utils/renderText.ts +0 -105
package/src/lib/template.ts
CHANGED
|
@@ -3,15 +3,17 @@ import { parse as parseCsv } from 'papaparse';
|
|
|
3
3
|
import { Jimp, rgbaToInt } from 'jimp';
|
|
4
4
|
import camelCase from 'lodash.camelcase';
|
|
5
5
|
import concat from 'lodash.concat';
|
|
6
|
-
import { registerFont } from 'canvas';
|
|
7
6
|
import path from 'path';
|
|
8
7
|
import {
|
|
9
|
-
|
|
8
|
+
placeBoundingBox,
|
|
10
9
|
gridPackingFn,
|
|
11
10
|
hboxPackingFn,
|
|
11
|
+
htmlToImage,
|
|
12
12
|
placeImage,
|
|
13
|
-
|
|
13
|
+
placeText,
|
|
14
14
|
vboxPackingFn,
|
|
15
|
+
prepareText,
|
|
16
|
+
prepareImage,
|
|
15
17
|
} from './utils';
|
|
16
18
|
import {
|
|
17
19
|
DirectionContainerOptions,
|
|
@@ -31,9 +33,15 @@ import {
|
|
|
31
33
|
LayerOptions,
|
|
32
34
|
ContainerOptions,
|
|
33
35
|
DEFAULT_CONTAINER_OPTIONS,
|
|
36
|
+
HyperNode,
|
|
37
|
+
ElementsFn,
|
|
38
|
+
ElementRef,
|
|
39
|
+
ImageLayerSpecificOptions,
|
|
34
40
|
} from './types';
|
|
35
41
|
import merge from 'lodash.merge';
|
|
36
42
|
import { RequiredDeep } from 'type-fest';
|
|
43
|
+
import { h } from 'virtual-dom';
|
|
44
|
+
import flatten from 'lodash.flatten';
|
|
37
45
|
|
|
38
46
|
type RequiredTemplateOptions = {
|
|
39
47
|
height: number;
|
|
@@ -61,7 +69,7 @@ export type LayerFnContext = {
|
|
|
61
69
|
export type LayerFn<EntryType> = (
|
|
62
70
|
entry: EntryType,
|
|
63
71
|
context: LayerFnContext,
|
|
64
|
-
) => Promise<
|
|
72
|
+
) => Promise<Array<HyperNode>>;
|
|
65
73
|
|
|
66
74
|
export type TemplateLayerFn<EntryType extends Record<string, string>> = (
|
|
67
75
|
template: Template<EntryType>,
|
|
@@ -102,13 +110,6 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
102
110
|
});
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
private shadowBackground(): ImageType {
|
|
106
|
-
return new Jimp({
|
|
107
|
-
width: this.background.width,
|
|
108
|
-
height: this.background.height,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
113
|
private get backgroundSize(): Size {
|
|
113
114
|
return {
|
|
114
115
|
width: this.background.width,
|
|
@@ -122,13 +123,24 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
template = (fn: TemplateLayerFn<EntryType>): this =>
|
|
125
|
-
this.layer(entry => {
|
|
126
|
+
this.layer(async entry => {
|
|
126
127
|
const template = fn(this.shadowTemplate());
|
|
127
|
-
|
|
128
|
+
const image = await template.render(entry);
|
|
129
|
+
return [
|
|
130
|
+
await placeImage({
|
|
131
|
+
image,
|
|
132
|
+
box: {
|
|
133
|
+
left: 0,
|
|
134
|
+
top: 0,
|
|
135
|
+
...this.backgroundSize,
|
|
136
|
+
},
|
|
137
|
+
options: DEFAULT_IMAGE_LAYER_OPTIONS,
|
|
138
|
+
}),
|
|
139
|
+
];
|
|
128
140
|
});
|
|
129
141
|
|
|
130
142
|
container = (
|
|
131
|
-
|
|
143
|
+
elementsFn: ElementsFn,
|
|
132
144
|
box: BoundingBox,
|
|
133
145
|
packingFn: PackingFn,
|
|
134
146
|
options?: ContainerOptions<EntryType>,
|
|
@@ -137,45 +149,39 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
137
149
|
const mergedOptions: RequiredDeep<ContainerOptions<EntryType>> =
|
|
138
150
|
merge({}, DEFAULT_CONTAINER_OPTIONS, options);
|
|
139
151
|
if (this.shouldSkipLayerForEntry(entry, mergedOptions)) {
|
|
140
|
-
return
|
|
152
|
+
return [];
|
|
141
153
|
}
|
|
142
154
|
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
this.shadowBackground(),
|
|
147
|
-
images,
|
|
148
|
-
);
|
|
155
|
+
const elementRefs = await elementsFn(entry);
|
|
156
|
+
const elements = await Promise.all(elementRefs.map(elementRef => this.elementFromElementRef(entry, elementRef)))
|
|
157
|
+
const result = await packingFn(box, elements);
|
|
149
158
|
|
|
150
159
|
// debug mode
|
|
151
160
|
if (debugMode) {
|
|
152
|
-
const debugImage = await
|
|
153
|
-
|
|
154
|
-
this.backgroundSize,
|
|
155
|
-
);
|
|
156
|
-
return debugImage.composite(result);
|
|
161
|
+
const debugImage = await placeBoundingBox(box);
|
|
162
|
+
return [result, debugImage];
|
|
157
163
|
}
|
|
158
164
|
|
|
159
|
-
return result;
|
|
165
|
+
return [result];
|
|
160
166
|
});
|
|
161
167
|
|
|
162
168
|
hbox = (
|
|
163
|
-
|
|
169
|
+
elementsFn: ElementsFn,
|
|
164
170
|
box: BoundingBox,
|
|
165
171
|
options?: DirectionContainerOptions<EntryType>,
|
|
166
|
-
): this => this.container(
|
|
172
|
+
): this => this.container(elementsFn, box, hboxPackingFn(options), options);
|
|
167
173
|
|
|
168
174
|
vbox = (
|
|
169
|
-
|
|
175
|
+
elementsFn: ElementsFn,
|
|
170
176
|
box: BoundingBox,
|
|
171
177
|
options?: DirectionContainerOptions<EntryType>,
|
|
172
|
-
): this => this.container(
|
|
178
|
+
): this => this.container(elementsFn, box, vboxPackingFn(options), options);
|
|
173
179
|
|
|
174
180
|
grid = (
|
|
175
|
-
|
|
181
|
+
elementsFn: ElementsFn,
|
|
176
182
|
box: BoundingBox,
|
|
177
183
|
options?: GridContainerOptions<EntryType>,
|
|
178
|
-
): this => this.container(
|
|
184
|
+
): this => this.container(elementsFn, box, gridPackingFn(options), options);
|
|
179
185
|
|
|
180
186
|
image = (
|
|
181
187
|
ref: ImageRef<EntryType>,
|
|
@@ -191,10 +197,10 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
191
197
|
options,
|
|
192
198
|
);
|
|
193
199
|
if (this.shouldSkipLayerForEntry(entry, mergedOptions)) {
|
|
194
|
-
return
|
|
200
|
+
return [];
|
|
195
201
|
}
|
|
196
202
|
|
|
197
|
-
const image = await this.
|
|
203
|
+
const image = await this.imageFromImageRef(
|
|
198
204
|
entry,
|
|
199
205
|
ref,
|
|
200
206
|
mergedOptions,
|
|
@@ -202,20 +208,16 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
202
208
|
const result = await placeImage({
|
|
203
209
|
image,
|
|
204
210
|
box,
|
|
205
|
-
backgroundSize: this.backgroundSize,
|
|
206
211
|
options: mergedOptions,
|
|
207
212
|
});
|
|
208
213
|
|
|
209
214
|
// debug mode
|
|
210
215
|
if (debugMode) {
|
|
211
|
-
const debugImage = await
|
|
212
|
-
|
|
213
|
-
this.backgroundSize,
|
|
214
|
-
);
|
|
215
|
-
return debugImage.composite(result);
|
|
216
|
+
const debugImage = await placeBoundingBox(box);
|
|
217
|
+
return [result, debugImage];
|
|
216
218
|
}
|
|
217
219
|
|
|
218
|
-
return result;
|
|
220
|
+
return [result];
|
|
219
221
|
});
|
|
220
222
|
|
|
221
223
|
loadImage = async (imagePath: string | Buffer): Promise<ImageType> => {
|
|
@@ -240,28 +242,23 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
240
242
|
options,
|
|
241
243
|
);
|
|
242
244
|
if (this.shouldSkipLayerForEntry(entry, mergedOptions)) {
|
|
243
|
-
return
|
|
245
|
+
return [];
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
const text = this.textFromTextRef(entry, ref);
|
|
247
|
-
const result = await
|
|
249
|
+
const result = await placeText({
|
|
248
250
|
text,
|
|
249
251
|
box,
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.fonts,
|
|
253
|
-
);
|
|
252
|
+
options: mergedOptions,
|
|
253
|
+
});
|
|
254
254
|
|
|
255
255
|
// debug mode
|
|
256
256
|
if (debugMode) {
|
|
257
|
-
const debugImage = await
|
|
258
|
-
|
|
259
|
-
this.backgroundSize,
|
|
260
|
-
);
|
|
261
|
-
return debugImage.composite(result);
|
|
257
|
+
const debugImage = await placeBoundingBox(box);
|
|
258
|
+
return [result, debugImage];
|
|
262
259
|
}
|
|
263
260
|
|
|
264
|
-
return result;
|
|
261
|
+
return [result];
|
|
265
262
|
});
|
|
266
263
|
|
|
267
264
|
font(path: fs.PathLike, name: string): this {
|
|
@@ -275,7 +272,7 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
275
272
|
return this;
|
|
276
273
|
}
|
|
277
274
|
|
|
278
|
-
|
|
275
|
+
async render(entry: EntryType): Promise<ImageType> {
|
|
279
276
|
const results = await Promise.all(
|
|
280
277
|
this.layers.map(layerFn =>
|
|
281
278
|
layerFn(entry, {
|
|
@@ -283,18 +280,28 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
283
280
|
}),
|
|
284
281
|
),
|
|
285
282
|
);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
283
|
+
const document = h('html', [
|
|
284
|
+
h('head', [
|
|
285
|
+
h(
|
|
286
|
+
'style',
|
|
287
|
+
Object.entries(this.fonts)
|
|
288
|
+
.map(
|
|
289
|
+
([name, data]) =>
|
|
290
|
+
`@font-face {
|
|
291
|
+
font-family: '${name}';
|
|
292
|
+
src: url(data:font/ttf;base64,${data}) format('truetype');
|
|
293
|
+
}`,
|
|
294
|
+
)
|
|
295
|
+
.join('\n'),
|
|
296
|
+
),
|
|
297
|
+
]),
|
|
298
|
+
h(
|
|
299
|
+
'body',
|
|
300
|
+
flatten(results),
|
|
301
|
+
),
|
|
302
|
+
]);
|
|
303
|
+
const renderedLayers = await htmlToImage(document, this.backgroundSize);
|
|
304
|
+
return this.background.clone().composite(renderedLayers);
|
|
298
305
|
}
|
|
299
306
|
|
|
300
307
|
async renderAll(
|
|
@@ -305,7 +312,7 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
305
312
|
entries.map(
|
|
306
313
|
entry =>
|
|
307
314
|
new Promise<Array<ImageType>>(resolve =>
|
|
308
|
-
this.render(entry
|
|
315
|
+
this.render(entry).then(image => {
|
|
309
316
|
if (!options?.duplication) {
|
|
310
317
|
return resolve([image]);
|
|
311
318
|
}
|
|
@@ -353,10 +360,10 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
353
360
|
});
|
|
354
361
|
}
|
|
355
362
|
|
|
356
|
-
private
|
|
363
|
+
private imageFromImageRef = async (
|
|
357
364
|
entry: EntryType,
|
|
358
365
|
ref: ImageRef<EntryType>,
|
|
359
|
-
options: RequiredDeep<
|
|
366
|
+
options: RequiredDeep<ImageLayerSpecificOptions<EntryType>>,
|
|
360
367
|
): Promise<ImageType> => {
|
|
361
368
|
const pathSegments = [];
|
|
362
369
|
if (options.assetsPath) {
|
|
@@ -395,6 +402,29 @@ export class Template<EntryType extends Record<string, string>> {
|
|
|
395
402
|
}
|
|
396
403
|
};
|
|
397
404
|
|
|
405
|
+
private elementFromElementRef = async (
|
|
406
|
+
entry: EntryType,
|
|
407
|
+
ref: ElementRef<EntryType>,
|
|
408
|
+
): Promise<HyperNode> => {
|
|
409
|
+
if ('text' in ref) {
|
|
410
|
+
const options = merge({}, DEFAULT_TEXT_LAYER_OPTIONS, ref.options);
|
|
411
|
+
return prepareText({
|
|
412
|
+
text: this.textFromTextRef(entry, ref.text),
|
|
413
|
+
options,
|
|
414
|
+
});
|
|
415
|
+
} else if ('image' in ref) {
|
|
416
|
+
const options = merge({}, DEFAULT_IMAGE_LAYER_OPTIONS, ref.options);
|
|
417
|
+
return prepareImage({
|
|
418
|
+
image: await this.imageFromImageRef(entry, ref.image, options),
|
|
419
|
+
options,
|
|
420
|
+
});
|
|
421
|
+
} else if ('node' in ref) {
|
|
422
|
+
return ref.node
|
|
423
|
+
} else {
|
|
424
|
+
throw new Error('Unknown TextRef variant');
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
398
428
|
private shouldSkipLayerForEntry = (
|
|
399
429
|
entry: EntryType,
|
|
400
430
|
options: LayerOptions<EntryType>,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { RequiredDeep } from 'type-fest';
|
|
2
2
|
import { BoundingBox } from './boundingBox';
|
|
3
|
-
import { ImageType } from './image';
|
|
3
|
+
import { ImageLayerSpecificOptions, ImageRef, ImageType } from './image';
|
|
4
4
|
import { ScaleMode } from './scale';
|
|
5
5
|
import { LayerOptions } from './layer';
|
|
6
|
+
import { HyperNode } from './hypernode';
|
|
7
|
+
import { TextLayerSpecificOptions, TextRef } from './text';
|
|
6
8
|
|
|
7
9
|
export type ContainerOptions<EntryType extends Record<string, string>> =
|
|
8
10
|
LayerOptions<EntryType> & {
|
|
@@ -65,8 +67,24 @@ export const DEFAULT_GRID_CONTAINER_OPTIONS: RequiredDeep<
|
|
|
65
67
|
direction: 'rows',
|
|
66
68
|
};
|
|
67
69
|
|
|
70
|
+
export type ElementRef<EntryType extends Record<string, string>> =
|
|
71
|
+
| {
|
|
72
|
+
image: ImageRef<EntryType>;
|
|
73
|
+
options?: ImageLayerSpecificOptions<EntryType>;
|
|
74
|
+
}
|
|
75
|
+
| {
|
|
76
|
+
text: TextRef<EntryType>;
|
|
77
|
+
options?: TextLayerSpecificOptions<EntryType>;
|
|
78
|
+
}
|
|
79
|
+
| {
|
|
80
|
+
node: HyperNode;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export type ElementsFn = <EntryType extends Record<string, string>>(
|
|
84
|
+
entry: EntryType,
|
|
85
|
+
) => Promise<Array<ElementRef<EntryType>>>;
|
|
86
|
+
|
|
68
87
|
export type PackingFn = (
|
|
69
88
|
box: BoundingBox,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
) => Promise<ImageType>;
|
|
89
|
+
elements: Array<HyperNode>,
|
|
90
|
+
) => Promise<HyperNode>;
|
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 = {
|
|
@@ -33,19 +37,25 @@ export type TextLayerOptions<EntryType extends Record<string, string>> =
|
|
|
33
37
|
|
|
34
38
|
color?: string;
|
|
35
39
|
|
|
40
|
+
border?: {
|
|
41
|
+
width?: number;
|
|
42
|
+
color?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
36
45
|
// TODO: processor fn
|
|
37
46
|
replacement?: ReplacementMap;
|
|
38
47
|
};
|
|
39
48
|
|
|
40
|
-
export const DEFAULT_FONT: Required<
|
|
41
|
-
FontOptions
|
|
42
|
-
> = {
|
|
49
|
+
export const DEFAULT_FONT: Required<FontOptions> = {
|
|
43
50
|
size: 28,
|
|
44
51
|
family: 'Arial',
|
|
45
52
|
bold: false,
|
|
46
53
|
italic: false,
|
|
47
54
|
};
|
|
48
55
|
|
|
56
|
+
export type TextLayerSpecificOptions<EntryType extends Record<string, string>> =
|
|
57
|
+
Omit<TextLayerOptions<EntryType>, keyof LayerOptions<EntryType>>;
|
|
58
|
+
|
|
49
59
|
export const DEFAULT_TEXT_LAYER_OPTIONS: RequiredDeep<
|
|
50
60
|
TextLayerOptions<Record<string, string>>
|
|
51
61
|
> = {
|
|
@@ -55,4 +65,8 @@ export const DEFAULT_TEXT_LAYER_OPTIONS: RequiredDeep<
|
|
|
55
65
|
color: 'black',
|
|
56
66
|
replacement: {},
|
|
57
67
|
skip: false,
|
|
68
|
+
border: {
|
|
69
|
+
width: 0,
|
|
70
|
+
color: 'black',
|
|
71
|
+
},
|
|
58
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,7 +1,6 @@
|
|
|
1
|
-
export * from './buildFontString';
|
|
2
1
|
export * from './container';
|
|
3
|
-
export * from './
|
|
2
|
+
export * from './placeBoundingBox';
|
|
4
3
|
export * from './placeImage';
|
|
5
|
-
export * from './
|
|
4
|
+
export * from './placeText';
|
|
6
5
|
export * from './htmlToImage';
|
|
7
6
|
export * from './toPx';
|
|
@@ -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
|
};
|