@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
@@ -18,7 +18,6 @@ const node_html_to_image_1 = __importDefault(require("node-html-to-image"));
18
18
  const jimp_1 = require("jimp");
19
19
  const htmlToImage = (document, backgroundSize) => __awaiter(void 0, void 0, void 0, function* () {
20
20
  const html = (0, virtual_dom_1.create)(document).toString();
21
- // TODO: extract this in a dif function
22
21
  const image = yield (0, node_html_to_image_1.default)({
23
22
  html,
24
23
  transparent: true,
@@ -27,6 +26,7 @@ const htmlToImage = (document, backgroundSize) => __awaiter(void 0, void 0, void
27
26
  defaultViewport: {
28
27
  width: backgroundSize.width,
29
28
  height: backgroundSize.height,
29
+ deviceScaleFactor: 1,
30
30
  },
31
31
  },
32
32
  });
@@ -1 +1 @@
1
- {"version":3,"file":"htmlToImage.js","sourceRoot":"","sources":["../../../../src/lib/utils/htmlToImage.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6CAA6D;AAC7D,4EAAiD;AACjD,+BAA4B;AAGrB,MAAM,WAAW,GAAG,CACvB,QAAe,EACf,cAAoB,EACF,EAAE;IACpB,MAAM,IAAI,GAAG,IAAA,oBAAa,EAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IAChD,uCAAuC;IACvC,MAAM,KAAK,GAAG,MAAM,IAAA,4BAAe,EAAC;QAChC,IAAI;QACJ,WAAW,EAAE,IAAI;QACjB,IAAI,EAAE,KAAK;QACX,aAAa,EAAE;YACX,eAAe,EAAE;gBACb,KAAK,EAAE,cAAc,CAAC,KAAK;gBAC3B,MAAM,EAAE,cAAc,CAAC,MAAM;aAChC;SACJ;KACJ,CAAC,CAAC;IACH,OAAO,WAAI,CAAC,IAAI,CAAC,KAAe,CAAuB,CAAC;AAC5D,CAAC,CAAA,CAAC;AAlBW,QAAA,WAAW,eAkBtB"}
1
+ {"version":3,"file":"htmlToImage.js","sourceRoot":"","sources":["../../../../src/lib/utils/htmlToImage.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6CAA6D;AAC7D,4EAAiD;AACjD,+BAA4B;AAGrB,MAAM,WAAW,GAAG,CACvB,QAAe,EACf,cAAoB,EACF,EAAE;IACpB,MAAM,IAAI,GAAG,IAAA,oBAAa,EAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IAChD,MAAM,KAAK,GAAG,MAAM,IAAA,4BAAe,EAAC;QAChC,IAAI;QACJ,WAAW,EAAE,IAAI;QACjB,IAAI,EAAE,KAAK;QACX,aAAa,EAAE;YACX,eAAe,EAAE;gBACb,KAAK,EAAE,cAAc,CAAC,KAAK;gBAC3B,MAAM,EAAE,cAAc,CAAC,MAAM;gBAC7B,iBAAiB,EAAE,CAAC;aACvB;SACJ;KACJ,CAAC,CAAC;IACH,OAAO,WAAI,CAAC,IAAI,CAAC,KAAe,CAAuB,CAAC;AAC5D,CAAC,CAAA,CAAC;AAlBW,QAAA,WAAW,eAkBtB"}
@@ -1,12 +1,13 @@
1
1
  import { ImageLayerOptions, ImageType, BoundingBox } from '../types';
2
- type PlaceImageProps = {
2
+ import { RequiredDeep } from 'type-fest';
3
+ type PlaceImageProps<EntryType extends Record<string, string>> = {
3
4
  image: ImageType;
4
5
  box: BoundingBox;
5
6
  backgroundSize: {
6
7
  width: number;
7
8
  height: number;
8
9
  };
9
- options: ImageLayerOptions;
10
+ options: RequiredDeep<ImageLayerOptions<EntryType>>;
10
11
  };
11
- export declare function placeImage({ image, box, backgroundSize, options, }: PlaceImageProps): Promise<ImageType>;
12
+ export declare const placeImage: <EntryType extends Record<string, string>>({ image, box, backgroundSize, options, }: PlaceImageProps<EntryType>) => Promise<ImageType>;
12
13
  export {};
@@ -8,37 +8,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.placeImage = placeImage;
12
+ exports.placeImage = void 0;
16
13
  const virtual_dom_1 = require("virtual-dom");
17
14
  const toPx_1 = require("./toPx");
18
15
  const types_1 = require("../types");
19
- const lodash_merge_1 = __importDefault(require("lodash.merge"));
20
16
  const htmlToImage_1 = require("./htmlToImage");
21
- function placeImage(_a) {
22
- return __awaiter(this, arguments, void 0, function* ({ image, box, backgroundSize, options, }) {
23
- const imageBase64 = yield image.getBase64('image/png');
24
- const mergedOptions = (0, lodash_merge_1.default)(types_1.DEFAULT_IMAGE_LAYER_OPTIONS, options);
25
- const objectFit = types_1.SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
26
- const document = (0, virtual_dom_1.h)('div', {
27
- style: Object.assign({ display: 'flex', position: 'absolute', scale: 1, justifyContent: mergedOptions.justifyContent, alignItems: mergedOptions.alignItems }, (0, toPx_1.boundingBoxToPx)(box)),
28
- }, [
29
- (0, virtual_dom_1.h)('img', {
30
- style: {
31
- objectFit,
32
- flex: '1 1 auto',
33
- minWidth: 0,
34
- minHeight: 0,
35
- maxWidth: '100%',
36
- maxHeight: '100%',
37
- },
38
- src: imageBase64,
39
- }, []),
40
- ]);
41
- return (0, htmlToImage_1.htmlToImage)(document, backgroundSize);
42
- });
43
- }
17
+ const placeImage = (_a) => __awaiter(void 0, [_a], void 0, function* ({ image, box, backgroundSize, options, }) {
18
+ const imageBase64 = yield image.getBase64('image/png');
19
+ const objectFit = types_1.SCALE_MODE_TO_OBJECT_FIT[options.scale];
20
+ const document = (0, virtual_dom_1.h)('div', {
21
+ style: Object.assign({ display: 'flex', position: 'absolute', scale: 1, justifyContent: options.justifyContent, alignItems: options.alignItems }, (0, toPx_1.boundingBoxToPx)(box)),
22
+ }, [
23
+ (0, virtual_dom_1.h)('img', {
24
+ style: {
25
+ objectFit,
26
+ flex: '1 1 auto',
27
+ minWidth: 0,
28
+ minHeight: 0,
29
+ maxWidth: '100%',
30
+ maxHeight: '100%',
31
+ },
32
+ src: imageBase64,
33
+ }, []),
34
+ ]);
35
+ return (0, htmlToImage_1.htmlToImage)(document, backgroundSize);
36
+ });
37
+ exports.placeImage = placeImage;
44
38
  //# sourceMappingURL=placeImage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"placeImage.js","sourceRoot":"","sources":["../../../../src/lib/utils/placeImage.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAmBA,gCA4CC;AA/DD,6CAAgC;AAChC,iCAAyC;AACzC,oCAMkB;AAClB,gEAAiC;AACjC,+CAA4C;AAS5C,SAAsB,UAAU;yDAAC,EAC7B,KAAK,EACL,GAAG,EACH,cAAc,EACd,OAAO,GACO;QACd,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACvD,MAAM,aAAa,GAAG,IAAA,sBAAK,EAAC,mCAA2B,EAAE,OAAO,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,gCAAwB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAEhE,MAAM,QAAQ,GAAG,IAAA,eAAC,EACd,KAAK,EACL;YACI,KAAK,kBACD,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,CAAC,EAER,cAAc,EAAE,aAAa,CAAC,cAAc,EAC5C,UAAU,EAAE,aAAa,CAAC,UAAU,IAEjC,IAAA,sBAAe,EAAC,GAAG,CAAC,CAC1B;SACJ,EACD;YACI,IAAA,eAAC,EACG,KAAK,EACL;gBACI,KAAK,EAAE;oBACH,SAAS;oBACT,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE,CAAC;oBACX,SAAS,EAAE,CAAC;oBACZ,QAAQ,EAAE,MAAM;oBAChB,SAAS,EAAE,MAAM;iBACpB;gBACD,GAAG,EAAE,WAAW;aACnB,EACD,EAAE,CACL;SACJ,CACJ,CAAC;QAEF,OAAO,IAAA,yBAAW,EAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACjD,CAAC;CAAA"}
1
+ {"version":3,"file":"placeImage.js","sourceRoot":"","sources":["../../../../src/lib/utils/placeImage.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAAgC;AAChC,iCAAyC;AACzC,oCAKkB;AAClB,+CAA4C;AAUrC,MAAM,UAAU,GAAG,KAKyB,EAAE,4CALsB,EACvE,KAAK,EACL,GAAG,EACH,cAAc,EACd,OAAO,GACkB;IACzB,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,gCAAwB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE1D,MAAM,QAAQ,GAAG,IAAA,eAAC,EACd,KAAK,EACL;QACI,KAAK,kBACD,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,CAAC,EAER,cAAc,EAAE,OAAO,CAAC,cAAc,EACtC,UAAU,EAAE,OAAO,CAAC,UAAU,IAE3B,IAAA,sBAAe,EAAC,GAAG,CAAC,CAC1B;KACJ,EACD;QACI,IAAA,eAAC,EACG,KAAK,EACL;YACI,KAAK,EAAE;gBACH,SAAS;gBACT,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE,CAAC;gBACX,SAAS,EAAE,CAAC;gBACZ,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,MAAM;aACpB;YACD,GAAG,EAAE,WAAW;SACnB,EACD,EAAE,CACL;KACJ,CACJ,CAAC;IAEF,OAAO,IAAA,yBAAW,EAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AACjD,CAAC,CAAA,CAAA;AA3CY,QAAA,UAAU,cA2CtB"}
@@ -1,2 +1,3 @@
1
1
  import { ImageType, BoundingBox, Size, TextLayerOptions } from '../types';
2
- export declare const renderText: (text: string, box: BoundingBox, backgroundSize: Size, options?: TextLayerOptions) => Promise<ImageType>;
2
+ import { RequiredDeep } from 'type-fest';
3
+ export declare const renderText: <EntryType extends Record<string, string>>(text: string, box: BoundingBox, backgroundSize: Size, options: RequiredDeep<TextLayerOptions<EntryType>>, fonts: Record<string, string>) => Promise<ImageType>;
@@ -11,13 +11,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.renderText = void 0;
13
13
  const virtual_dom_1 = require("virtual-dom");
14
- const types_1 = require("../types");
15
14
  const toPx_1 = require("./toPx");
16
15
  const htmlToImage_1 = require("./htmlToImage");
17
- const renderText = (text, box, backgroundSize, options) => __awaiter(void 0, void 0, void 0, function* () {
18
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
16
+ const renderText = (text, box, backgroundSize, options, fonts) => __awaiter(void 0, void 0, void 0, function* () {
19
17
  let textChildren = [text];
20
- for (const [word, image] of Object.entries((_a = options === null || options === void 0 ? void 0 : options.replacement) !== null && _a !== void 0 ? _a : {})) {
18
+ for (const [word, image] of Object.entries(options.replacement)) {
21
19
  const regex = new RegExp(word, 'gi');
22
20
  const imageBase64 = yield image.getBase64('image/png');
23
21
  let tmpChildren = [];
@@ -32,7 +30,7 @@ const renderText = (text, box, backgroundSize, options) => __awaiter(void 0, voi
32
30
  style: {
33
31
  display: 'inline',
34
32
  verticalAlign: 'middle',
35
- height: (0, toPx_1.toPx)((_c = (_b = options === null || options === void 0 ? void 0 : options.font) === null || _b === void 0 ? void 0 : _b.size) !== null && _c !== void 0 ? _c : 28), // TODO: use constant
33
+ height: (0, toPx_1.toPx)(options.font.size),
36
34
  width: 'auto',
37
35
  },
38
36
  src: imageBase64,
@@ -43,22 +41,33 @@ const renderText = (text, box, backgroundSize, options) => __awaiter(void 0, voi
43
41
  }
44
42
  textChildren = tmpChildren;
45
43
  }
46
- const document = (0, virtual_dom_1.h)('div', {
47
- style: Object.assign({ display: 'flex', overflow: 'visible', position: 'absolute', justifyContent: (_d = options === null || options === void 0 ? void 0 : options.xAlign) !== null && _d !== void 0 ? _d : types_1.DEFAULT_TEXT_LAYER_OPTIONS.xAlign, alignItems: (_e = options === null || options === void 0 ? void 0 : options.yAlign) !== null && _e !== void 0 ? _e : types_1.DEFAULT_TEXT_LAYER_OPTIONS.yAlign }, (0, toPx_1.boundingBoxToPx)(box)),
44
+ const content = (0, virtual_dom_1.h)('div', {
45
+ style: Object.assign({ display: 'flex', overflow: 'visible', position: 'absolute', justifyContent: options.justifyContent, alignItems: options.alignItems }, (0, toPx_1.boundingBoxToPx)(box)),
48
46
  }, [
49
47
  (0, virtual_dom_1.h)('div', {
50
48
  style: {
51
49
  overflow: 'visible',
52
50
  overflowWrap: 'word-wrap',
53
51
  whiteSpace: 'normal',
54
- color: options === null || options === void 0 ? void 0 : options.color,
55
- fontFamily: (_f = options === null || options === void 0 ? void 0 : options.font) === null || _f === void 0 ? void 0 : _f.family,
56
- fontSize: (_g = options === null || options === void 0 ? void 0 : options.font) === null || _g === void 0 ? void 0 : _g.size,
57
- fontStyle: ((_h = options === null || options === void 0 ? void 0 : options.font) === null || _h === void 0 ? void 0 : _h.italic) ? 'italic' : undefined,
58
- fontWeight: ((_j = options === null || options === void 0 ? void 0 : options.font) === null || _j === void 0 ? void 0 : _j.bold) ? 'bold' : undefined,
52
+ color: options.color,
53
+ fontFamily: options.font.family,
54
+ fontSize: options.font.size,
55
+ fontStyle: options.font.italic ? 'italic' : undefined,
56
+ fontWeight: options.font.bold ? 'bold' : undefined,
59
57
  },
60
58
  }, textChildren),
61
59
  ]);
60
+ const document = (0, virtual_dom_1.h)('html', [
61
+ (0, virtual_dom_1.h)('head', [
62
+ (0, virtual_dom_1.h)('style', Object.entries(fonts)
63
+ .map(([name, data]) => `@font-face {
64
+ font-family: '${name}';
65
+ src: url(data:font/ttf;base64,${data}) format('truetype');
66
+ }`)
67
+ .join('\n')),
68
+ ]),
69
+ (0, virtual_dom_1.h)('body', [content]),
70
+ ]);
62
71
  return (0, htmlToImage_1.htmlToImage)(document, backgroundSize);
63
72
  });
64
73
  exports.renderText = renderText;
@@ -1 +1 @@
1
- {"version":3,"file":"renderText.js","sourceRoot":"","sources":["../../../../src/lib/utils/renderText.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"renderText.js","sourceRoot":"","sources":["../../../../src/lib/utils/renderText.ts"],"names":[],"mappings":"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"}
package/build/src/test.js CHANGED
@@ -17,8 +17,9 @@ const lib_1 = require("./lib");
17
17
  const result = yield lib_1.Template.new({
18
18
  defaultFontFamily: 'branela',
19
19
  defaultAssetsPath: 'assets',
20
+ color: 0xC3C3C3FF,
20
21
  })
21
- .text({ key: 'subtitle' }, {
22
+ .text({ key: 'title' }, {
22
23
  left: 25,
23
24
  top: 25,
24
25
  width: 700,
@@ -40,6 +41,7 @@ const lib_1 = require("./lib");
40
41
  size: 38,
41
42
  },
42
43
  color: 'purple',
44
+ skip: (entry) => entry.title.toLowerCase() === 'luigi'
43
45
  })
44
46
  .font('assets/branela.otf', 'branela')
45
47
  .image({ pathFn: entry => `${entry.title.toLowerCase()}.png` }, { left: 25, top: 225, width: 700, height: 700 }, {
@@ -76,14 +78,21 @@ const lib_1 = require("./lib");
76
78
  scale: 'stretch',
77
79
  gap: 50,
78
80
  })
79
- .vbox(() => Promise.resolve([luigi.clone(), luigi.clone()]), {
81
+ .grid(() => Promise.resolve([
82
+ luigi.clone(), luigi.clone(), luigi.clone(),
83
+ luigi.clone(), luigi.clone(), luigi.clone(),
84
+ luigi.clone(), luigi.clone(), luigi.clone(),
85
+ ]), {
80
86
  left: 300,
81
87
  top: 325,
82
- width: 100,
88
+ width: 400,
83
89
  height: 600,
84
90
  }, {
85
- scale: 'keep-ratio',
86
- gap: 50,
91
+ scale: 'stretch',
92
+ gap: 10,
93
+ alignItems: 'flex-start',
94
+ justifyContent: 'flex-start',
95
+ skip: true,
87
96
  })
88
97
  .debug()
89
98
  .fromCsv('assets/data.csv', {
@@ -1 +1 @@
1
- {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hellkite/pipkin",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "Library for board game card generation",
5
5
  "main": "build/src/index.js",
6
6
  "types": "build/src/index.d.ts",
@@ -18,6 +18,7 @@
18
18
  "canvas": "^3.1.0",
19
19
  "jimp": "^1.6.0",
20
20
  "lodash.camelcase": "^4.3.0",
21
+ "lodash.chunk": "^4.2.0",
21
22
  "lodash.concat": "^4.5.0",
22
23
  "lodash.merge": "^4.6.2",
23
24
  "node-html-to-image": "^5.0.0",
@@ -27,12 +28,14 @@
27
28
  },
28
29
  "devDependencies": {
29
30
  "@types/lodash.camelcase": "^4.3.9",
31
+ "@types/lodash.chunk": "^4.2.9",
30
32
  "@types/lodash.concat": "^4.5.9",
31
33
  "@types/lodash.merge": "^4.6.9",
32
34
  "@types/node": "^22.7.5",
33
35
  "@types/papaparse": "^5.3.15",
34
36
  "@types/virtual-dom": "^2.1.4",
35
37
  "gts": "^6.0.2",
38
+ "type-fest": "^4.40.0",
36
39
  "typescript": "^5.6.3"
37
40
  }
38
41
  }
package/roadmap.md CHANGED
@@ -2,19 +2,21 @@
2
2
 
3
3
 
4
4
  ### Features
5
- - [ ] Containers (row, column, grid)
6
- - [ ] Replace text rendering solution with `html2canvas`
7
5
  - [ ] Storage adaptors for fs, web, browser, s3, etc
8
6
  - [ ] Draw geometry
9
- - [ ] Noise overlays
7
+ - [ ] Noise/texture overlays
8
+ - [ ] Text border
10
9
 
10
+ ### Bugfixes
11
+ - [x] Broken custom fonts
11
12
 
12
13
  ### Optimizations
13
14
  - [ ] Optional image registry that reduce repeating loading of same images
14
15
  - [ ] Stream processing support
16
+ - [ ] Overlay the entire card in HTML and render to image only once
15
17
 
16
18
 
17
19
  ### Quality of Life Improvements
18
20
  - [ ] Expose logging, warnings, stats and progress status data
19
21
  - [ ] Runtime resource configuration
20
-
22
+ - [ ] Debugging layer
@@ -5,8 +5,35 @@ import camelCase from 'lodash.camelcase';
5
5
  import concat from 'lodash.concat';
6
6
  import { registerFont } from 'canvas';
7
7
  import path from 'path';
8
- import { drawBoundingBox, hboxPackingFn, placeImage, renderText, vboxPackingFn } from './utils';
9
- import { DirectionContainerOptions, ImageLayerOptions, ImageRef, ImageType, PackingFn, BoundingBox, RenderOptions, Size, TextLayerOptions, TextRef } from './types';
8
+ import {
9
+ drawBoundingBox,
10
+ gridPackingFn,
11
+ hboxPackingFn,
12
+ placeImage,
13
+ renderText,
14
+ vboxPackingFn,
15
+ } from './utils';
16
+ import {
17
+ DirectionContainerOptions,
18
+ ImageLayerOptions,
19
+ ImageRef,
20
+ ImageType,
21
+ PackingFn,
22
+ BoundingBox,
23
+ RenderOptions,
24
+ Size,
25
+ TextLayerOptions,
26
+ TextRef,
27
+ GridContainerOptions,
28
+ DEFAULT_TEXT_LAYER_OPTIONS,
29
+ FontOptions,
30
+ DEFAULT_IMAGE_LAYER_OPTIONS,
31
+ LayerOptions,
32
+ ContainerOptions,
33
+ DEFAULT_CONTAINER_OPTIONS,
34
+ } from './types';
35
+ import merge from 'lodash.merge';
36
+ import { RequiredDeep } from 'type-fest';
10
37
 
11
38
  type RequiredTemplateOptions = {
12
39
  height: number;
@@ -34,13 +61,14 @@ export type LayerFnContext = {
34
61
  export type LayerFn<EntryType> = (
35
62
  entry: EntryType,
36
63
  context: LayerFnContext,
37
- ) => Promise<ImageType>;
64
+ ) => Promise<ImageType | undefined>;
38
65
 
39
66
  export type TemplateLayerFn<EntryType extends Record<string, string>> = (
40
67
  template: Template<EntryType>,
41
68
  ) => Template<EntryType>;
42
69
 
43
70
  export class Template<EntryType extends Record<string, string>> {
71
+ private readonly fonts: Record<string, string> = {};
44
72
  private readonly layers: LayerFn<EntryType>[] = [];
45
73
  private readonly background: ImageType;
46
74
  private debugMode: boolean = false;
@@ -99,40 +127,83 @@ export class Template<EntryType extends Record<string, string>> {
99
127
  return template.render(entry);
100
128
  });
101
129
 
102
- // TODO: logic for rendering debug helpers
103
130
  container = (
104
131
  imagesFn: (entry: EntryType) => Promise<Array<ImageType>>,
132
+ box: BoundingBox,
105
133
  packingFn: PackingFn,
134
+ options?: ContainerOptions<EntryType>,
106
135
  ): this =>
107
- this.layer(async (entry: EntryType) => {
136
+ this.layer(async (entry: EntryType, { debugMode }) => {
137
+ const mergedOptions: RequiredDeep<ContainerOptions<EntryType>> =
138
+ merge({}, DEFAULT_CONTAINER_OPTIONS, options);
139
+ if (this.shouldSkipLayerForEntry(entry, mergedOptions)) {
140
+ return undefined;
141
+ }
142
+
108
143
  const images = await imagesFn(entry);
109
- return packingFn(this.shadowBackground(), images);
144
+ const result = await packingFn(
145
+ box,
146
+ this.shadowBackground(),
147
+ images,
148
+ );
149
+
150
+ // debug mode
151
+ if (debugMode) {
152
+ const debugImage = await drawBoundingBox(
153
+ box,
154
+ this.backgroundSize,
155
+ );
156
+ return debugImage.composite(result);
157
+ }
158
+
159
+ return result;
110
160
  });
111
161
 
112
162
  hbox = (
113
163
  imagesFn: (entry: EntryType) => Promise<Array<ImageType>>,
114
164
  box: BoundingBox,
115
- options?: DirectionContainerOptions,
116
- ): this => this.container(imagesFn, hboxPackingFn(box, options));
165
+ options?: DirectionContainerOptions<EntryType>,
166
+ ): this => this.container(imagesFn, box, hboxPackingFn(options), options);
117
167
 
118
168
  vbox = (
119
169
  imagesFn: (entry: EntryType) => Promise<Array<ImageType>>,
120
170
  box: BoundingBox,
121
- options?: DirectionContainerOptions,
122
- ): this => this.container(imagesFn, vboxPackingFn(box, options));
171
+ options?: DirectionContainerOptions<EntryType>,
172
+ ): this => this.container(imagesFn, box, vboxPackingFn(options), options);
173
+
174
+ grid = (
175
+ imagesFn: (entry: EntryType) => Promise<Array<ImageType>>,
176
+ box: BoundingBox,
177
+ options?: GridContainerOptions<EntryType>,
178
+ ): this => this.container(imagesFn, box, gridPackingFn(options), options);
123
179
 
124
180
  image = (
125
181
  ref: ImageRef<EntryType>,
126
182
  box: BoundingBox,
127
- options: ImageLayerOptions,
183
+ options: ImageLayerOptions<EntryType>,
128
184
  ): this =>
129
185
  this.layer(async (entry, { debugMode }) => {
130
- const image = await this.pathFromImageRef(entry, ref, options);
186
+ const mergedOptions: RequiredDeep<ImageLayerOptions<EntryType>> =
187
+ merge(
188
+ {},
189
+ DEFAULT_IMAGE_LAYER_OPTIONS,
190
+ { assetsPath: this.defaultAssetsPath },
191
+ options,
192
+ );
193
+ if (this.shouldSkipLayerForEntry(entry, mergedOptions)) {
194
+ return undefined;
195
+ }
196
+
197
+ const image = await this.pathFromImageRef(
198
+ entry,
199
+ ref,
200
+ mergedOptions,
201
+ );
131
202
  const result = await placeImage({
132
203
  image,
133
204
  box,
134
205
  backgroundSize: this.backgroundSize,
135
- options,
206
+ options: mergedOptions,
136
207
  });
137
208
 
138
209
  // debug mode
@@ -146,6 +217,7 @@ export class Template<EntryType extends Record<string, string>> {
146
217
 
147
218
  return result;
148
219
  });
220
+
149
221
  loadImage = async (imagePath: string | Buffer): Promise<ImageType> => {
150
222
  const image = (await Jimp.read(imagePath)) as unknown as ImageType;
151
223
  return image;
@@ -154,22 +226,47 @@ export class Template<EntryType extends Record<string, string>> {
154
226
  text = (
155
227
  ref: TextRef<EntryType>,
156
228
  box: BoundingBox,
157
- options?: TextLayerOptions,
229
+ options?: TextLayerOptions<EntryType>,
158
230
  ): this =>
159
231
  this.layer(async (entry, { debugMode }) => {
160
- const text = this.textFromImageRef(entry, ref);
161
- const fontFamily =
162
- options?.font?.family ?? this.defaultFontFamily ?? 'Arial';
163
- return renderText(text, box, this.backgroundSize, {
164
- ...options,
165
- font: { ...options?.font, family: fontFamily },
166
- });
232
+ const mergedOptions = merge(
233
+ {},
234
+ DEFAULT_TEXT_LAYER_OPTIONS,
235
+ {
236
+ font: {
237
+ family: this.defaultFontFamily,
238
+ },
239
+ } as FontOptions,
240
+ options,
241
+ );
242
+ if (this.shouldSkipLayerForEntry(entry, mergedOptions)) {
243
+ return undefined;
244
+ }
245
+
246
+ const text = this.textFromTextRef(entry, ref);
247
+ const result = await renderText(
248
+ text,
249
+ box,
250
+ this.backgroundSize,
251
+ mergedOptions,
252
+ this.fonts,
253
+ );
254
+
255
+ // debug mode
256
+ if (debugMode) {
257
+ const debugImage = await drawBoundingBox(
258
+ box,
259
+ this.backgroundSize,
260
+ );
261
+ return debugImage.composite(result);
262
+ }
263
+
264
+ return result;
167
265
  });
168
266
 
169
267
  font(path: fs.PathLike, name: string): this {
170
- registerFont(path.toString(), {
171
- family: name,
172
- });
268
+ // TODO: pass font weight and font style as well
269
+ this.fonts[name] = fs.readFileSync(path.toString()).toString('base64');
173
270
  return this;
174
271
  }
175
272
 
@@ -179,13 +276,14 @@ export class Template<EntryType extends Record<string, string>> {
179
276
  }
180
277
 
181
278
  private async renderLayers(entry: EntryType): Promise<Array<ImageType>> {
182
- return Promise.all(
279
+ const results = await Promise.all(
183
280
  this.layers.map(layerFn =>
184
281
  layerFn(entry, {
185
282
  debugMode: this.debugMode,
186
283
  }),
187
284
  ),
188
285
  );
286
+ return results.filter(result => !!result);
189
287
  }
190
288
 
191
289
  async render(
@@ -258,12 +356,11 @@ export class Template<EntryType extends Record<string, string>> {
258
356
  private pathFromImageRef = async (
259
357
  entry: EntryType,
260
358
  ref: ImageRef<EntryType>,
261
- options: ImageLayerOptions,
359
+ options: RequiredDeep<ImageLayerOptions<EntryType>>,
262
360
  ): Promise<ImageType> => {
263
- const assetsPath = options?.assetsPath ?? this.defaultAssetsPath;
264
361
  const pathSegments = [];
265
- if (assetsPath) {
266
- pathSegments.push(assetsPath);
362
+ if (options.assetsPath) {
363
+ pathSegments.push(options.assetsPath);
267
364
  }
268
365
 
269
366
  if ('buffer' in ref) {
@@ -283,7 +380,7 @@ export class Template<EntryType extends Record<string, string>> {
283
380
  }
284
381
  };
285
382
 
286
- private textFromImageRef = (
383
+ private textFromTextRef = (
287
384
  entry: EntryType,
288
385
  ref: TextRef<EntryType>,
289
386
  ): string => {
@@ -297,6 +394,14 @@ export class Template<EntryType extends Record<string, string>> {
297
394
  throw new Error('Unknown TextRef variant');
298
395
  }
299
396
  };
300
- }
301
397
 
302
- // TODO: Ledger of actions applied to the image like a logging feed
398
+ private shouldSkipLayerForEntry = (
399
+ entry: EntryType,
400
+ options: LayerOptions<EntryType>,
401
+ ): boolean => {
402
+ if (typeof options.skip === 'function') {
403
+ return options.skip(entry);
404
+ }
405
+ return options.skip ?? false;
406
+ };
407
+ }