three-text 0.4.11 → 0.5.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 (94) hide show
  1. package/LICENSE +5 -660
  2. package/LICENSE_THIRD_PARTY +15 -49
  3. package/README.md +265 -44
  4. package/dist/index.cjs +3424 -3450
  5. package/dist/index.d.ts +163 -9
  6. package/dist/index.js +3420 -3451
  7. package/dist/index.min.cjs +718 -676
  8. package/dist/index.min.js +721 -679
  9. package/dist/index.umd.js +3561 -3579
  10. package/dist/index.umd.min.js +803 -756
  11. package/dist/p5/index.cjs +2738 -5
  12. package/dist/p5/index.js +2738 -5
  13. package/dist/patterns/index.js +0 -4
  14. package/dist/slug/index.cjs +380 -0
  15. package/dist/slug/index.d.ts +62 -0
  16. package/dist/slug/index.js +374 -0
  17. package/dist/three/index.cjs +50 -35
  18. package/dist/three/index.js +50 -35
  19. package/dist/three/react.cjs +5 -2
  20. package/dist/three/react.d.ts +66 -120
  21. package/dist/three/react.js +6 -3
  22. package/dist/types/core/Text.d.ts +3 -10
  23. package/dist/types/core/cache/sharedCaches.d.ts +2 -1
  24. package/dist/types/core/shaping/DrawCallbacks.d.ts +11 -3
  25. package/dist/types/core/shaping/TextShaper.d.ts +1 -5
  26. package/dist/types/core/types.d.ts +84 -0
  27. package/dist/types/index.d.ts +7 -3
  28. package/dist/types/{core/cache → mesh}/GlyphContourCollector.d.ts +4 -4
  29. package/dist/types/{core/cache → mesh}/GlyphGeometryBuilder.d.ts +5 -5
  30. package/dist/types/mesh/MeshGeometryBuilder.d.ts +18 -0
  31. package/dist/types/{core → mesh}/geometry/BoundaryClusterer.d.ts +1 -1
  32. package/dist/types/{core → mesh}/geometry/Extruder.d.ts +1 -1
  33. package/dist/types/{core → mesh}/geometry/PathOptimizer.d.ts +1 -1
  34. package/dist/types/{core → mesh}/geometry/Polygonizer.d.ts +1 -1
  35. package/dist/types/{core → mesh}/geometry/Tessellator.d.ts +1 -1
  36. package/dist/types/react/utils.d.ts +2 -0
  37. package/dist/types/vector/GlyphOutlineCollector.d.ts +25 -0
  38. package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +26 -0
  39. package/dist/types/vector/LoopBlinnGeometry.d.ts +68 -0
  40. package/dist/types/vector/index.d.ts +29 -0
  41. package/dist/types/vector/loopBlinnTSL.d.ts +11 -0
  42. package/dist/types/vector/react.d.ts +24 -0
  43. package/dist/types/vector/webgl/index.d.ts +7 -0
  44. package/dist/types/vector/webgpu/index.d.ts +11 -0
  45. package/dist/vector/index.cjs +1458 -0
  46. package/dist/vector/index.d.ts +122 -0
  47. package/dist/vector/index.js +1434 -0
  48. package/dist/vector/react.cjs +153 -0
  49. package/dist/vector/react.d.ts +317 -0
  50. package/dist/vector/react.js +132 -0
  51. package/dist/vector/types/slug-lib/src/SlugPacker.d.ts +17 -0
  52. package/dist/vector/types/slug-lib/src/WebGL2Renderer.d.ts +21 -0
  53. package/dist/vector/types/slug-lib/src/WebGPURenderer.d.ts +16 -0
  54. package/dist/vector/types/slug-lib/src/index.d.ts +15 -0
  55. package/dist/vector/types/slug-lib/src/shaderStrings.d.ts +9 -0
  56. package/dist/vector/types/slug-lib/src/types.d.ts +34 -0
  57. package/dist/vector/types/src/core/types.d.ts +381 -0
  58. package/dist/vector/types/src/hyphenation/HyphenationPatternLoader.d.ts +2 -0
  59. package/dist/vector/types/src/hyphenation/index.d.ts +7 -0
  60. package/dist/vector/types/src/hyphenation/types.d.ts +6 -0
  61. package/dist/vector/types/src/utils/Cache.d.ts +14 -0
  62. package/dist/vector/types/src/utils/vectors.d.ts +75 -0
  63. package/dist/vector/types/src/vector/VectorDataBuilder.d.ts +30 -0
  64. package/dist/vector/types/src/vector/VectorThreeAdapter.d.ts +27 -0
  65. package/dist/vector/types/src/vector/index.d.ts +15 -0
  66. package/dist/vector/webgl/index.cjs +229 -0
  67. package/dist/vector/webgl/index.d.ts +53 -0
  68. package/dist/vector/webgl/index.js +227 -0
  69. package/dist/vector/webgpu/index.cjs +321 -0
  70. package/dist/vector/webgpu/index.d.ts +57 -0
  71. package/dist/vector/webgpu/index.js +319 -0
  72. package/dist/webgl-vector/index.cjs +243 -0
  73. package/dist/webgl-vector/index.d.ts +34 -0
  74. package/dist/webgl-vector/index.js +241 -0
  75. package/dist/webgpu-vector/index.cjs +336 -0
  76. package/dist/webgpu-vector/index.d.ts +38 -0
  77. package/dist/webgpu-vector/index.js +334 -0
  78. package/package.json +49 -4
  79. package/dist/patterns/cs.cjs +0 -14
  80. package/dist/patterns/cs.d.ts +0 -28
  81. package/dist/patterns/cs.js +0 -14
  82. package/dist/patterns/cs.umd.js +0 -14
  83. package/dist/patterns/id.cjs +0 -14
  84. package/dist/patterns/id.d.ts +0 -28
  85. package/dist/patterns/id.js +0 -14
  86. package/dist/patterns/id.umd.js +0 -14
  87. package/dist/patterns/mk.cjs +0 -14
  88. package/dist/patterns/mk.d.ts +0 -28
  89. package/dist/patterns/mk.js +0 -14
  90. package/dist/patterns/mk.umd.js +0 -14
  91. package/dist/patterns/sr-cyrl.cjs +0 -14
  92. package/dist/patterns/sr-cyrl.d.ts +0 -28
  93. package/dist/patterns/sr-cyrl.js +0 -14
  94. package/dist/patterns/sr-cyrl.umd.js +0 -14
