TTFLoader.js 4.3 KB

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