@lightningjs/blits 0.9.8 → 0.10.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/boilerplate/common/public/fonts/Lato-Regular.ttf +0 -0
  3. package/boilerplate/common/public/fonts/Raleway-ExtraBold.ttf +0 -0
  4. package/docs/transitions_animations/transitions.md +3 -3
  5. package/package.json +3 -4
  6. package/src/application.d.ts +10 -10
  7. package/src/component/base/announcer.js +27 -0
  8. package/src/component/base/events.js +37 -0
  9. package/src/component/base/index.js +29 -0
  10. package/src/component/base/log.js +27 -0
  11. package/src/component/base/methods.js +136 -0
  12. package/src/component/base/router.js +40 -0
  13. package/src/component/base/timeouts_intervals.js +92 -0
  14. package/src/component/base/utils.js +29 -0
  15. package/src/{lib → component}/setup/computed.js +3 -3
  16. package/src/component/setup/index.js +61 -0
  17. package/src/{lib → component}/setup/input.js +4 -4
  18. package/src/{lib → component}/setup/methods.js +3 -3
  19. package/src/{lib → component}/setup/props.js +3 -3
  20. package/src/{lib → component}/setup/routes.js +3 -3
  21. package/src/{lib → component}/setup/state.js +13 -6
  22. package/src/{lib → component}/setup/watch.js +5 -5
  23. package/src/component.d.ts +157 -38
  24. package/src/component.js +145 -154
  25. package/src/components/Circle.js +5 -5
  26. package/src/components/FPScounter.js +249 -185
  27. package/src/components/Image.js +2 -2
  28. package/src/components/RouterView.js +1 -1
  29. package/src/components/Sprite.js +5 -5
  30. package/src/components/Sprite.orig.js +2 -2
  31. package/src/components/Text.js +11 -11
  32. package/src/components/index.js +32 -0
  33. package/src/components/index.orig.js +32 -0
  34. package/src/engines/L3/element.js +41 -35
  35. package/src/engines/L3/fontLoader.js +16 -2
  36. package/src/engines/L3/launch.js +1 -1
  37. package/src/lib/codegenerator/generator.js +18 -8
  38. package/vite/msdfGenerator.js +52 -53
  39. package/boilerplate/common/public/fonts/Lato-Regular.msdf.json +0 -5383
  40. package/boilerplate/common/public/fonts/Lato-Regular.msdf.png +0 -0
  41. package/boilerplate/common/public/fonts/Raleway-ExtraBold.msdf.json +0 -10838
  42. package/boilerplate/common/public/fonts/Raleway-ExtraBold.msdf.png +0 -0
  43. package/src/lib/setup/base.js +0 -288
  44. /package/src/{lib → component}/setup/props.test.js +0 -0