@@ -1,6 +1,6 @@
1
1
  This document details the licenses of third-party software used in this project.
2
2
 
3
- The `three-text` library is licensed under the AGPL-3.0-or-later.
3
+ The `three-text` library is licensed under the MIT License.
4
4
 
5
5
  This software incorporates code from several third-party projects. The original
6
6
  copyright notices and license terms for these projects are detailed below.
@@ -45,8 +45,8 @@ The software is licensed under the SGI Free Software License B, Version 2.0
45
45
  3. TeX Hyphenation Patterns
46
46
 
47
47
  The hyphenation patterns are derived from the TeX hyphenation pattern files,
48
- and are distributed under a variety of licenses. The copyright notices and
49
- licenses for the patterns used in this project are listed below.
48
+ and are distributed under a variety of permissive licenses. The copyright
49
+ notices and licenses for the patterns included in this project are listed below.
50
50
 
51
51
  Copyright (C) 1986, 1988, 1989 Kauko Saarinen (fi)
52
52
  Copyright (C) 1987-1995 Hanna Kołodziejska, Bogusław Jackowski, Marek Ryćko (pl)
@@ -69,12 +69,10 @@ licenses for the patterns used in this project are listed below.
69
69
  Copyright (C) 2004-2005 Janis Vilims (lv)
70
70
  Copyright (C) 2004-2015 Enn Saar (et)
71
71
  Copyright (C) 2004-2015 Kevin P. Scannell (ga)
72
- Copyright (C) 2004-2007 Rune Kleveland, Ole Michael Selberg, Karl Ove Hufthammer (nb, nn, no)
73
- Copyright (C) 2004—2010 Claudio Beccari (cop)
72
+ Copyright (C) 2004-2007 Rune Kleveland, Ole Michael Selberg, Karl Ove Hufthammer (nb, nn)
74
73
  Copyright (C) 2006, 2007, 2008, 2010 Javier A. Múgica (gl)
75
74
  Copyright (C) 2008-2011 Claudio Beccari (it)
76
75
  Copyright (C) 2008-2011 Dimitrios Filippou (el-monoton, el-polyton)
77
- Copyright (C) 2008-2016 Dimitrios Filippou (grc)
78
76
  Copyright (C) 2010-2015 Nazar Annagurban (tk)
79
77
  Copyright (C) 2011, 2016 Arthur Reutenauer (mul-ethi)
80
78
  Copyright (C) 2012 Claudio Beccari (fur)
@@ -82,48 +80,37 @@ licenses for the patterns used in this project are listed below.
82
80
  Copyright (C) 2013 Claudio Beccari (pms)
83
81
  Copyright (C) 2013 Levan Shoshiashvili (ka)
84
82
  Copyright (C) 2013 Tilla Fick and Chris Swanepoel (af)
85
- Copyright (c) 2013-2024 Deutschsprachige Trennmustermannschaft (de-1901, de-1996, de-ch-1901)
83
+ Copyright (c) 2013-2024 Deutschsprachige Trennmustermannschaft (de-1996)
86
84
  Copyright (C) 2016 Claudio Beccari (oc)
87
85
  Copyright (C) 2016 Maksim Salau (be)
88
86
  Copyright (C) 2016 Santhosh Thottingal (as, bn, gu, hi, kn, ml, mr, or, pa, ta, te)
89
- Copyright (C) 2016-2019 Claudio Beccari, Elie Roux (la-x-liturgic)
90
- Copyright (C) 2018 Wie-Ming Cittānurakkho Bhikkhu (pi)
91
- Copyright (C) 2019 Keno Wehr (la-x-classic)
92
- Copyright (C) 2020 Teemu Likonen, Arthur Rosendahl (fi-x-school)
93
- Copyright (C) Arthur Rosendahl 2018, 2025 (ar, fa, he, vi)
94
87
  Copyright (C) 1987 Pierre A. MacKay, 2008, 2011 TUG (tr): LPPL
95
88
  Copyright (C) 1988, 2004 Jörgen Pind (is): LPPL
96
89
  Copyright (C) 1989-2005 Peter Kleiweg (ia): LPPL
97
- Copyright (C) 1990-2003 Dejan Muhamedagić, Aleksandar Jelenak (sr-cyrl): GPL
98
90
  Copyright (C) 1990, 2008 Dejan Muhamedagić (sh-cyrl, sh-latn): LPPL
99
- Copyright (C) 1992 Jana Chlebíková (sk): GPL
100
- Copyright (C) 1995 Pavel Ševeček (cs): GPL
101
- Copyright (C) 1995-1996 Adrian Rezus (ro): distributed via tex-hyphen project
102
- Copyright (C) 1996, 1997 Jörg Knappen, Terry Mart (id): GPL
91
+ Copyright (C) 1992 Jana Chlebíková (sk): author permission granted
92
+ Copyright (C) 1995-1996 Adrian Rezus (ro): permissive
103
93
  Copyright (C) 1997 Eduard Werner (hsb): LPPL
104
- Copyright (C) 1999 Oliver Corff, Dorjpalam Dorj (mn-cyrl-x-lmc): distributed via tex-hyphen project
94
+ Copyright (C) 1999 Oliver Corff, Dorjpalam Dorj (mn-cyrl): permissive
105
95
  Copyright (C) 1999 Sergei B. Pokrovsky (eo): LPPL 1.0+
106
96
  Copyright (C) 1999-2003 Alexander I. Lebedev (ru): LPPL
107
- Copyright (C) 2003 Bence Nagy (hu): MPL 1.1 / GPLv2 / LGPLv2.1
108
- Copyright (C) 2006 Vasil Taneski (mk): LPPL 1.3a+
109
- Copyright (C) 2006-2011 Yves Codet (sa): distributed via tex-hyphen project
97
+ Copyright (C) 2003 Bence Nagy (hu): MPL 1.1 / LGPLv2.1
98
+ Copyright (C) 2006-2011 Yves Codet (sa): permissive
110
99
  Copyright (C) 2009 Jörg Knappen, Medeni Shemdê (kmr): LPPL 1.3c+
