@hellkite/pipkin 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/build/src/lib/template.d.ts +11 -8
  2. package/build/src/lib/template.js +54 -22
  3. package/build/src/lib/template.js.map +1 -1
  4. package/build/src/lib/types/containers.d.ts +11 -22
  5. package/build/src/lib/types/containers.js +3 -1
  6. package/build/src/lib/types/containers.js.map +1 -1
  7. package/build/src/lib/types/image.d.ts +5 -12
  8. package/build/src/lib/types/image.js +2 -0
  9. package/build/src/lib/types/image.js.map +1 -1
  10. package/build/src/lib/types/index.d.ts +1 -0
  11. package/build/src/lib/types/index.js +1 -0
  12. package/build/src/lib/types/index.js.map +1 -1
  13. package/build/src/lib/types/layer.d.ts +15 -0
  14. package/build/src/lib/types/layer.js +3 -0
  15. package/build/src/lib/types/layer.js.map +1 -0
  16. package/build/src/lib/types/text.d.ts +23 -27
  17. package/build/src/lib/types/text.js +13 -4
  18. package/build/src/lib/types/text.js.map +1 -1
  19. package/build/src/lib/utils/buildFontString.d.ts +2 -2
  20. package/build/src/lib/utils/buildFontString.js.map +1 -1
  21. package/build/src/lib/utils/container.d.ts +4 -3
  22. package/build/src/lib/utils/container.js +41 -4
  23. package/build/src/lib/utils/container.js.map +1 -1
  24. package/build/src/lib/utils/drawBoundingBox.js.map +1 -1
  25. package/build/src/lib/utils/htmlToImage.js +1 -1
  26. package/build/src/lib/utils/htmlToImage.js.map +1 -1
  27. package/build/src/lib/utils/placeImage.d.ts +4 -3
  28. package/build/src/lib/utils/placeImage.js +22 -28
  29. package/build/src/lib/utils/placeImage.js.map +1 -1
  30. package/build/src/lib/utils/renderText.d.ts +2 -1
  31. package/build/src/lib/utils/renderText.js +21 -12
  32. package/build/src/lib/utils/renderText.js.map +1 -1
  33. package/build/src/test.js +14 -5
  34. package/build/src/test.js.map +1 -1
  35. package/package.json +4 -1
  36. package/roadmap.md +6 -4
  37. package/src/lib/template.ts +137 -32
  38. package/src/lib/types/containers.ts +52 -46
  39. package/src/lib/types/image.ts +21 -29
  40. package/src/lib/types/index.ts +1 -0
  41. package/src/lib/types/layer.ts +18 -0
  42. package/src/lib/types/text.ts +44 -49
  43. package/src/lib/utils/buildFontString.ts +2 -2
  44. package/src/lib/utils/container.ts +102 -9
  45. package/src/lib/utils/drawBoundingBox.ts +12 -9
  46. package/src/lib/utils/htmlToImage.ts +1 -1
  47. package/src/lib/utils/placeImage.ts +8 -10
  48. package/src/lib/utils/renderText.ts +33 -21
@@ -1,66 +1,72 @@
1
- import { AlignItems, JustifyContent } from './css';
1
+ import { RequiredDeep } from 'type-fest';
2
+ import { BoundingBox } from './boundingBox';
2
3
  import { ImageType } from './image';
3
4
  import { ScaleMode } from './scale';
5
+ import { LayerOptions } from './layer';
4
6
 