@@ -21,7 +21,7 @@ export default () =>
21
21
  Component('Image', {
22
22
  /* eslint-disable no-unused-vars */
23
23
  code: {
24
- render: function anonymous(parent, component, context) {
24
+ render: function anonymous(parent, component, context, components) {
25
25
  const elms = []
26
26
 
27
27
  if (!elms[0]) {
@@ -36,7 +36,7 @@ export default () =>
36
36
  return elms
37
37
  },
38
38
  effects: [
39
- function anonymous(component, elms, context) {
39
+ function anonymous(component, elms, context, components) {
40
40
  elms[0].set('imageSource', component.imageSource)
41
41
  },
42
42
  ],
@@ -24,7 +24,7 @@ export default () =>
24
24
  Component('RouterView', {
25
25
  /* eslint-disable no-unused-vars */
26
26
  code: {
27
- render: function anonymous(parent, component, context) {
27
+ render: function anonymous(parent, component, context, components) {
28
28
  const elms = []
29
29
 
30
30
  if (!elms[0]) {
@@ -23,7 +23,7 @@ export default () =>
23
23
  Component('Sprite', {
24
24
  /* eslint-disable no-unused-vars */
25
25
  code: {
26
- render: function anonymous(parent, component, context) {
26
+ render: function anonymous(parent, component, context, components) {
27
27
  const elms = []
28
28
 
29
29
  if (!elms[0]) {
@@ -41,10 +41,10 @@ export default () =>
41
41
  return elms
42
42
  },
43
43
  effects: [
44
- function anonymous(component, elms, context) {
44
+ function anonymous(component, elms, context, components) {
45
45
  elms[0].set('texture', component.texture)
46
46
  },
47
- function anonymous(component, elms, context) {
47
+ function anonymous(component, elms, context, components) {
48
48
  elms[0].set('color', component.color)
49
49
  },
50
50
  ],
@@ -64,7 +64,7 @@ export default () =>
64
64
  : this.map[this.frame]
65
65
 
66
66
  if (this.spriteTexture && options) {
67
- return this[symbols.renderer].createTexture('SubTexture', {
67
+ return this[symbols.renderer]().createTexture('SubTexture', {
68
68
  texture: this.spriteTexture,
69
69
  x: options.x,
70
70
  y: options.y,
@@ -76,7 +76,7 @@ export default () =>
76
76
  },
77
77
  hooks: {
78
78
  ready() {
79
- this.spriteTexture = this[symbols.renderer].createTexture('ImageTexture', {
79
+ this.spriteTexture = this[symbols.renderer]().createTexture('ImageTexture', {
80
80
  src: this.image,
81
81
  })
82
82
  },
@@ -38,7 +38,7 @@ export default () =>
38
38
  : this.map[this.frame]
39
39
 
40
40
  if (this.spriteTexture && options) {
41
- return this[symbols.renderer].createTexture('SubTexture', {
41
+ return this[symbols.renderer]().createTexture('SubTexture', {
42
42
  texture: this.spriteTexture,
43
43
  x: options.x,
44
44
  y: options.y,
@@ -50,7 +50,7 @@ export default () =>
50
50
  },
51
51
  hooks: {
52
52
  ready() {
53
- this.spriteTexture = this[symbols.renderer].createTexture('ImageTexture', {
53
+ this.spriteTexture = this[symbols.renderer]().createTexture('ImageTexture', {
54
54
  src: this.image,
55
55
  })
56
56
  },
@@ -22,7 +22,7 @@ export default () =>
22
22
  Component('Text', {
23
23
  /* eslint-disable no-unused-vars */
24
24
  code: {
25
- render: function anonymous(parent, component, context) {
25
+ render: function anonymous(parent, component, context, components) {
26
26
  const elms = []
27
27
 
28
28
  if (!elms[0]) {
@@ -46,34 +46,34 @@ export default () =>
46
46
  return elms
47
47
  },
48
48
  effects: [
49
- function anonymous(component, elms, context) {
49
+ function anonymous(component, elms, context, components) {
50
50
  elms[0].set('text', component.content)
51
51
  },
52
- function anonymous(component, elms, context) {
52
+ function anonymous(component, elms, context, components) {
53
53
  elms[0].set('fontSize', component.size)
54
54
  },
55
- function anonymous(component, elms, context) {
55
+ function anonymous(component, elms, context, components) {
56
56
  elms[0].set('color', component.color)
57
57
  },
58
- function anonymous(component, elms, context) {
58
+ function anonymous(component, elms, context, components) {
59
59
  elms[0].set('style', component.style)
60
60
  },
61
- function anonymous(component, elms, context) {
61
+ function anonymous(component, elms, context, components) {
62
62
  elms[0].set('weight', component.weight)
63
63
  },
64
- function anonymous(component, elms, context) {
64
+ function anonymous(component, elms, context, components) {
65
65
  elms[0].set('wordWrap', component.wordwrap)
66
66
  },
67
- function anonymous(component, elms, context) {
67
+ function anonymous(component, elms, context, components) {
68
68
  elms[0].set('maxLines', component.maxlines)
69
69
  },
70
- function anonymous(component, elms, context) {
70
+ function anonymous(component, elms, context, components) {
71
71
  elms[0].set('h', component.maxheight)
72
72
  },
73
- function anonymous(component, elms, context) {
73
+ function anonymous(component, elms, context, components) {
74
74
  elms[0].set('textAlign', component.align)
75
75
  },
76
- function anonymous(component, elms, context) {
76
+ function anonymous(component, elms, context, components) {
77
77
  elms[0].set('overflowSuffix', component.textoverflow)
78
78
  },
79
79
  ],
@@ -0,0 +1,32 @@
1
+ /*
2
+ * Copyright 2024 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
18
+ import Image from './Image.js'
19
+ import Circle from './Circle.js'
20
+ import RouterView from './RouterView.js'
21
+ import Sprite from './Sprite.js'
22
+ import Text from './Text.js'
23
+ import FPScounter from './FPScounter.js'
24
+
25
+ export default () => ({
26
+ Image: Image(),
27
+ Circle: Circle(),
28
+ RouterView: RouterView(),
29
+ Sprite: Sprite(),
30
+ Text: Text(),
31
+ FPScounter: FPScounter(),
32
+ })
@@ -0,0 +1,32 @@
1
+ /*
2
+ * Copyright 2024 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
18
+ import Image from './Image.js'
19
+ import Circle from './Circle.js'
20
+ import RouterView from './RouterView.js'
21
+ import Sprite from './Sprite.js'
22
+ import Text from './Text.js'
23
+ import FPScounter from './FPScounter.js'
24
+
25
+ export default () => ({
26
+ Image: Image(),
27
+ Circle: Circle(),
28
+ RouterView: RouterView(),
29
+ Sprite: Sprite(),
30
+ Text: Text(),
31
+ FPScounter: FPScounter(),
32
+ })
@@ -240,6 +240,10 @@ const Props = {
240
240
  set textAlign(v) {
241
241
  this._props.textAlign = v
242
242
  },
243
+ set rtt(v) {
244
+ this._props.rtt = v
245
+ this._set.add('rtt')
246
+ },
243
247
  }
244
248
 
245
249
  const Element = {
@@ -271,9 +275,12 @@ const Element = {
271
275
  // correct for default white nodes (but not for text nodes)
272
276
  if (!props.__textnode && !this.props._set.has('color')) {
273
277
  this.props._props.color =
274
- this.props._set.has('src') || this.props._set.has('texture') ? 0xffffffff : 0
278
+ this.props._set.has('src') ||
279
+ this.props._set.has('texture') ||
280
+ (this.props._set.has('rtt') && this.props._props.rtt === true)
281
+ ? 0xffffffff
282
+ : 0
275
283
  }
276
-
277
284
  this.node = props.__textnode
278
285
  ? renderer.createTextNode(this.props._props)
279
286
  : renderer.createNode(this.props._props)
@@ -312,7 +319,8 @@ const Element = {
312
319
  }
313
320
  }
314
321
  },
315
- animate(prop, value, transition) {
322
+ async animate(prop, value, transition) {
323
+ // if current value is the same as the value to animate to, instantly resolve
316
324
  if (this.node[prop] === value) return Promise.resolve()
317
325
  // check if a transition is already scheduled or running on the same prop
318
326
  if (this.scheduledTransitions[prop]) {
@@ -345,39 +353,37 @@ const Element = {
345
353
  delay: typeof transition === 'object' ? ('delay' in transition ? transition.delay : 0) : 0,
346
354
  })
347
355
 
348
- // schedule transition
349
- return new Promise((resolve) => {
350
- const startValue = this.node[prop]
351
- this.scheduledTransitions[prop] = {
352
- v: props[prop],
353
- cancel: false,
354
- f,
355
- }
356
+ // capture the current value to be used in the transition start
357
+ const startValue = this.node[prop]
356
358
 
357
- try {
358
- f.start()
359
- .waitUntilStarted()
360
- .then((animation) => {
361
- // fire transition start callback if specified
362
- transition.start &&
363
- typeof transition.start === 'function' &&
364
- transition.start.call(this.component, this, prop, startValue)
365
- // continue the chain
366
- animation
367
- .waitUntilStopped()
368
- .then(() => delete this.scheduledTransitions[prop])
369
- .then(() => {
370
- // fire transition end callback if specified
371
- transition.end &&
372
- typeof transition.end === 'function' &&
373
- transition.end.call(this.component, this, prop, this.node[prop])
374
- })
375
- .then(resolve)
376
- })
377
- } catch (e) {
378
- Log.error(e)
379
- }
380
- })
359
+ // schedule the transition for this prop, so it can be canceled /
360
+ // removed if another transition for the same prop starts in the mean time
361
+ this.scheduledTransitions[prop] = {
362
+ v: props[prop],
363
+ cancel: false,
364
+ f,
365
+ }
366
+
367
+ // wait until the animation really starts (depending on specified delay)
368
+ const animation = await f.start().waitUntilStarted()
369
+
370
+ // fire transition start callback if specified
371
+ transition.start &&
372
+ typeof transition.start === 'function' &&
373
+ transition.start.call(this.component, this, prop, startValue)
374
+
375
+ // wait until the animation ends
376
+ const finished = await animation.waitUntilStopped()
377
+
378
+ // removed the prop from scheduled transitions
379
+ delete this.scheduledTransitions[prop]
380
+
381
+ // fire transition end callback if specified
382
+ transition.end &&
383
+ typeof transition.end === 'function' &&
384
+ transition.end.call(this.component, this, prop, this.node[prop])
385
+
386
+ return Promise.resolve()
381
387
  },
382
388
  destroy() {
383
389
  Log.debug('Deleting Node', this.nodeId)
@@ -16,10 +16,24 @@ export default () => {
16
16
  font.json = font.file.replace(/\.[^.]+$/, `.${font.type}.json`)
17
17
  }
18
18
  stage.fontManager.addFontFace(
19
- new SdfTrFontFace(font.family, {}, font.type, stage, font.png, font.json)
19
+ new SdfTrFontFace(font.type, {
20
+ fontFamily: font.family,
21
+ descriptors: {},
22
+ atlasUrl: font.png,
23
+ atlasDataUrl: font.json,
24
+ stage,
25
+ metrics: font.metrics,
26
+ })
20
27
  )
21
28
  } else if (font.type === 'web') {
22
- stage.fontManager.addFontFace(new WebTrFontFace(font.family, {}, font.file))
29
+ stage.fontManager.addFontFace(
30
+ new WebTrFontFace({
31
+ fontFamily: font.family,
32
+ fontUrl: font.file,
33
+ descriptors: {},
34
+ metrics: font.metrics,
35
+ })
36
+ )
23
37
  }
24
38
  })
25
39
  }
@@ -24,7 +24,7 @@ import fontLoader from './fontLoader.js'
24
24
 
25
25
  export let renderer
26
26
 
27
- export default (App, target, settings) => {
27
+ export default (App, target, settings = {}) => {
28
28
  const driver = new MainCoreDriver()
29
29
  // settings.multithreaded === true
30
30
  // ? new ThreadXRenderDriver({
@@ -29,8 +29,10 @@ export default function (templateObject = { children: [] }) {
29
29
  ctx.renderCode.push('return elms')
30
30
 
31
31
  return {
32
- render: new Function('parent', 'component', 'context', ctx.renderCode.join('\n')),
33
- effects: ctx.effectsCode.map((code) => new Function('component', 'elms', 'context', code)),
32
+ render: new Function('parent', 'component', 'context', 'components', ctx.renderCode.join('\n')),
33
+ effects: ctx.effectsCode.map(
34
+ (code) => new Function('component', 'elms', 'context', 'components', code)
35
+ ),
34
36
  context: ctx.context,
35
37
  }
36
38
  }
@@ -119,8 +121,7 @@ const generateComponentCode = function (
119
121
  const cmp${counter} =
120
122
  (context.components && context.components['${
121
123
  templateObject[Symbol.for('componentType')]
122
- }']) ||
123
- component[Symbol.for('components')]['${templateObject[Symbol.for('componentType')]}']
124
+ }']) || components['${templateObject[Symbol.for('componentType')]}']
124
125
  `)
125
126
 
126
127
  if ('key' in templateObject) {
@@ -174,9 +175,18 @@ const generateComponentCode = function (
174
175
  templateObject[Symbol.for('componentType')]
175
176
  }'
176
177
 
177
- ${elm} = (context.components && context.components[componentType] || component[Symbol.for('components')][componentType] || (() => { console.error('component ${
178
- templateObject[Symbol.for('componentType')]
179
- } not found')})).call(null, {props: props${counter}}, ${parent}, component)
178
+ let component${counter}
179
+ if(typeof componentType === 'string') {
180
+ component${counter} = context.components && context.components[componentType] || components[componentType]
181
+ if(!component${counter}) {
182
+ throw new Error('Component "${templateObject[Symbol.for('componentType')]}" not found')
183
+ }
184
+ } else if(typeof componentType === 'function' && componentType.name === 'factory') {
185
+ component${counter} = componentType
186
+ }
187
+
188
+ ${elm} = component${counter}.call(null, {props: props${counter}}, ${parent}, component)
189
+
180
190
  if (${elm}[Symbol.for('slots')][0]) {
181
191
  parent = ${elm}[Symbol.for('slots')][0]
182
192
  component = ${elm}
@@ -215,7 +225,7 @@ const generateForLoopCode = function (templateObject, parent) {
215
225
  }
216
226
 
217
227
  if (parent) {
218
- ctx.renderCode.push(`parent = ${parent}`)
228
+ ctx.renderCode.push(`let parent = ${parent}`)
219
229
  }
220
230
 
221
231
  const forStartCounter = counter
@@ -1,7 +1,25 @@
1
1
  import path from 'path'
2
2
  import * as fs from 'fs'
3
- import generateBMFont from 'msdf-bmfont-xml'
3
+ import { genFont, setGeneratePaths } from '@lightningjs/msdf-generator'
4
+ import { adjustFont } from '@lightningjs/msdf-generator/adjustFont'
4
5
 
6
+ class TaskQueue {
7
+ constructor() {
8
+ this.currentTask = Promise.resolve()
9
+ }
10
+
11
+ async enqueue(task) {
12
+ // Wait for the current task to complete before starting a new one
13
+ this.currentTask = this.currentTask.then(task, this.handleError)
14
+ await this.currentTask
15
+ }
16
+
17
+ handleError(error) {
18
+ console.error('Error during task execution:', error)
19
+ }
20
+ }
21
+
22
+ const fontGenerationQueue = new TaskQueue()
5
23
  let config
6
24
 
7
25
  export default function () {
@@ -15,14 +33,13 @@ export default function () {
15
33
  config = resolvedConfig
16
34
  buildOutputPath = config.build.outDir
17
35
  publicDir = path.join(config.root, 'public')
18
- msdfOutputDir = path.resolve(config.root, 'node_modules', '.tmp-msdf-fonts')
36
+ msdfOutputDir = path.resolve(config.root, 'node_modules', '.tmp-msdf-fonts-v2')
19
37
  },
20
38
  async configureServer(server) {
21
39
  server.middlewareMode = true
22
40
  server.middlewares.use('/', async (req, res, next) => {
23
41
  const file = req.url.substring(req.url.lastIndexOf('/') + 1)
24
42
  const match = file.match(/(.+)\.msdf\.(json|png)/)
25
-
26
43
  // msdf font request
27
44
  if (match) {
28
45
  const fontPath = path.dirname(req.url.split('?')[0]).replace(/^\//, '')
@@ -42,21 +59,28 @@ export default function () {
42
59
  )
43
60
  const mimeType = ext === 'png' ? 'image/png' : 'application/json'
44
61
 
45
- if (!fs.existsSync(generatedFontFile)) {
46
- console.log(`\nGenerating ${targetDir}/${fontName}.msdf.${ext}`)
47
- await generateFont(fontFile, targetDir, fontName)
48
- }
49
-
50
- const fileContent = fs.readFileSync(generatedFontFile)
51
-
52
- // Check if headers have already been sent
53
- if (!res.headersSent) {
54
- res.setHeader('Content-Type', mimeType)
55
- res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
56
- res.end(fileContent)
62
+ await fontGenerationQueue.enqueue(async () => {
63
+ if (!fs.existsSync(generatedFontFile)) {
64
+ // Check if generation is needed
65
+ console.log(`\nGenerating ${targetDir}/${fontName}.msdf.${ext}`)
66
+ await generateSDF(fontFile, path.join(targetDir))
67
+ }
68
+ })
69
+
70
+ if (fs.existsSync(generatedFontFile)) {
71
+ const fileContent = fs.readFileSync(generatedFontFile)
72
+
73
+ // Check if headers have already been sent
74
+ if (!res.headersSent) {
75
+ res.setHeader('Content-Type', mimeType)
76
+ res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
77
+ res.end(fileContent)
78
+ } else {
79
+ // this should never happen except some edge cases
80
+ console.error(`ERROR: Headers already sent for ${req.url}`, res.getHeaders())
81
+ }
57
82
  } else {
58
- // this should never happen except some edge cases
59
- console.error(`ERROR: Headers already sent for ${req.url}`, res.getHeaders())
83
+ next() // Handle case where generation might have failed
60
84
  }
61
85
  } else {
62
86
  next() // ttf file does not exist
@@ -71,7 +95,6 @@ export default function () {
71
95
  },
72
96
  // after build ends, the first hook is renderStart where we can modify the output
73
97
  async renderStart() {
74
- // Find all TTF files under publicDir
75
98
  const ttfFiles = findAllTtfFiles(publicDir)
76
99
 
77
100
  for (const ttfFile of ttfFiles) {
@@ -83,7 +106,7 @@ export default function () {
83
106
  // Check if MSDF files are generated, if not, generate them
84
107
  if (!fs.existsSync(msdfJsonPath) || !fs.existsSync(msdfPngPath)) {
85
108
  console.log(`Generating missing MSDF files for ${ttfFile}`)
86
- await generateFont(ttfFile, path.join(msdfOutputDir, relativePath), baseName)
109
+ await generateSDF(ttfFile, path.join(msdfOutputDir, relativePath))
87
110
  }
88
111
  }
89
112
 
@@ -94,40 +117,16 @@ export default function () {
94
117
  }
95
118
  }
96
119
 
97
- const generateFont = (fontFile, fontDir, fontName) => {
98
- return new Promise((resolve, reject) => {
99
- if (!fs.existsSync(fontDir)) {
100
- fs.mkdirSync(fontDir, { recursive: true })
101
- }
102
- generateBMFont(
103
- fontFile,
104
- {
105
- outputType: 'json',
106
- },
107
- (err, textures, font) => {
108
- if (err) {
109
- console.error(err)
110
- reject(err)
111
- } else {
112
- textures.forEach((texture) => {
113
- try {
114
- fs.writeFileSync(path.resolve(fontDir, `${fontName}.msdf.png`), texture.texture)
115
- } catch (e) {
116
- console.error(e)
117
- reject(e)
118
- }
119
- })
120
- try {
121
- fs.writeFileSync(path.resolve(fontDir, `${fontName}.msdf.json`), font.data)
122
- resolve()
123
- } catch (e) {
124
- console.error(err)
125
- reject(e)
126
- }
127
- }
128
- }
129
- )
130
- })
120
+ const generateSDF = async (inputFilePath, outputDirPath) => {
121
+ // Ensure the destination directory exists
122
+ fs.mkdirSync(outputDirPath, { recursive: true })
123
+
124
+ setGeneratePaths(path.dirname(inputFilePath), outputDirPath)
125
+
126
+ let font = await genFont(path.basename(inputFilePath), 'msdf')
127
+
128
+ if (font) await adjustFont(font)
129
+ else console.error('Failed to generate MSDF file')
131
130
  }
132
131
 
133
132
  // finds all TTF files in a directory