111
100
  Copyright (C) 2010 Sahak Petrosyan (hy): LGPL
112
101
  Copyright (C) 2012-2013 Theppitak Karoonboonyanan (th): LPPL
113
- Copyright (C) 2016 Aleksandr Andreev, Mike Kroutikov (cu): GPLv3+
114
102
  Copyright (C) December 1991-January 1995, July 2003 Gonçal Badenes (ca): LPPL
115
103
 
116
- The following notice applies to patterns distributed under the MIT License. For
117
- other licenses (LPPL, GPL, LGPL, MPL), the full license texts are provided in
118
- the appendices below, or refer to the original license text in the relevant
119
- hyphenation files.
104
+ The hyphenation pattern files themselves contain the specific copyright notices
105
+ and license information for each language. See the tex-hyphen project for
106
+ complete source and licensing details: https://github.com/hyphenation/tex-hyphen
120
107
 
121
108
  ---
122
109
 
123
110
  Appendix A: MIT License text
124
111
 
125
112
  Permission is hereby granted, free of charge, to any person obtaining a copy
126
- of this software and associated documentation files (the Software), to deal
113
+ of this software and associated documentation files (the "Software"), to deal
127
114
  in the Software without restriction, including without limitation the rights
128
115
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
129
116
  copies of the Software, and to permit persons to whom the Software is
@@ -144,14 +131,8 @@ THE SOFTWARE.
144
131
 
145
132
  Appendix B: Apache License 2.0
146
133
 
147
- Apache License
148
- Version 2.0, January 2004
149
- http://www.apache.org/licenses/
150
-
151
134
  For the most recent Apache License 2.0 text, see: https://www.apache.org/licenses/LICENSE-2.0
152
135
 
153
-
154
-
155
136
  Apache License
156
137
  Version 2.0, January 2004
157
138
  http://www.apache.org/licenses/
@@ -327,22 +308,7 @@ For the most recent Apache License 2.0 text, see: https://www.apache.org/license
327
308
  incurred by, or claims asserted against, such Contributor by reason
328
309
  of your accepting any such warranty or additional liability.
329
310
 
330
-
331
- ---
332
-
333
- Appendix B-2: Hyphenation Pattern Licenses
334
-
335
- For hyphenation patterns distributed under GPL, LGPL, LPPL, and MPL, the full
336
- license texts can be found at:
337
-
338
- GNU General Public License (GPL): https://www.gnu.org/licenses/gpl.html
339
- GNU Lesser General Public License (LGPL): https://www.gnu.org/licenses/lgpl.html
340
- LaTeX Project Public License (LPPL): https://www.latex-project.org/lppl/
341
- Mozilla Public License (MPL): https://www.mozilla.org/en-US/MPL/
342
-
343
- The hyphenation pattern files themselves contain the specific copyright notices
344
- and license information for each language. See the tex-hyphen project for
345
- complete source and licensing details: https://github.com/hyphenation/tex-hyphen
311
+ END OF TERMS AND CONDITIONS
346
312
 
347
313
  ---
