123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import {
- FileLoader,
- Loader
- } from 'three';
- import opentype from '../libs/opentype.module.js';
- /**
- * A loader for the TTF format.
- *
- * Loads TTF files and converts them into typeface JSON that can be used directly
- * to create THREE.Font objects.
- *
- * ```js
- * const loader = new TTFLoader();
- * const json = await loader.loadAsync( 'fonts/ttf/kenpixel.ttf' );
- * const font = new Font( json );
- * ```
- *
- * @augments Loader
- * @three_import import { TTFLoader } from 'three/addons/loaders/TTFLoader.js';
- */
- class TTFLoader extends Loader {
- /**
- * Constructs a new TTF loader.
- *
- * @param {LoadingManager} [manager] - The loading manager.
- */
- constructor( manager ) {
- super( manager );
- /**
- * Whether the TTF commands should be reversed or not.
- *
- * @type {boolean}
- * @default false
- */
- this.reversed = false;
- }
- /**
- * Starts loading from the given URL and passes the loaded TTF asset
- * to the `onLoad()` callback.
- *
- * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
- * @param {function(Object)} onLoad - Executed when the loading process has been finished.
- * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
- * @param {onErrorCallback} onError - Executed when errors occur.
- */
- load( url, onLoad, onProgress, onError ) {
- const scope = this;
- const loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setResponseType( 'arraybuffer' );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( this.withCredentials );
- loader.load( url, function ( buffer ) {
- try {
- onLoad( scope.parse( buffer ) );
- } catch ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- }
- }, onProgress, onError );
- }
- /**
- * Parses the given TTF data and returns a JSON for creating a font.
- *
- * @param {ArrayBuffer} arraybuffer - The raw TTF data as an array buffer.
- * @return {Object} The result JSON.
- */
- parse( arraybuffer ) {
- function convert( font, reversed ) {
- const round = Math.round;
- const glyphs = {};
- const scale = ( 100000 ) / ( ( font.unitsPerEm || 2048 ) * 72 );
- const glyphIndexMap = font.encoding.cmap.glyphIndexMap;
- const unicodes = Object.keys( glyphIndexMap );
- for ( let i = 0; i < unicodes.length; i ++ ) {
- const unicode = unicodes[ i ];
- const glyph = font.glyphs.glyphs[ glyphIndexMap[ unicode ] ];
- if ( unicode !== undefined ) {
- const token = {
- ha: round( glyph.advanceWidth * scale ),
- x_min: round( glyph.xMin * scale ),
- x_max: round( glyph.xMax * scale ),
- o: ''
- };
- if ( reversed ) {
- glyph.path.commands = reverseCommands( glyph.path.commands );
- }
- glyph.path.commands.forEach( function ( command ) {
- if ( command.type.toLowerCase() === 'c' ) {
- command.type = 'b';
- }
- token.o += command.type.toLowerCase() + ' ';
- if ( command.x !== undefined && command.y !== undefined ) {
- token.o += round( command.x * scale ) + ' ' + round( command.y * scale ) + ' ';
- }
- if ( command.x1 !== undefined && command.y1 !== undefined ) {
- token.o += round( command.x1 * scale ) + ' ' + round( command.y1 * scale ) + ' ';
- }
- if ( command.x2 !== undefined && command.y2 !== undefined ) {
- token.o += round( command.x2 * scale ) + ' ' + round( command.y2 * scale ) + ' ';
- }
- } );
- glyphs[ String.fromCodePoint( glyph.unicode ) ] = token;
- }
- }
- return {
- glyphs: glyphs,
- familyName: font.getEnglishName( 'fullName' ),
- ascender: round( font.ascender * scale ),
- descender: round( font.descender * scale ),
- underlinePosition: font.tables.post.underlinePosition,
- underlineThickness: font.tables.post.underlineThickness,
- boundingBox: {
- xMin: font.tables.head.xMin,
- xMax: font.tables.head.xMax,
- yMin: font.tables.head.yMin,
- yMax: font.tables.head.yMax
- },
- resolution: 1000,
- original_font_information: font.tables.name
- };
- }
- function reverseCommands( commands ) {
- const paths = [];
- let path;
- commands.forEach( function ( c ) {
- if ( c.type.toLowerCase() === 'm' ) {
- path = [ c ];
- paths.push( path );
- } else if ( c.type.toLowerCase() !== 'z' ) {
- path.push( c );
- }
- } );
- const reversed = [];
- paths.forEach( function ( p ) {
- const result = {
- type: 'm',
- x: p[ p.length - 1 ].x,
- y: p[ p.length - 1 ].y
- };
- reversed.push( result );
- for ( let i = p.length - 1; i > 0; i -- ) {
- const command = p[ i ];
- const result = { type: command.type };
- if ( command.x2 !== undefined && command.y2 !== undefined ) {
- result.x1 = command.x2;
- result.y1 = command.y2;
- result.x2 = command.x1;
- result.y2 = command.y1;
- } else if ( command.x1 !== undefined && command.y1 !== undefined ) {
- result.x1 = command.x1;
- result.y1 = command.y1;
- }
- result.x = p[ i - 1 ].x;
- result.y = p[ i - 1 ].y;
- reversed.push( result );
- }
- } );
- return reversed;
- }
- return convert( opentype.parse( arraybuffer ), this.reversed );
- }
- }
- export { TTFLoader };
|