TTFLoader.js 3.8 KB

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