348
314
 
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/three-text.svg)](https://www.npmjs.com/package/three-text)
4
4
  [![TypeScript](https://img.shields.io/badge/built%20with-TypeScript-007acc.svg)](https://www.typescriptlang.org/)
5
- [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3_or_later-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- High fidelity 3D mesh font geometry and text layout engine for the web
7
+ High fidelity 3D text rendering and layout for the web
8
8
 
9
9
  ![Screenshot of three-text example file](https://countertype.com/assets/three-text/3D.png)
10
10
 
@@ -13,22 +13,21 @@ High fidelity 3D mesh font geometry and text layout engine for the web
13
13
  ## Overview
14
14
 
15
15
  > [!CAUTION]
16
- > three-text is an alpha release and the API may break rapidly. This warning will likely last until the end of March 2026. If API stability is important to you, consider pinning your version. Community feedback is encouraged; please open an issue if you have any suggestions or feedback, thank you
16
+ > three-text is in alpha release and the API may break rapidly. This warning will likely last until summer of 2026. If API stability is important to you, consider pinning your version. Community feedback is encouraged; please open an issue if you have any suggestions or feedback, thank you
17
17
 
18
- **three-text** is a 3D mesh font geometry and text layout library for the web. It supports TTF, OTF, WOFF, and WOFF2 font files. For layout, it uses [TeX](https://en.wikipedia.org/wiki/TeX)-based parameters for breaking text into paragraphs across multiple lines and supports CJK and RTL scripts. three-text caches the geometries it generates for low CPU overhead in languages with lots of repeating glyphs. Variable fonts are supported as static instances at a given axis coordinate, and can be animated by re-drawing each frame with new coordinates
18
+ **three-text** is a 3D font rendering and text layout library for the web. It supports TTF, OTF, WOFF, and WOFF2 font files and uses [TeX](https://en.wikipedia.org/wiki/TeX)-based parameters for layout, with support for CJK and RTL scripts. Two rendering pipelines share the same core: **mesh** (extruded, lit, deformable geometry) and **vector** (resolution-independent Loop-Blinn outlines). Contours and geometries are cached per glyph for low CPU overhead in text-heavy scenes. Variable fonts are supported
19
19
 
20
- The library has a framework-agnostic core that returns raw vertex data, with lightweight adapters for [Three.js](https://threejs.org), [React Three Fiber](https://docs.pmnd.rs/react-three-fiber), [p5.js](https://p5js.org), [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API), and [WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API)
20
+ The library has a framework-agnostic core with lightweight adapters for [Three.js](https://threejs.org), [React Three Fiber](https://docs.pmnd.rs/react-three-fiber), [p5.js](https://p5js.org), [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API), and [WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API)
21
21
 
22
- Under the hood, three-text relies on [harfbuzzjs](https://github.com/harfbuzz/harfbuzzjs) (based on [HarfBuzz](https://github.com/harfbuzz/harfbuzz) by Behdad Esfahbod et al) for text shaping, [Knuth-Plass](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) line breaking (with [SILE](https://github.com/sile-typesetter/sile/blob/master/core/break.lua) being the cleanest modern reference), [Liang](https://tug.org/docs/liang/liang-thesis.pdf) hyphenation and the [TeX hyphenation patterns](https://github.com/hyphenation/tex-hyphen), [libtess-ts](https://github.com/countertype/libtess-ts) (a port of the [GLU tessellator](https://www.songho.ca/opengl/gl_tessellation.html) by Eric Veach) for removing overlaps and triangulation, adaptive curve polygonization from Maxim Shemanarev's [Anti-Grain Geometry](https://web.archive.org/web/20060128212843/http://www.antigrain.com/research/adaptive_bezier/index.html), [Visvalingam-Whyatt](https://hull-repository.worktribe.com/preview/376364/000870493786962263.pdf) [line simplification](https://bost.ocks.org/mike/simplify/), as well as [woff-lib](https://github.com/countertype/woff-lib) for optional WOFF2 support
22
+ Under the hood, three-text relies on a core of [harfbuzzjs](https://github.com/harfbuzz/harfbuzzjs) (based on [HarfBuzz](https://github.com/harfbuzz/harfbuzz) by Behdad Esfahbod et al) for text shaping, [Knuth-Plass](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) line breaking (with [SILE](https://github.com/sile-typesetter/sile/blob/master/core/break.lua) and LuaTex being the closest modern references), [Liang](https://tug.org/docs/liang/liang-thesis.pdf) hyphenation and the [TeX hyphenation patterns](https://github.com/hyphenation/tex-hyphen), and [woff-lib](https://github.com/countertype/woff-lib) for optional WOFF2 support. The mesh text pipeline uses [libtess-ts](https://github.com/countertype/libtess-ts) (a port of the [GLU tessellator](https://www.songho.ca/opengl/gl_tessellation.html) by Eric Veach) for removing overlaps and triangulation, adaptive curve polygonization from Maxim Shemanarev's [Anti-Grain Geometry](https://web.archive.org/web/20060128212843/http://www.antigrain.com/research/adaptive_bezier/index.html), and [Visvalingam-Whyatt](https://hull-repository.worktribe.com/preview/376364/000870493786962263.pdf) [line simplification](https://bost.ocks.org/mike/simplify/). The vector pipeline uses [Loop-Blinn](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) resolution-independent curve rendering with [Kokojima et al.](https://dl.acm.org/doi/10.1145/1179849.1179997) stencil filling
23
23
 
24
24
  ## Table of contents
25
25
 
26
26
  - [Overview](#overview)
27
27
  - [Getting started](#getting-started)
28
28
  - [Architecture](#architecture)
29
- - [Three.js](#threejs)
30
- - [React Three Fiber](#react-three-fiber)
31
- - [p5.js](#p5js)
29
+ - [Mesh vs vector](#mesh-vs-vector)
30
+ - [Basic Usage](#basic-usage)
32
31
  - [Coordinate systems](#coordinate-systems)
33
32
  - [Development and examples](#development-and-examples)
34
33
  - [Why three-text?](#why-three-text)
@@ -63,22 +62,44 @@ npm install three
63
62
 
64
63
  three-text has a framework-agnostic core that processes fonts and generates geometry data. Lightweight adapters convert this data to framework-specific formats:
65
64
 
66
- - **`three-text`** - Three.js adapter (default export, returns BufferGeometry)
67
- - **`three-text/three`** - Same as above (explicit alias)
68
- - **`three-text/three/react`** - React Three Fiber component
65
+ - **`three-text`** - Three.js adapter (default export, returns `BufferGeometry`)
66
+ - **`three-text/mesh`** - Same as above (explicit alias)
67
+ - **`three-text/mesh/react`** - React Three Fiber component for extruded mesh text
68
+ - **`three-text/three`** - Deprecated, use `three-text/mesh`
69
+ - **`three-text/three/react`** - Deprecated, use `three-text/mesh/react`
70
+ - **`three-text/mesh/webgl`** - WebGL mesh buffer utility
71
+ - **`three-text/mesh/webgpu`** - WebGPU mesh buffer utility
72
+ - **`three-text/mesh/p5`** - p5.js adapter
69
73
  - **`three-text/core`** - Framework-agnostic core (returns raw arrays)
70
- - **`three-text/webgl`** - WebGL buffer utility
71
- - **`three-text/webgpu`** - WebGPU buffer utility
72
- - **`three-text/p5`** - p5.js adapter
74
+ - **`three-text/vector`** - Vector rendering (Loop-Blinn and Kokojima stencil fill, resolution-independent)
75
+ - **`three-text/vector/react`** - React Three Fiber component for vector text
76
+ - **`three-text/vector/webgl`** - WebGL vector renderer
77
+ - **`three-text/vector/webgpu`** - WebGPU vector renderer
78
+ - **`three-text/webgl`** - Deprecated, use `three-text/mesh/webgl`
79
+ - **`three-text/webgpu`** - Deprecated, use `three-text/mesh/webgpu`
80
+ - **`three-text/p5`** - Deprecated, use `three-text/mesh/p5`
73
81
 
74
- Most users will just `import { Text } from 'three-text'` for Three.js projects
82
+ Most users will just `import { Text } from 'three-text'` for Three.js projects with mesh, or `import { Text } from 'three-text/vector'` for vector text
83
+
84
+ ### Mesh vs vector
85
+
86
+ The library offers two rendering modes that share the same core (HarfBuzz shaping, Knuth-Plass justification, glyph caching):
87
+
88
+ - **Mesh** (`three-text` (default) / `three-text/mesh`): triangulated geometry you can extrude, light, and shade. Use for 3D text, text in a scene graph, or anywhere you need depth
89
+ - **Vector** (`three-text/vector`): resolution-independent rendering on the GPU without tessellation. Use for text that needs to stay sharp at arbitrary zoom
90
+
91
+ Both can be used in the same project from separate entry points
92
+
93
+ **React Three Fiber:** both `three-text/mesh/react` and `three-text/vector/react` export `Text`
75
94
 
76
95
  ### Basic Usage
77
96
 
78
- #### Three.js
97
+ #### Mesh (Three.js)
98
+
99
+ Extruded `BufferGeometry` — light, shade, and deform as a normal mesh:
79
100
 
80
101
  ```javascript
81
- import { Text } from 'three-text/three';
102
+ import { Text } from 'three-text';
82
103
  import { woff2Decode } from 'woff-lib/woff2/decode';
83
104
  import * as THREE from 'three';
84
105
 
@@ -94,11 +115,58 @@ const mesh = new THREE.Mesh(result.geometry, material);
94
115
  scene.add(mesh);
95
116
  ```
96
117
 
97
- #### React Three Fiber
118
+ #### Vector (Three.js)
119
+
120
+ Resolution-independent outlines via Loop-Blinn stencil passes (see [Vector rendering](#vector-rendering)):
121
+
122
+ ```javascript
123
+ import { Text } from 'three-text/vector';
124
+ import { woff2Decode } from 'woff-lib/woff2/decode';
125
+
126
+ Text.setHarfBuzzPath('/hb/hb.wasm');
127
+ Text.enableWoff2(woff2Decode);
128
+ const result = await Text.create({
129
+ text: 'Hello Vector',
130
+ font: '/fonts/Font.woff2',
131
+ size: 72
132
+ });
133
+
134
+ const vectorData = result.geometryData;
135
+ ```
136
+
137
+ Use `createVectorMeshes(vectorData)` from `three-text/vector` for TSL / `WebGPURenderer`, or build your own stencil materials (see [Vector rendering](#vector-rendering))
138
+
139
+ #### Mesh + vector in one scene
140
+
141
+ Alias one import to avoid the name collision between `Text` components. The entry points share a core (shaping, layout, font cache) so you can mix them freely — fonts load once regardless of which entry point requests them:
142
+
143
+ ```javascript
144
+ import { Text as MeshText } from 'three-text';
145
+ import { Text as VectorText, createVectorMeshes } from 'three-text/vector';
146
+
147
+ MeshText.setHarfBuzzPath('/hb/hb.wasm');
148
+
149
+ const heading = await MeshText.create({
150
+ text: 'Heading',
151
+ font: '/fonts/Font.woff2',
152
+ size: 72, depth: 10
153
+ });
154
+ scene.add(new THREE.Mesh(heading.geometry, material));
155
+
156
+ const caption = await VectorText.create({
157
+ text: 'Caption text',
158
+ font: '/fonts/Font.woff2',
159
+ size: 24
160
+ });
161
+ const { interiorMesh, curveMesh, fillMesh } = createVectorMeshes(caption.geometryData);
162
+ scene.add(interiorMesh, curveMesh, fillMesh);
163
+ ```
164
+
165
+ #### React Three Fiber — mesh
98
166
 
99
167
  ```jsx
100
168
  import { Canvas } from '@react-three/fiber';
101
- import { Text } from 'three-text/three/react';
169
+ import { Text } from 'three-text/mesh/react';
102
170
 
103
171
  Text.setHarfBuzzPath('/hb/hb.wasm');
104
172
 
@@ -114,6 +182,72 @@ function App() {
114
182
  }
115
183
  ```
116
184
 
185
+ #### React Three Fiber — vector
186
+
187
+ The vector `Text` builds three internal meshes (interior / curve / fill) with TSL stencil materials. Requires a renderer that supports `MeshBasicNodeMaterial` (Three.js r170+). With WebGPU, pass a `WebGPURenderer` with `stencil: true` and await `init()` (see the [three.js WebGPU examples](https://threejs.org/examples/?q=webgpu)):
188
+
189
+ ```jsx
190
+ import { Canvas } from '@react-three/fiber';
191
+ import * as THREE from 'three/webgpu';
192
+ import { Text } from 'three-text/vector/react';
193
+
194
+ Text.setHarfBuzzPath('/hb/hb.wasm');
195
+
196
+ function App() {
197
+ return (
198
+ <Canvas
199
+ gl={async (props) => {
200
+ const renderer = new THREE.WebGPURenderer({
201
+ canvas: props.canvas,
202
+ stencil: true
203
+ });
204
+ await renderer.init();
205
+ return renderer;
206
+ }}
207
+ >
208
+ <Text font="/fonts/Font.woff" size={72} fillColor="#ffffff">
209
+ Sharp vector text
210
+ </Text>
211
+ </Canvas>
212
+ );
213
+ }
214
+ ```
215
+
216
+ #### React Three Fiber — both in one app
217
+
218
+ Since both adapters export `Text`, alias one at the import site:
219
+
220
+ ```jsx
221
+ import { Canvas } from '@react-three/fiber';
222
+ import * as THREE from 'three/webgpu';
223
+ import { Text as MeshText } from 'three-text/mesh/react';
224
+ import { Text as VectorText } from 'three-text/vector/react';
225
+
226
+ MeshText.setHarfBuzzPath('/hb/hb.wasm');
227
+
228
+ function App() {
229
+ return (
230
+ <Canvas
231
+ gl={async (props) => {
232
+ const renderer = new THREE.WebGPURenderer({
233
+ canvas: props.canvas,
234
+ stencil: true
235
+ });
236
+ await renderer.init();
237
+ return renderer;
238
+ }}
239
+ >
240
+ <MeshText font="/fonts/Font.woff" size={72} depth={10}>
241
+ Extruded heading
242
+ </MeshText>
243
+ <VectorText font="/fonts/Font.woff" size={24} fillColor="#cccccc">
244
+ Sharp caption
245
+ </VectorText>
246
+ </Canvas>
247
+ );
248
+ }
249
+ ```
250
+
117
251
  #### p5.js
118
252
 
119
253
  ```javascript
@@ -147,6 +281,54 @@ function draw() {
147
281
 
148
282
  `createThreeTextGeometry()` accepts all the same options as Three.js (`layout`, `fontVariations`, `depth`, etc.) and returns `{ geometry, planeBounds, glyphs }`. Use `planeBounds` to center the text
149
283
 
284
+ #### Vector rendering
285
+
286
+ **Raw WebGL2 (via `three-text/vector/webgl`):**
287
+
288
+ ```javascript
289
+ import { Text } from 'three-text/vector';
290
+ import { createWebGLVectorRenderer } from 'three-text/vector/webgl';
291
+ import { woff2Decode } from 'woff-lib/woff2/decode';
292
+
293
+ Text.setHarfBuzzPath('/hb/hb.wasm');
294
+ Text.enableWoff2(woff2Decode);
295
+
296
+ const gl = canvas.getContext('webgl2', { antialias: true, stencil: true });
297
+ const renderer = createWebGLVectorRenderer(gl);
298
+
299
+ const result = await Text.create({ text: 'Hello', font: '/fonts/Font.woff2', size: 72 });
300
+ const vectorData = result.geometryData;
301
+ renderer.setGeometry(vectorData);
302
+
303
+ // In render loop:
304
+ renderer.render(mvpMatrix, new Float32Array([1, 1, 1, 1]));
305
+ ```
306
+
307
+ **Raw WebGPU (via `three-text/vector/webgpu`):**
308
+
309
+ ```javascript
310
+ import { Text } from 'three-text/vector';
311
+ import { createWebGPUVectorRenderer } from 'three-text/vector/webgpu';
312
+ import { woff2Decode } from 'woff-lib/woff2/decode';
313
+
314
+ Text.setHarfBuzzPath('/hb/hb.wasm');
315
+ Text.enableWoff2(woff2Decode);
316
+
317
+ const renderer = createWebGPUVectorRenderer(device, format, {
318
+ depthStencilFormat: 'depth24plus-stencil8',
319
+ sampleCount: 4
320
+ });
321
+
322
+ const result = await Text.create({ text: 'Hello', font: '/fonts/Font.woff2', size: 72 });
323
+ const vectorData = result.geometryData;
324
+ renderer.setGeometry(vectorData);
325
+
326
+ // In render pass:
327
+ renderer.render(passEncoder, mvpMatrix, new Float32Array([1, 1, 1, 1]));
328
+ ```
329
+
330
+ See `examples/webgl-vector.html` and `examples/webgpu-vector.html` for raw WebGL/WebGPU demos. The main interactive demo (`examples/index.html`) uses `WebGPURenderer` with TSL node materials for both mesh and Loop-Blinn vector paths. A `WebGLRenderer` variant is available at `examples/index-webgl.html`
331
+
150
332
  ### Coordinate systems
151
333
 
152
334
  The core library uses a right-handed coordinate system with +Y down. Text extrudes from z=0 toward positive Z
@@ -172,7 +354,7 @@ cp node_modules/harfbuzzjs/hb.wasm public/hb/
172
354
  Then, before any `Text.create()` calls, configure the path:
173
355
 
174
356
  ```javascript
175
- import { Text } from 'three-text/three';
357
+ import { Text } from 'three-text';
176
358
  Text.setHarfBuzzPath('/hb/hb.wasm');
177
359
  ```
178
360
 
@@ -184,7 +366,7 @@ This method is essential for applications that use Web Workers, as it is the onl
184
366
 
185
367
 
186
368
  ```javascript
187
- import { Text } from 'three-text/three';
369
+ import { Text } from 'three-text';
188
370
 
189
371
  // Main thread
190
372
  const wasmResponse = await fetch('/hb/hb.wasm');
@@ -203,7 +385,7 @@ The library will prioritize the buffer if both a path and a buffer have been set
203
385
 
204
386
  **NW.js with CommonJS:** If using `require()` to load the CJS build in NW.js, use Option 2 (buffer-based loading). NW.js's [dual-context architecture](https://docs.nwjs.io/For%20Users/Advanced/JavaScript%20Contexts%20in%20NW.js/#separate-context-mode) causes path resolution issues in this specific scenario. ESM imports and bundled code work normally
205
387
 
206
- **Electron with `file://` protocol:** If loading HTML directly from the filesystem (not via a dev server), use Option 2 (buffer-based loading) or enable `nodeIntegration` in your BrowserWindow
388
+ **Electron with `file://` protocol:** If loading HTML directly from the filesystem (not via a dev server), use Option 2 (buffer-based loading) or enable `nodeIntegration` in your `BrowserWindow`
207
389
 
208
390
  ### Hyphenation patterns
209
391
 
@@ -211,7 +393,7 @@ The library will prioritize the buffer if both a path and a buffer have been set
211
393
 
212
394
  ```javascript
213
395
  import enUs from 'three-text/patterns/en-us';
214
- import { Text } from 'three-text/three';
396
+ import { Text } from 'three-text';
215
397
 
216
398
  Text.registerPattern('en-us', enUs);
217
399
  ```
@@ -253,15 +435,24 @@ Then navigate to `http://localhost:3000`
253
435
 
254
436
  ## Why three-text?
255
437
 
256
- three-text generates high-fidelity 3D mesh geometry from font files. Unlike texture-based approaches, it produces true geometry that can be lit, shaded, and manipulated like any 3D model
438
+ three-text renders text from real font files (TTF, OTF, WOFF, WOFF2) with two pipelines:
439
+
440
+ - **Mesh** — tessellated 3D geometry that can be extruded, lit, and shaded like any model
441
+ - **Vector** — resolution-independent outlines rendered directly from curve data on the GPU, sharp at any zoom or angle
442
+
443
+ Both share the same layout engine (HarfBuzz shaping, Knuth-Plass line breaking) and glyph cache, so a paragraph of 1000 words might only require 50 unique glyphs to be processed
257
444
 
258
445
  Existing solutions take different approaches:
259
446
 
260
- - **Three.js native TextGeometry** uses fonts converted by facetype.js to JSON format. It creates 3D text by extruding flat 2D character outlines. While this produces true 3D geometry with depth, there is no support for real fonts or OpenType features needed for many of the world's scripts
261
- - **three-bmfont-text** renders from pre-generated SDF atlas textures. Atlases are built offline at fixed sizes
262
- - **troika-three-text** generates SDF glyphs at runtime from font files via HarfBuzz. More flexible than bmfont, but still a 2D image-space technique with artifacts up close
447
+ - **Three.js native TextGeometry** extrudes 2D outlines from facetype.js JSON. True 3D geometry with depth, but no support for real fonts or OpenType features needed for many of the world's scripts
448
+ - **three-bmfont-text** renders from pre-generated SDF atlas textures built offline at fixed sizes
449
+ - **troika-three-text** generates SDF glyphs at runtime via HarfBuzz. More flexible than bmfont, but still an image-space technique with artifacts up close
263
450
 
264
- three-text generates true 3D geometry from font files via HarfBuzz. It is sharper at close distances than bitmap approaches, and produces mesh data that can be used with any rendering system. The library caches glyph geometry, so a paragraph of 1000 words might only require 50 unique glyphs to be processed. This makes it well-suited to longer texts. three-text also provides control over typesetting and paragraph justification via TeX-based parameters
451
+ three-text produces actual geometry from font files, sharper at close distances than bitmap approaches, with control over typesetting and paragraph justification via TeX-based parameters
452
+
453
+ ### Why Loop-Blinn
454
+
455
+ The vector path uses Loop-Blinn curve rendering, where each quadratic Bezier segment becomes a triangle whose fragment shader evaluates the implicit equation `u² - v = 0` to resolve inside/outside analytically, while interior regions are filled separately. We also evaluated Eric Lengyel's [Slug](https://github.com/EricLengyel/Slug) algorithm, which renders glyphs by casting rays against banded curve data over a dilated bounding polygon, and tested several antialiasing configurations including adaptive supersampling and alpha-to-coverage. Loop-Blinn produced cleaner results under strong perspective and oblique viewing angles, so that is what we use
265
456
 
266
457
  ## Library structure
267
458
 
@@ -269,20 +460,32 @@ three-text generates true 3D geometry from font files via HarfBuzz. It is sharpe
269
460
  three-text/
270
461
  ├── src/
271
462
  │ ├── core/ # Framework-agnostic text engine
272
- │ │ ├── Text.ts # Core API (returns raw arrays)
463
+ │ │ ├── Text.ts # Core API, font loading, shaping, layout
273
464
  │ │ ├── vectors.ts # Vec2, Vec3, Box3Core
274
465
  │ │ ├── types.ts # TypeScript interfaces
275
466
  │ │ ├── cache/ # Glyph caching system
276
467
  │ │ ├── font/ # Font loading and metrics
277
468
  │ │ ├── shaping/ # HarfBuzz text shaping
278
- │ │ ├── layout/ # Line breaking and text layout
279
- │ └── geometry/ # Tessellation and geometry processing
469
+ │ │ └── layout/ # Line breaking and text layout
470
+ ├── mesh/ # Mesh geometry pipeline
471
+ │ │ ├── MeshGeometryBuilder.ts # Orchestrates mesh output from layout
472
+ │ │ ├── GlyphGeometryBuilder.ts # Instanced geometry from glyph contours
473
+ │ │ ├── GlyphContourCollector.ts # Collects draw callbacks for mesh path
474
+ │ │ └── geometry/ # Tessellation, extrusion, optimization
280
475
  │ ├── three/ # Three.js adapter
281
476
  │ │ ├── index.ts # BufferGeometry wrapper
282
477
  │ │ ├── react.tsx # React component export
283
478
  │ │ └── ThreeText.tsx # React Three Fiber component
284
- │ ├── webgl/ # WebGL buffer utility
285
- │ ├── webgpu/ # WebGPU buffer utility
479
+ │ ├── vector/ # Vector rendering (Loop-Blinn)
480
+ ├── index.ts # Vector entry point and TSL re-exports
481
+ │ │ ├── loopBlinnTSL.ts # TSL adapter for Three.js WebGPURenderer
482
+ │ │ ├── LoopBlinnGeometry.ts # Fan triangulation + curve extraction
483
+ │ │ ├── GlyphVectorGeometryBuilder.ts # Outline collection and geometry packing
484
+ │ │ ├── GlyphOutlineCollector.ts # Collects draw callbacks for vector path
485
+ │ │ ├── webgl/ # WebGL2 stencil-based renderer
486
+ │ │ └── webgpu/ # WebGPU stencil-based renderer
487
+ │ ├── webgl/ # WebGL mesh buffer utility
488
+ │ ├── webgpu/ # WebGPU mesh buffer utility
286
489
  │ ├── p5/ # p5.js adapter
287
490
  │ ├── hyphenation/ # Language-specific hyphenation patterns
288
491
  │ └── utils/ # Performance logging, data structures
@@ -310,13 +513,15 @@ Line badness is calculated based on how much glue must stretch or shrink from it
310
513
 
311
514
  This uses a three-pass approach: first without hyphenation (pretolerance), then with hyphenation (tolerance), and finally with emergency stretch for difficult paragraphs that cannot be broken acceptably
312
515
 
516
+ For book typesetting, TeX uses delta nodes to efficiently handle long paragraphs that may span multiple pages with many possible break points. Since three-text isn't a page layout engine, we take a simpler approach and store cumulative widths directly on each break candidate
517
+
313
518
  #### Hyphenation
314
519
 
315
520
  Hyphenation uses patterns derived from the Tex hyphenation project, converted into optimized trie structures for efficient lookup. The library supports over 70 languages with patterns that follow Liang's algorithm for finding valid hyphenation points while avoiding false positives
316
521
 
317
522
  ### Geometry generation and optimization
318
523
 
319
- The geometry pipeline runs once per unique glyph (or glyph cluster), with intermediate results cached to avoid redundant work:
524
+ By default, three-text runs in mesh mode, generating triangulated geometry from glyph outlines that you can extrude, light, or deform. The mesh pipeline runs once per unique glyph (or glyph cluster), with intermediate results cached to avoid redundant work:
320
525
 
321
526
  1. **Path collection**: HarfBuzz callbacks provide low level drawing operations
322
527
  2. **Curve polygonization**: Flattens bezier curves into line segments, placing more points where curves are tight
@@ -328,6 +533,14 @@ The geometry pipeline runs once per unique glyph (or glyph cluster), with interm
328
533
 
329
534
  The multi-stage geometry approach (curve polygonization followed by cleanup, then triangulation) reduces triangle counts and removes overlaps in variable fonts
330
535
 
536
+ ### Vector rendering
537
+
538
+ The vector pipeline (`three-text/vector`) renders glyphs directly from their mathematical outlines without tessellation or curve flattening. Text stays sharp at any zoom level and the geometry footprint is small -- just the control points of each curve
539
+
540
+ Curves use the [Loop-Blinn](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) technique: each quadratic curve is rendered as a triangle whose fragment shader evaluates `u² - v` to resolve inside/outside, with screen-space derivatives producing a signed distance that feeds alpha-to-coverage for smooth MSAA edges. Glyph interiors use [Kokojima et al.](https://dl.acm.org/doi/10.1145/1179849.1179997) stencil filling: fan-triangulate, stencil XOR, fill where nonzero
541
+
542
+ An alternative for this sort of resolution-independent rendering is [Slug](https://github.com/EricLengyel/Slug) by Eric Lengyel, which casts rays against all curves per fragment to compute winding numbers. Loop-Blinn was chosen here because it integrates with hardware MSAA and alpha-to-coverage directly, without the overhead of adaptive supersampling that Slug requires for comparable antialiasing
543
+
331
544
  #### Glyph caching
332
545
 
333
546
  The library uses a hybrid caching strategy to maximize performance while ensuring visual correctness
@@ -339,7 +552,7 @@ For text with tight tracking, connected scripts, or complex kerning pairs, indiv
339
552
 
340
553
  #### Flat geometry mode
341
554
 
342
- When `depth` is 0, the library generates single-sided geometry, reducing triangles by approximately 50%
555
+ When `depth` is 0 in mesh mode, the library generates single-sided geometry, reducing triangles by approximately 50%
343
556
 
344
557
  - Use `THREE.DoubleSide` for flat text so it remains visible from both sides
345
558
  - For extruded text, `THREE.FrontSide` is typical since front and back faces are separate geometry
@@ -369,11 +582,11 @@ const text = await Text.create({
369
582
  },
370
583
  });
371
584
 
372
- // Fixed-step: 8 segments per curve
585
+ // Fixed-step: 32 segments per curve
373
586
  const text = await Text.create({
374
587
  text: 'Sample',
375
588
  font: '/fonts/Font.ttf',
376
- curveSteps: 8,
589
+ curveSteps: 32,
377
590
  });
378
591
  ```
379
592
 
@@ -385,6 +598,8 @@ After curve polygonization, the library applies Visvalingam-Whyatt simplificatio
385
598
  const text = await Text.create({
386
599
  text: 'Sample text',
387
600
  font: '/fonts/Font.ttf',
601
+ // Fixed-step: 32 segments per curve
602
+ curveSteps: 32,
388
603
  geometryOptimization: {
389
604
  areaThreshold: 1.0, // remove triangles < 1 font unit²
390
605
  },
@@ -533,7 +748,7 @@ Common tags include [`liga`](https://learn.microsoft.com/en-us/typography/openty
533
748
 
534
749
  ### Per-glyph attributes
535
750
 
536
- For shader-based animations and interactive effects, the library can generate per-vertex attributes that identify which glyph each vertex belongs to:
751
+ For shader-based animations and interactive effects, the library can generate per-vertex attributes that identify which glyph each vertex belongs to. Both mesh and vector entry points support this: pass `perGlyphAttributes: true` to `Text.create()`
537
752
 
538
753
  ```javascript
539
754
  const text = await Text.create({
@@ -550,7 +765,9 @@ const text = await Text.create({
550
765
  // - glyphBaselineY (float): Y coordinate of glyph baseline
551
766
  ```
552
767
 
553
- This option bypasses overlap-based clustering and adds vertex attributes suitable for per-character manipulation in vertex shaders. Each unique glyph is still tessellated only once and cached for reuse. The tradeoff is potential visual artifacts where glyphs actually overlap (tight kerning, cursive scripts)
768
+ **Mesh:** attributes live on the extruded `geometry`. **Vector:** the same attributes are emitted on interior, curve, and fill buffer geometries. When you need per-glyph draw ranges (for example stencil passes that must not XOR across overlapping glyphs), use `geometryData.glyphRanges`: each entry lists index/vertex ranges for that glyph’s interior, curve, and fill quads
769
+
770
+ This option bypasses overlap-based clustering and adds vertex attributes suitable for per-character manipulation in vertex shaders (or TSL `positionNode` displacements). Each unique glyph is still tessellated only once and cached for reuse. The tradeoff is potential visual artifacts where glyphs actually overlap (tight kerning, cursive scripts)
554
771
 
555
772
  ## Querying text content
556
773
 
@@ -945,7 +1162,7 @@ While `three-text` runs on all modern browsers, performance varies significantly
945
1162
 
946
1163
  **Safari** for macOS shows reduced performance, which is likely due to the platform's conservative resource management; 120FPS is not acheivable
947
1164
 
948
- The library was also tested on a Brightsign 223HD, which took a long time to generate the initial geometry but seemed fine after that
1165
+ The library was also tested on a Brightsign 223HD, which took a very long time to generate the initial geometry but ran fine after that. We did not push our luck with further testing
949
1166
 
950
1167
  ## Testing
951
1168
 
@@ -1023,8 +1240,12 @@ The build generates multiple module formats for core and all adapters:
1023
1240
  **Adapters:**
1024
1241
  - `dist/three/` - Three.js adapter
1025
1242
  - `dist/three/react.js` - React component
1026
- - `dist/webgl/` - WebGL utility
1027
- - `dist/webgpu/` - WebGPU utility
1243
+ - `dist/vector/` - Vector rendering (Loop-Blinn, Three.js adapter)
1244
+ - `dist/vector/react.js` - React Three Fiber vector component
1245
+ - `dist/webgl/` - WebGL mesh buffer utility
1246
+ - `dist/vector/webgl/` - WebGL vector renderer
1247
+ - `dist/webgpu/` - WebGPU mesh buffer utility
1248
+ - `dist/vector/webgpu/` - WebGPU vector renderer
1028
1249
  - `dist/p5/` - p5.js adapter
1029
1250
 
1030
1251
  **Patterns:**
@@ -1036,6 +1257,6 @@ The build generates multiple module formats for core and all adapters:
1036
1257
 
1037
1258
  ## License
1038
1259
 
1039
- `three-text` was written by Jeremy Tribby ([@jpt](https://github.com/jpt)) and is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See the [LICENSE](LICENSE) file for details
1260
+ `three-text` was written by Jeremy Tribby ([@jpt](https://github.com/jpt)) and is licensed under the MIT License. See the [LICENSE](LICENSE) file for details
1040
1261
 
1041
1262
  This software includes code from third-party libraries under compatible permissive licenses. For full license details, see the [LICENSE_THIRD_PARTY](LICENSE_THIRD_PARTY) file