TTFLoader.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. console.warn( "THREE.TTFLoader: As part of the transition to ES6 Modules, the files in 'examples/js' have been deprecated in r117 (May 2020) and will be deleted in r124 (December 2020). You can find more information about developing using ES6 Modules in https://threejs.org/docs/index.html#manual/en/introduction/Import-via-modules." );
  2. /**
  3. * @author gero3 / https://github.com/gero3
  4. * @author tentone / https://github.com/tentone
  5. * @author troy351 / https://github.com/troy351
  6. *
  7. * Requires opentype.js to be included in the project.
  8. * Loads TTF files and converts them into typeface JSON that can be used directly
  9. * to create THREE.Font objects.
  10. */
  11. THREE.TTFLoader = function ( manager ) {
  12. THREE.Loader.call( this, manager );
  13. this.reversed = false;
  14. };
  15. THREE.TTFLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
  16. constructor: THREE.TTFLoader,
  17. load: function ( url, onLoad, onProgress, onError ) {
  18. var scope = this;
  19. var loader = new THREE.FileLoader( this.manager );
  20. loader.setPath( this.path );
  21. loader.setResponseType( 'arraybuffer' );
  22. loader.load( url, function ( buffer ) {
  23. try {
  24. onLoad( scope.parse( buffer ) );
  25. } catch ( e ) {
  26. if ( onError ) {
  27. onError( e );
  28. } else {
  29. console.error( e );
  30. }
  31. scope.manager.itemError( url );
  32. }
  33. }, onProgress, onError );
  34. },
  35. parse: function ( arraybuffer ) {
  36. function convert( font, reversed ) {
  37. var round = Math.round;
  38. var glyphs = {};
  39. var scale = ( 100000 ) / ( ( font.unitsPerEm || 2048 ) * 72 );
  40. var glyphIndexMap = font.encoding.cmap.glyphIndexMap;
  41. var unicodes = Object.keys( glyphIndexMap );
  42. for ( var i = 0; i < unicodes.length; i ++ ) {
  43. var unicode = unicodes[ i ];
  44. var glyph = font.glyphs.glyphs[ glyphIndexMap[ unicode ] ];
  45. if ( unicode !== undefined ) {
  46. var token = {
  47. ha: round( glyph.advanceWidth * scale ),
  48. x_min: round( glyph.xMin * scale ),
  49. x_max: round( glyph.xMax * scale ),
  50. o: ''
  51. };
  52. if ( reversed ) {
  53. glyph.path.commands = reverseCommands( glyph.path.commands );
  54. }
  55. glyph.path.commands.forEach( function ( command ) {
  56. if ( command.type.toLowerCase() === 'c' ) {
  57. command.type = 'b';
  58. }
  59. token.o += command.type.toLowerCase() + ' ';
  60. if ( command.x !== undefined && command.y !== undefined ) {
  61. token.o += round( command.x * scale ) + ' ' + round( command.y * scale ) + ' ';
  62. }
  63. if ( command.x1 !== undefined && command.y1 !== undefined ) {
  64. token.o += round( command.x1 * scale ) + ' ' + round( command.y1 * scale ) + ' ';
  65. }
  66. if ( command.x2 !== undefined && command.y2 !== undefined ) {
  67. token.o += round( command.x2 * scale ) + ' ' + round( command.y2 * scale ) + ' ';
  68. }
  69. } );
  70. glyphs[ String.fromCodePoint( glyph.unicode ) ] = token;
  71. }
  72. }
  73. return {
  74. glyphs: glyphs,
  75. familyName: font.getEnglishName( 'fullName' ),
  76. ascender: round( font.ascender * scale ),
  77. descender: round( font.descender * scale ),
  78. underlinePosition: font.tables.post.underlinePosition,
  79. underlineThickness: font.tables.post.underlineThickness,
  80. boundingBox: {
  81. xMin: font.tables.head.xMin,
  82. xMax: font.tables.head.xMax,
  83. yMin: font.tables.head.yMin,
  84. yMax: font.tables.head.yMax
  85. },
  86. resolution: 1000,
  87. original_font_information: font.tables.name
  88. };
  89. }
  90. function reverseCommands( commands ) {
  91. var paths = [];
  92. var path;
  93. commands.forEach( function ( c ) {
  94. if ( c.type.toLowerCase() === 'm' ) {
  95. path = [ c ];
  96. paths.push( path );
  97. } else if ( c.type.toLowerCase() !== 'z' ) {
  98. path.push( c );
  99. }
  100. } );
  101. var reversed = [];
  102. paths.forEach( function ( p ) {
  103. var result = {
  104. type: 'm',
  105. x: p[ p.length - 1 ].x,
  106. y: p[ p.length - 1 ].y
  107. };
  108. reversed.push( result );
  109. for ( var i = p.length - 1; i > 0; i -- ) {
  110. var command = p[ i ];
  111. var result = { type: command.type };
  112. if ( command.x2 !== undefined && command.y2 !== undefined ) {
  113. result.x1 = command.x2;
  114. result.y1 = command.y2;
  115. result.x2 = command.x1;
  116. result.y2 = command.y1;
  117. } else if ( command.x1 !== undefined && command.y1 !== undefined ) {
  118. result.x1 = command.x1;
  119. result.y1 = command.y1;
  120. }
  121. result.x = p[ i - 1 ].x;
  122. result.y = p[ i - 1 ].y;
  123. reversed.push( result );
  124. }
  125. } );
  126. return reversed;
  127. }
  128. if ( typeof opentype === 'undefined' ) {
  129. console.warn( 'THREE.TTFLoader: The loader requires opentype.js. Make sure it\'s included before using the loader.' );
  130. return null;
  131. }
  132. return convert( opentype.parse( arraybuffer ), this.reversed );
  133. }
  134. } );