FontLoader.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import {
  2. FileLoader,
  3. Loader,
  4. ShapePath
  5. } from 'three';
  6. /**
  7. * A loader for loading fonts.
  8. *
  9. * You can convert fonts online using [facetype.js](https://gero3.github.io/facetype.js/).
  10. *
  11. * ```js
  12. * const loader = new FontLoader();
  13. * const font = await loader.loadAsync( 'fonts/helvetiker_regular.typeface.json' );
  14. * ```
  15. *
  16. * @augments Loader
  17. * @three_import import { FontLoader } from 'three/addons/loaders/FontLoader.js';
  18. */
  19. class FontLoader extends Loader {
  20. /**
  21. * Constructs a new font loader.
  22. *
  23. * @param {LoadingManager} [manager] - The loading manager.
  24. */
  25. constructor( manager ) {
  26. super( manager );
  27. }
  28. /**
  29. * Starts loading from the given URL and passes the loaded font
  30. * to the `onLoad()` callback.
  31. *
  32. * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
  33. * @param {function(Font)} onLoad - Executed when the loading process has been finished.
  34. * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
  35. * @param {onErrorCallback} onError - Executed when errors occur.
  36. */
  37. load( url, onLoad, onProgress, onError ) {
  38. const scope = this;
  39. const loader = new FileLoader( this.manager );
  40. loader.setPath( this.path );
  41. loader.setRequestHeader( this.requestHeader );
  42. loader.setWithCredentials( this.withCredentials );
  43. loader.load( url, function ( text ) {
  44. const font = scope.parse( JSON.parse( text ) );
  45. if ( onLoad ) onLoad( font );
  46. }, onProgress, onError );
  47. }
  48. /**
  49. * Parses the given font data and returns the resulting font.
  50. *
  51. * @param {Object} json - The raw font data as a JSON object.
  52. * @return {Font} The font.
  53. */
  54. parse( json ) {
  55. return new Font( json );
  56. }
  57. }
  58. /**
  59. * Class representing a font.
  60. */
  61. class Font {
  62. /**
  63. * Constructs a new font.
  64. *
  65. * @param {Object} data - The font data as JSON.
  66. */
  67. constructor( data ) {
  68. /**
  69. * This flag can be used for type testing.
  70. *
  71. * @type {boolean}
  72. * @readonly
  73. * @default true
  74. */
  75. this.isFont = true;
  76. this.type = 'Font';
  77. /**
  78. * The font data as JSON.
  79. *
  80. * @type {Object}
  81. */
  82. this.data = data;
  83. }
  84. /**
  85. * Generates geometry shapes from the given text and size. The result of this method
  86. * should be used with {@link ShapeGeometry} to generate the actual geometry data.
  87. *
  88. * @param {string} text - The text.
  89. * @param {number} [size=100] - The text size.
  90. * @param {string} [direction='ltr'] - Char direction: ltr(left to right), rtl(right to left) & tb(top bottom).
  91. * @return {Array<Shape>} An array of shapes representing the text.
  92. */
  93. generateShapes( text, size = 100, direction = 'ltr' ) {
  94. const shapes = [];
  95. const paths = createPaths( text, size, this.data, direction );
  96. for ( let p = 0, pl = paths.length; p < pl; p ++ ) {
  97. shapes.push( ...paths[ p ].toShapes() );
  98. }
  99. return shapes;
  100. }
  101. }
  102. function createPaths( text, size, data, direction ) {
  103. const chars = Array.from( text );
  104. const scale = size / data.resolution;
  105. const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
  106. const paths = [];
  107. let offsetX = 0, offsetY = 0;
  108. if ( direction == 'rtl' || direction == 'tb' ) {
  109. chars.reverse();
  110. }
  111. for ( let i = 0; i < chars.length; i ++ ) {
  112. const char = chars[ i ];
  113. if ( char === '\n' ) {
  114. offsetX = 0;
  115. offsetY -= line_height;
  116. } else {
  117. const ret = createPath( char, scale, offsetX, offsetY, data );
  118. if ( direction == 'tb' ) {
  119. offsetX = 0;
  120. offsetY += data.ascender * scale;
  121. } else {
  122. offsetX += ret.offsetX;
  123. }
  124. paths.push( ret.path );
  125. }
  126. }
  127. return paths;
  128. }
  129. function createPath( char, scale, offsetX, offsetY, data ) {
  130. const glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
  131. if ( ! glyph ) {
  132. console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' );
  133. return;
  134. }
  135. const path = new ShapePath();
  136. let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
  137. if ( glyph.o ) {
  138. const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
  139. for ( let i = 0, l = outline.length; i < l; ) {
  140. const action = outline[ i ++ ];
  141. switch ( action ) {
  142. case 'm': // moveTo
  143. x = outline[ i ++ ] * scale + offsetX;
  144. y = outline[ i ++ ] * scale + offsetY;
  145. path.moveTo( x, y );
  146. break;
  147. case 'l': // lineTo
  148. x = outline[ i ++ ] * scale + offsetX;
  149. y = outline[ i ++ ] * scale + offsetY;
  150. path.lineTo( x, y );
  151. break;
  152. case 'q': // quadraticCurveTo
  153. cpx = outline[ i ++ ] * scale + offsetX;
  154. cpy = outline[ i ++ ] * scale + offsetY;
  155. cpx1 = outline[ i ++ ] * scale + offsetX;
  156. cpy1 = outline[ i ++ ] * scale + offsetY;
  157. path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
  158. break;
  159. case 'b': // bezierCurveTo
  160. cpx = outline[ i ++ ] * scale + offsetX;
  161. cpy = outline[ i ++ ] * scale + offsetY;
  162. cpx1 = outline[ i ++ ] * scale + offsetX;
  163. cpy1 = outline[ i ++ ] * scale + offsetY;
  164. cpx2 = outline[ i ++ ] * scale + offsetX;
  165. cpy2 = outline[ i ++ ] * scale + offsetY;
  166. path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
  167. break;
  168. }
  169. }
  170. }
  171. return { offsetX: glyph.ha * scale, path: path };
  172. }
  173. export { FontLoader, Font };
粤ICP备19079148号