TTFLoader.js 4.4 KB

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