215 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import {
 | 
						|
	FileLoader,
 | 
						|
	Loader
 | 
						|
} from 'three';
 | 
						|
import opentype from '../libs/opentype.module.js';
 | 
						|
 | 
						|
/**
 | 
						|
 * Requires opentype.js to be included in the project.
 | 
						|
 * Loads TTF files and converts them into typeface JSON that can be used directly
 | 
						|
 * to create THREE.Font objects.
 | 
						|
 */
 | 
						|
 | 
						|
class TTFLoader extends Loader {
 | 
						|
 | 
						|
	constructor( manager ) {
 | 
						|
 | 
						|
		super( manager );
 | 
						|
 | 
						|
		this.reversed = false;
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	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 );
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	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 };
 |