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 };
							 |