5
- export type ContainerOptions = {
6
- /**
7
- * Setting a non-default value for justify will cause the alignment to be ignored.
8
- *
9
- * Defaults to `normal`
10
- */
11
- justifyContent?: JustifyContent;
7
+ export type ContainerOptions<EntryType extends Record<string, string>> =
8
+ LayerOptions<EntryType> & {
9
+ /**
10
+ * This is treated as a minimum length of an unit of space
11
+ * between two items, but it can grow larger than it
12
+ *
13
+ * Defaults to 0
14
+ */
15
+ gap?: number;
12
16
 
13
- /**
14
- * This is treated as a minimum length of an unit of space
15
- * between two items, but it can grow larger than it
16
- *
17
- * Defaults to 0
18
- */
19
- gap?: number;
17
+ /**
18
+ * Defaults to `none`
19
+ */
20
+ scale?: ScaleMode;
21
+ };
20
22
 
21
- /**
22
- * Defines how the items should be placed
23
- * across the secondary direction of the container
24
- *
25
- * Defaults to `center`
26
- */
27
- alignItems?: AlignItems;
28
-
29
- /**
30
- * Defaults to `none`
31
- */
32
- scale?: ScaleMode;
33
- };
34
-
35
- export const DEFAULT_CONTAINER_OPTIONS: Required<ContainerOptions> = {
23
+ export const DEFAULT_CONTAINER_OPTIONS: RequiredDeep<
24
+ ContainerOptions<Record<string, string>>
25
+ > = {
36
26
  gap: 0,
37
27
  justifyContent: 'normal',
38
28
  alignItems: 'center',
39
29
  scale: 'none',
30
+ skip: false,
40
31
  };
41
32
 
42
- export type DirectionContainerOptions = ContainerOptions & {
33
+ export type DirectionContainerOptions<
34
+ EntryType extends Record<string, string>,
35
+ > = ContainerOptions<EntryType> & {
43
36
  reversed?: boolean;
44
37
  };
45
38
 
46
- export const DEFAULT_DIRECTION_CONTAINER_OPTIONS: Required<DirectionContainerOptions> = {
39
+ export const DEFAULT_DIRECTION_CONTAINER_OPTIONS: RequiredDeep<
40
+ DirectionContainerOptions<Record<string, string>>
41
+ > = {
47
42
  ...DEFAULT_CONTAINER_OPTIONS,
48
43
  reversed: false,
49
- }
44
+ };
50
45
 
51
- export type GridContainerOptions = ContainerOptions & {
52
- rows?: number;
53
- cols?: number;
46
+ export type GridContainerOptions<EntryType extends Record<string, string>> =
47
+ ContainerOptions<EntryType> & {
48
+ size?: number;
54
49
 
55
- /**
56
- * Defines what is considered the main direction of the container.
57
- * -> `rows` - the items would be placed to fill each row before starting the next
58
- * -> `cols` - the items would be placed to fill each col before starting the next
59
- *
60
- * Defaults to `rows`
61
- */
62
- direction?: 'rows' | 'cols';
63
- };
50
+ /**
51
+ * Defines what is considered the main direction of the container.
52
+ * -> `rows` - the items would be placed to fill each row before starting the next
53
+ * -> `cols` - the items would be placed to fill each col before starting the next
54
+ *
55
+ * Defaults to `rows`
56
+ */
57
+ direction?: 'rows' | 'cols';
58
+ };
64
59
 
60
+ export const DEFAULT_GRID_CONTAINER_OPTIONS: RequiredDeep<
61
+ GridContainerOptions<Record<string, string>>
62
+ > = {
63
+ ...DEFAULT_CONTAINER_OPTIONS,
64
+ size: 3,
65
+ direction: 'rows',
66
+ };
65
67
 
66
- export type PackingFn = (background: ImageType, images: Array<ImageType>) => Promise<ImageType>;
68
+ export type PackingFn = (
69
+ box: BoundingBox,
70
+ background: ImageType,
71
+ images: Array<ImageType>,
72
+ ) => Promise<ImageType>;
@@ -1,47 +1,39 @@
1
1
  import { JimpInstance } from 'jimp';
2
2
  import { ScaleMode } from './scale';
3
- import { AlignItems, JustifyContent } from './css';
3
+ import { LayerOptions } from './layer';
4
+ import { RequiredDeep } from 'type-fest';
4
5
 
5
6
  export type ImageType = JimpInstance;
6
7
 
7
- export type ImageRef<EntryType> =
8
+ export type ImageRef<EntryType extends Record<string, string>> =
8
9
  | { buffer: Buffer }
9
10
  | { path: string }
10
11
  | { absolutePath: string }
11
12
  | { key: keyof EntryType }
12
13
  | { pathFn: (entry: EntryType) => string };
13
14
 
14
- export type ImageLayerOptions = {
15
- /**
16
- * Base path for the assets. Overrides the more global property of the template `defaultAssetsPath`.
17
- */
18
- assetsPath?: string;
19
-
20
- /**
21
- * default `center`
22
- */
23
- justifyContent?: JustifyContent;
24
- /**
25
- * default `center`
26
- */
27
- alignItems?: AlignItems;
28
-
29
- /**
30
- * Defaults to `none`
31
- */
32
- scale?: ScaleMode;
33
-
34
- // TODO:
35
- // processorFn?: (entry: EntryType, image: ImageType) => Promise<ImageType>;
36
- };
15
+ export type ImageLayerOptions<EntryType extends Record<string, string>> =
16
+ LayerOptions<EntryType> & {
17
+ /**
18
+ * Base path for the assets. Overrides the more global property of the template `defaultAssetsPath`.
19
+ */
20
+ assetsPath?: string;
21
+
22
+ /**
23
+ * Defaults to `none`
24
+ */
25
+ scale?: ScaleMode;
26
+
27
+ // TODO:
28
+ // processorFn?: (entry: EntryType, image: ImageType) => Promise<ImageType>;
29
+ };
37
30
 
38
- export const DEFAULT_IMAGE_LAYER_OPTIONS: Required<
39
- Omit<ImageLayerOptions, 'assetsPath'>
40
- > &
41
- Pick<ImageLayerOptions, 'assetsPath'> = {
31
+ export const DEFAULT_IMAGE_LAYER_OPTIONS: RequiredDeep<ImageLayerOptions<Record<string, string>>> = {
42
32
  justifyContent: 'center',
43
33
  alignItems: 'center',
44
34
  scale: 'none',
35
+ skip: false,
36
+ assetsPath: ''
45
37
  };
46
38
 
47
39
  export type Alignment = 'start' | 'center' | 'end';
@@ -7,3 +7,4 @@ export * from './scale';
7
7
  export * from './text';
8
8
  export * from './boundingBox';
9
9
  export * from './css';
10
+ export * from './layer';
@@ -0,0 +1,18 @@
1
+ import { JustifyContent, AlignItems } from "./css";
2
+
3
+ export type LayerOptions<EntryType extends Record<string, string>> = {
4
+ /**
5
+ * default `center`
6
+ */
7
+ justifyContent?: JustifyContent;
8
+
9
+ /**
10
+ * default `center`
11
+ */
12
+ alignItems?: AlignItems;
13
+
14
+ /**
15
+ * Decides if a layer should be rendered or not for a certain entry
16
+ */
17
+ skip?: boolean | ((entry: EntryType) => boolean);
18
+ };
@@ -1,3 +1,5 @@
1
+ import type { RequiredDeep } from 'type-fest';
2
+ import { LayerOptions } from './layer';
1
3
  import { ReplacementMap } from './replacement';
2
4
 
3
5
  export type TextRef<EntryType> =
@@ -5,59 +7,52 @@ export type TextRef<EntryType> =
5
7
  | { text: string }
6
8
  | { textFn: (entry: EntryType) => string };
7
9
 
8
- export type TextLayerOptions = {
9
- font?: {
10
- /**
11
- * Size of font is represented in pixels
12
- */
13
- size?: number;
14
- /**
15
- * Either use one supported by canvas
16
- * or load a custom one before rendering
17
- */
18
- family?: string;
19
- /**
20
- * Warning: Not supported by all fonts
21
- */
22
- bold?: boolean;
23
- /**
24
- * Warning: Not supported by all fonts
25
- */
26
- italic?: boolean;
27
- };
28
-
29
- color?: string;
30
-
31
- // TODO: processor fn
32
- replacement?: ReplacementMap;
33
-
10
+ export type FontOptions = {
34
11
  /**
35
- * default `center`
12
+ * Size of font is represented in pixels
36
13
  */
37
- xAlign?:
38
- | 'left'
39
- | 'center'
40
- | 'right'
41
- | 'justify'
42
- | 'justify-left'
43
- | 'justify-center'
44
- | 'justify-right';
45
-
14
+ size?: number;
15
+ /**
16
+ * Either use one supported by canvas
17
+ * or load a custom one before rendering
18
+ */
19
+ family?: string;
46
20
  /**
47
- * default `center`
21
+ * Warning: Not supported by all fonts
48
22
  */
49
- yAlign?:
50
- | 'left'
51
- | 'center'
52
- | 'right'
53
- | 'justify'
54
- | 'justify-left'
55
- | 'justify-center'
56
- | 'justify-right';
23
+ bold?: boolean;
24
+ /**
25
+ * Warning: Not supported by all fonts
26
+ */
27
+ italic?: boolean;
28
+ };
29
+
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,
57
47
  };
58
48
 
59
- // TODO: all required
60
- export const DEFAULT_TEXT_LAYER_OPTIONS: TextLayerOptions = {
61
- xAlign: 'center',
62
- yAlign: 'center',
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,
63
58
  };
@@ -1,7 +1,7 @@
1
- import { TextLayerOptions } from "../types";
1
+ import { FontOptions } from "../types";
2
2
 
3
3
  export const buildFontString = (
4
- font: TextLayerOptions['font'],
4
+ font: FontOptions,
5
5
  defaultFontFamily?: string,
6
6
  ): string => {
7
7
  const fragments: string[] = [];
@@ -1,13 +1,25 @@
1
-
2
1
  import { h } from 'virtual-dom';
3
- import { DEFAULT_DIRECTION_CONTAINER_OPTIONS, DirectionContainerOptions, ImageType, PackingFn, BoundingBox, SCALE_MODE_TO_OBJECT_FIT, Size } from '../types';
2
+ import {
3
+ DEFAULT_DIRECTION_CONTAINER_OPTIONS,
4
+ DirectionContainerOptions,
5
+ ImageType,
6
+ PackingFn,
7
+ BoundingBox,
8
+ SCALE_MODE_TO_OBJECT_FIT,
9
+ Size,
10
+ GridContainerOptions,
11
+ DEFAULT_GRID_CONTAINER_OPTIONS,
12
+ } from '../types';
4
13
  import { boundingBoxToPx, toPx } from './toPx';
5
14
  import merge from 'lodash.merge';
6
15
  import { htmlToImage } from './htmlToImage';
16
+ import chunk from 'lodash.chunk';
7
17
 
8
18
  export const vboxPackingFn =
9
- (box: BoundingBox, options?: DirectionContainerOptions): PackingFn =>
10
- (background: ImageType, images: Array<ImageType>) =>
19
+ <EntryType extends Record<string, string>>(
20
+ options?: DirectionContainerOptions<EntryType>,
21
+ ): PackingFn =>
22
+ (box: BoundingBox, background: ImageType, images: Array<ImageType>) =>
11
23
  directionalPackingFn({
12
24
  isVertical: true,
13
25
  backgroundSize: background,
@@ -17,8 +29,10 @@ export const vboxPackingFn =
17
29
  });
18
30
 
19
31
  export const hboxPackingFn =
20
- (box: BoundingBox, options?: DirectionContainerOptions): PackingFn =>
21
- (background: ImageType, images: Array<ImageType>) =>
32
+ <EntryType extends Record<string, string>>(
33
+ options?: DirectionContainerOptions<EntryType>,
34
+ ): PackingFn =>
35
+ (box: BoundingBox, background: ImageType, images: Array<ImageType>) =>
22
36
  directionalPackingFn({
23
37
  isVertical: false,
24
38
  backgroundSize: background,
@@ -27,7 +41,82 @@ export const hboxPackingFn =
27
41
  options,
28
42
  });
29
43
 
30
- const directionalPackingFn = async ({
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>>({
31
120
  isVertical,
32
121
  backgroundSize: background,
33
122
  images,
@@ -38,9 +127,13 @@ const directionalPackingFn = async ({
38
127
  backgroundSize: Size;
39
128
  images: Array<ImageType>;
40
129
  box: BoundingBox;
41
- options?: DirectionContainerOptions;
130
+ options?: DirectionContainerOptions<EntryType>;
42
131
  }): Promise<ImageType> => {
43
- const mergedOptions = merge(DEFAULT_DIRECTION_CONTAINER_OPTIONS, options);
132
+ const mergedOptions = merge(
133
+ {},
134
+ DEFAULT_DIRECTION_CONTAINER_OPTIONS,
135
+ options,
136
+ );
44
137
  const objectFit = SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
45
138
 
46
139
  const children = await Promise.all(
@@ -5,18 +5,21 @@ import { ImageType, Size, BoundingBox } from '../types';
5
5
 
6
6
  export const drawBoundingBox = async (
7
7
  box: BoundingBox,
8
-
9
8
  imageSize: Size,
10
9
  ): Promise<ImageType> => {
11
- const document = h('div', {
12
- style: {
13
- position: 'absolute',
14
- border: '2px solid red',
15
- background: 'transparent',
16
- boxSizing: 'border-box',
17
- ...boundingBoxToPx(box),
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
+ },
18
20
  },
19
- }, []);
21
+ [],
22
+ );
20
23
 
21
24
  return htmlToImage(document, imageSize);
22
25
  };
@@ -8,7 +8,6 @@ export const htmlToImage = async (
8
8
  backgroundSize: Size,
9
9
  ): Promise<ImageType> => {
10
10
  const html = createElement(document).toString();
11
- // TODO: extract this in a dif function
12
11
  const image = await nodeHtmlToImage({
13
12
  html,
14
13
  transparent: true,
@@ -17,6 +16,7 @@ export const htmlToImage = async (
17
16
  defaultViewport: {
18
17
  width: backgroundSize.width,
19
18
  height: backgroundSize.height,
19
+ deviceScaleFactor: 1,
20
20
  },
21
21
  },
22
22
  });
@@ -1,31 +1,29 @@
1
1
  import { h } from 'virtual-dom';
2
2
  import { boundingBoxToPx } from './toPx';
3
3
  import {
4
- DEFAULT_IMAGE_LAYER_OPTIONS,
5
4
  ImageLayerOptions,
6
5
  ImageType,
7
6
  BoundingBox,
8
7
  SCALE_MODE_TO_OBJECT_FIT,
9
8
  } from '../types';
10
- import merge from 'lodash.merge';
11
9
  import { htmlToImage } from './htmlToImage';
10
+ import { RequiredDeep } from 'type-fest';
12
11
 
13
- type PlaceImageProps = {
12
+ type PlaceImageProps<EntryType extends Record<string, string>> = {
14
13
  image: ImageType;
15
14
  box: BoundingBox;
16
15
  backgroundSize: { width: number; height: number };
17
- options: ImageLayerOptions;
16
+ options: RequiredDeep<ImageLayerOptions<EntryType>>;
18
17
  };
19
18
 
20
- export async function placeImage({
19
+ export const placeImage = async <EntryType extends Record<string, string>>({
21
20
  image,
22
21
  box,
23
22
  backgroundSize,
24
23
  options,
25
- }: PlaceImageProps): Promise<ImageType> {
24
+ }: PlaceImageProps<EntryType>): Promise<ImageType> => {
26
25
  const imageBase64 = await image.getBase64('image/png');
27
- const mergedOptions = merge(DEFAULT_IMAGE_LAYER_OPTIONS, options);
28
- const objectFit = SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
26
+ const objectFit = SCALE_MODE_TO_OBJECT_FIT[options.scale];
29
27
 
30
28
  const document = h(
31
29
  'div',
@@ -35,8 +33,8 @@ export async function placeImage({
35
33
  position: 'absolute',
36
34
  scale: 1,
37
35
 
38
- justifyContent: mergedOptions.justifyContent,
39
- alignItems: mergedOptions.alignItems,
36
+ justifyContent: options.justifyContent,
37
+ alignItems: options.alignItems,
40
38
 
41
39
  ...boundingBoxToPx(box),
42
40
  },
@@ -1,22 +1,18 @@
1
1
  import { h, VNode } from 'virtual-dom';
2
- import {
3
- DEFAULT_TEXT_LAYER_OPTIONS,
4
- ImageType,
5
- BoundingBox,
6
- Size,
7
- TextLayerOptions,
8
- } from '../types';
2
+ import { ImageType, BoundingBox, Size, TextLayerOptions } from '../types';
9
3
  import { boundingBoxToPx, toPx } from './toPx';
10
4
  import { htmlToImage } from './htmlToImage';
5
+ import { RequiredDeep } from 'type-fest';
11
6
 
12
- export const renderText = async (
7
+ export const renderText = async <EntryType extends Record<string, string>>(
13
8
  text: string,
14
9
  box: BoundingBox,
15
10
  backgroundSize: Size,
16
- options?: TextLayerOptions,
11
+ options: RequiredDeep<TextLayerOptions<EntryType>>,
12
+ fonts: Record<string, string>,
17
13
  ): Promise<ImageType> => {
18
14
  let textChildren: Array<string | VNode> = [text];
19
- for (const [word, image] of Object.entries(options?.replacement ?? {})) {
15
+ for (const [word, image] of Object.entries(options.replacement)) {
20
16
  const regex = new RegExp(word, 'gi');
21
17
  const imageBase64 = await image.getBase64('image/png');
22
18
 
@@ -36,7 +32,7 @@ export const renderText = async (
36
32
  style: {
37
33
  display: 'inline',
38
34
  verticalAlign: 'middle',
39
- height: toPx(options?.font?.size ?? 28), // TODO: use constant
35
+ height: toPx(options.font.size),
40
36
  width: 'auto',
41
37
  },
42
38
  src: imageBase64,
@@ -52,7 +48,7 @@ export const renderText = async (
52
48
  textChildren = tmpChildren;
53
49
  }
54
50
 
55
- const document = h(
51
+ const content = h(
56
52
  'div',
57
53
  {
58
54
  style: {
@@ -60,10 +56,8 @@ export const renderText = async (
60
56
  overflow: 'visible',
61
57
  position: 'absolute',
62
58
 
63
- justifyContent:
64
- options?.xAlign ?? DEFAULT_TEXT_LAYER_OPTIONS.xAlign,
65
- alignItems:
66
- options?.yAlign ?? DEFAULT_TEXT_LAYER_OPTIONS.yAlign,
59
+ justifyContent: options.justifyContent,
60
+ alignItems: options.alignItems,
67
61
 
68
62
  ...boundingBoxToPx(box),
69
63
  },
@@ -77,11 +71,11 @@ export const renderText = async (
77
71
  overflowWrap: 'word-wrap',
78
72
  whiteSpace: 'normal',
79
73
 
80
- color: options?.color,
81
- fontFamily: options?.font?.family,
82
- fontSize: options?.font?.size,
83
- fontStyle: options?.font?.italic ? 'italic' : undefined,
84
- fontWeight: options?.font?.bold ? 'bold' : undefined,
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,
85
79
  },
86
80
  },
87
81
  textChildren,
@@ -89,5 +83,23 @@ export const renderText = async (
89
83
  ],
90
84
  );
91
85
 
86
+ const document = h('html', [
87
+ h('head', [
88
+ h(
89
+ 'style',
90
+ Object.entries(fonts)
91
+ .map(
92
+ ([name, data]) =>
93
+ `@font-face {
94
+ font-family: '${name}';
95
+ src: url(data:font/ttf;base64,${data}) format('truetype');
96
+ }`,
97
+ )
98
+ .join('\n'),
99
+ ),
100
+ ]),
101
+ h('body', [content]),
102
+ ]);
103
+
92
104
  return htmlToImage(document, backgroundSize);
93
105
  };