three-text 0.5.1 → 0.6.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 (61) hide show
  1. package/LICENSE_THIRD_PARTY +15 -0
  2. package/README.md +80 -50
  3. package/dist/index.cjs +66 -20
  4. package/dist/index.d.ts +8 -0
  5. package/dist/index.js +66 -20
  6. package/dist/index.min.cjs +310 -307
  7. package/dist/index.min.js +265 -262
  8. package/dist/index.umd.js +68 -21
  9. package/dist/index.umd.min.js +268 -265
  10. package/dist/three/index.cjs +2 -1
  11. package/dist/three/index.d.ts +1 -0
  12. package/dist/three/index.js +2 -1
  13. package/dist/three/react.cjs +35 -17
  14. package/dist/three/react.d.ts +8 -0
  15. package/dist/three/react.js +35 -17
  16. package/dist/types/core/Text.d.ts +6 -0
  17. package/dist/types/core/types.d.ts +2 -33
  18. package/dist/types/three/index.d.ts +1 -0
  19. package/dist/types/vector/core/index.d.ts +28 -0
  20. package/dist/types/vector/index.d.ts +17 -12
  21. package/dist/types/vector/react.d.ts +3 -4
  22. package/dist/types/vector/slug/SlugPacker.d.ts +2 -0
  23. package/dist/types/vector/slug/curveUtils.d.ts +6 -0
  24. package/dist/types/vector/slug/index.d.ts +8 -0
  25. package/dist/types/vector/slug/shaderStrings.d.ts +4 -0
  26. package/dist/types/vector/slug/slugGLSL.d.ts +21 -0
  27. package/dist/types/vector/slug/slugTSL.d.ts +13 -0
  28. package/dist/types/vector/slug/types.d.ts +30 -0
  29. package/dist/types/vector/slug/unpackVertices.d.ts +11 -0
  30. package/dist/types/vector/webgl/index.d.ts +7 -3
  31. package/dist/types/vector/webgpu/index.d.ts +4 -4
  32. package/dist/vector/all.cjs +21 -0
  33. package/dist/vector/all.d.ts +134 -0
  34. package/dist/vector/all.js +2 -0
  35. package/dist/vector/core/index.cjs +856 -0
  36. package/dist/vector/core/index.d.ts +63 -0
  37. package/dist/vector/core/index.js +854 -0
  38. package/dist/vector/core.cjs +5489 -0
  39. package/dist/vector/core.d.ts +402 -0
  40. package/dist/vector/core.js +5486 -0
  41. package/dist/vector/index.cjs +5 -1305
  42. package/dist/vector/index.d.ts +41 -67
  43. package/dist/vector/index.js +3 -1306
  44. package/dist/vector/index2.cjs +287 -0
  45. package/dist/vector/index2.js +264 -0
  46. package/dist/vector/loopBlinnTSL.d.ts +69 -0
  47. package/dist/vector/react.cjs +54 -40
  48. package/dist/vector/react.d.ts +11 -2
  49. package/dist/vector/react.js +55 -41
  50. package/dist/vector/slugTSL.cjs +252 -0
  51. package/dist/vector/slugTSL.js +231 -0
  52. package/dist/vector/webgl/index.cjs +131 -201
  53. package/dist/vector/webgl/index.d.ts +19 -44
  54. package/dist/vector/webgl/index.js +131 -201
  55. package/dist/vector/webgpu/index.cjs +100 -283
  56. package/dist/vector/webgpu/index.d.ts +16 -45
  57. package/dist/vector/webgpu/index.js +100 -283
  58. package/package.json +6 -1
  59. package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +0 -26
  60. package/dist/types/vector/LoopBlinnGeometry.d.ts +0 -68
  61. package/dist/types/vector/loopBlinnTSL.d.ts +0 -11
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * three-text v0.5.1
3
+ * three-text v0.6.0
4
4
  * Copyright © 2025-2026 Jeremy Tribby, Countertype LLC
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
@@ -3260,6 +3260,8 @@ class Text {
3260
3260
  static { this.patternCache = new Map(); }
3261
3261
  static { this.hbInitPromise = null; }
3262
3262
  static { this.fontCache = new Map(); }
3263
+ static { this.fontLoadPromises = new Map(); }
3264
+ static { this.fontRefCounts = new Map(); }
3263
3265
  static { this.fontCacheMemoryBytes = 0; }
3264
3266
  static { this.maxFontCacheMemoryBytes = Infinity; }
3265
3267
  static { this.fontIdCounter = 0; }
@@ -3304,9 +3306,9 @@ class Text {
3304
3306
  if (!Text.hbInitPromise) {
3305
3307
  Text.hbInitPromise = HarfBuzzLoader.getHarfBuzz();
3306
3308
  }
3307
- const loadedFont = await Text.resolveFont(options);
3309
+ const { loadedFont, fontKey } = await Text.resolveFont(options);
3308
3310
  const text = new Text();
3309
- text.setLoadedFont(loadedFont);
3311
+ text.setLoadedFont(loadedFont, fontKey);
3310
3312
  const result = await text.createLayout(options);
3311
3313
  const update = async (newOptions) => {
3312
3314
  const mergedOptions = { ...options };
@@ -3319,8 +3321,8 @@ class Text {
3319
3321
  if (newOptions.font !== undefined ||
3320
3322
  newOptions.fontVariations !== undefined ||
3321
3323
  newOptions.fontFeatures !== undefined) {
3322
- const newLoadedFont = await Text.resolveFont(mergedOptions);
3323
- text.setLoadedFont(newLoadedFont);
3324
+ const { loadedFont: newLoadedFont, fontKey: newFontKey } = await Text.resolveFont(mergedOptions);
3325
+ text.setLoadedFont(newLoadedFont, newFontKey);
3324
3326
  text.resetHelpers();
3325
3327
  }
3326
3328
  options = mergedOptions;
@@ -3341,6 +3343,22 @@ class Text {
3341
3343
  dispose: () => text.destroy()
3342
3344
  };
3343
3345
  }
3346
+ static retainFont(fontKey) {
3347
+ Text.fontRefCounts.set(fontKey, (Text.fontRefCounts.get(fontKey) ?? 0) + 1);
3348
+ }
3349
+ static releaseFont(fontKey, loadedFont) {
3350
+ const nextCount = (Text.fontRefCounts.get(fontKey) ?? 0) - 1;
3351
+ if (nextCount > 0) {
3352
+ Text.fontRefCounts.set(fontKey, nextCount);
3353
+ return;
3354
+ }
3355
+ Text.fontRefCounts.delete(fontKey);
3356
+ // Cached fonts stay alive while present in the cache. If a font has been
3357
+ // evicted, destroy it once the last live handle releases it.
3358
+ if (!Text.fontCache.has(fontKey)) {
3359
+ FontLoader.destroyFont(loadedFont);
3360
+ }
3361
+ }
3344
3362
  static async resolveFont(options) {
3345
3363
  const baseFontKey = typeof options.font === 'string'
3346
3364
  ? options.font
@@ -3354,9 +3372,17 @@ class Text {
3354
3372
  }
3355
3373
  let loadedFont = Text.fontCache.get(fontKey);
3356
3374
  if (!loadedFont) {
3357
- loadedFont = await Text.loadAndCacheFont(fontKey, options.font, options.fontVariations, options.fontFeatures);
3375
+ let loadPromise = Text.fontLoadPromises.get(fontKey);
3376
+ if (!loadPromise) {
3377
+ loadPromise = Text.loadAndCacheFont(fontKey, options.font, options.fontVariations, options.fontFeatures).finally(() => {
3378
+ Text.fontLoadPromises.delete(fontKey);
3379
+ });
3380
+ Text.fontLoadPromises.set(fontKey, loadPromise);
3381
+ }
3382
+ loadedFont = await loadPromise;
3358
3383
  }
3359
- return loadedFont;
3384
+ Text.retainFont(fontKey);
3385
+ return { loadedFont, fontKey };
3360
3386
  }
3361
3387
  static async loadAndCacheFont(fontKey, font, fontVariations, fontFeatures) {
3362
3388
  const tempText = new Text();
@@ -3388,8 +3414,12 @@ class Text {
3388
3414
  const firstKey = Text.fontCache.keys().next().value;
3389
3415
  if (firstKey === undefined)
3390
3416
  break;
3417
+ const font = Text.fontCache.get(firstKey);
3391
3418
  Text.trackFontCacheRemove(firstKey);
3392
3419
  Text.fontCache.delete(firstKey);
3420
+ if ((Text.fontRefCounts.get(firstKey) ?? 0) <= 0 && font) {
3421
+ FontLoader.destroyFont(font);
3422
+ }
3393
3423
  }
3394
3424
  }
3395
3425
  static generateFontContentHash(buffer) {
@@ -3411,8 +3441,12 @@ class Text {
3411
3441
  return `c${++Text.fontIdCounter}`;
3412
3442
  }
3413
3443
  }
3414
- setLoadedFont(loadedFont) {
3444
+ setLoadedFont(loadedFont, fontKey) {
3445
+ if (this.loadedFont && this.loadedFont !== loadedFont) {
3446
+ this.releaseCurrentFont();
3447
+ }
3415
3448
  this.loadedFont = loadedFont;
3449
+ this.currentFontCacheKey = fontKey;
3416
3450
  const contentHash = Text.generateFontContentHash(loadedFont._buffer);
3417
3451
  this.currentFontId = `font_${contentHash}`;
3418
3452
  if (loadedFont.fontVariations) {
@@ -3422,6 +3456,29 @@ class Text {
3422
3456
  this.currentFontId += `_feat_${Text.stableStringify(loadedFont.fontFeatures)}`;
3423
3457
  }
3424
3458
  }
3459
+ releaseCurrentFont() {
3460
+ if (!this.loadedFont)
3461
+ return;
3462
+ const currentFont = this.loadedFont;
3463
+ const currentFontKey = this.currentFontCacheKey;
3464
+ try {
3465
+ if (currentFontKey) {
3466
+ Text.releaseFont(currentFontKey, currentFont);
3467
+ }
3468
+ else {
3469
+ FontLoader.destroyFont(currentFont);
3470
+ }
3471
+ }
3472
+ catch (error) {
3473
+ logger.warn('Error destroying HarfBuzz objects:', error);
3474
+ }
3475
+ finally {
3476
+ this.loadedFont = undefined;
3477
+ this.currentFontCacheKey = undefined;
3478
+ this.textLayout = undefined;
3479
+ this.textShaper = undefined;
3480
+ }
3481
+ }
3425
3482
  async loadFont(fontSrc, fontVariations, fontFeatures) {
3426
3483
  perfLogger.start('Text.loadFont', {
3427
3484
  fontSrc: typeof fontSrc === 'string' ? fontSrc : `buffer(${fontSrc.byteLength})`
@@ -3641,18 +3698,7 @@ class Text {
3641
3698
  if (!this.loadedFont) {
3642
3699
  return;
3643
3700
  }
3644
- const currentFont = this.loadedFont;
3645
- try {
3646
- FontLoader.destroyFont(currentFont);
3647
- }
3648
- catch (error) {
3649
- logger.warn('Error destroying HarfBuzz objects:', error);
3650
- }
3651
- finally {
3652
- this.loadedFont = undefined;
3653
- this.textLayout = undefined;
3654
- this.textShaper = undefined;
3655
- }
3701
+ this.releaseCurrentFont();
3656
3702
  }
3657
3703
  }
3658
3704