UNPKG

three

Version:

JavaScript 3D library

243 lines (164 loc) 4.96 kB
import { FileLoader, Loader, ShapePath } from 'three'; /** * A loader for loading fonts. * * You can convert fonts online using [facetype.js]{@link https://gero3.github.io/facetype.js/}. * * ```js * const loader = new FontLoader(); * const font = await loader.loadAsync( 'fonts/helvetiker_regular.typeface.json' ); * ``` * * @augments Loader * @three_import import { FontLoader } from 'three/addons/loaders/FontLoader.js'; */ class FontLoader extends Loader { /** * Constructs a new font loader. * * @param {LoadingManager} [manager] - The loading manager. */ constructor( manager ) { super( manager ); } /** * Starts loading from the given URL and passes the loaded font * 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(Font)} 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.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { const font = scope.parse( JSON.parse( text ) ); if ( onLoad ) onLoad( font ); }, onProgress, onError ); } /** * Parses the given font data and returns the resulting font. * * @param {Object} json - The raw font data as a JSON object. * @return {Font} The font. */ parse( json ) { return new Font( json ); } } /** * Class representing a font. */ class Font { /** * Constructs a new font. * * @param {Object} data - The font data as JSON. */ constructor( data ) { /** * This flag can be used for type testing. * * @type {boolean} * @readonly * @default true */ this.isFont = true; this.type = 'Font'; /** * The font data as JSON. * * @type {Object} */ this.data = data; } /** * Generates geometry shapes from the given text and size. The result of this method * should be used with {@link ShapeGeometry} to generate the actual geometry data. * * @param {string} text - The text. * @param {number} [size=100] - The text size. * @return {Array<Shape>} An array of shapes representing the text. */ generateShapes( text, size = 100 ) { const shapes = []; const paths = createPaths( text, size, this.data ); for ( let p = 0, pl = paths.length; p < pl; p ++ ) { shapes.push( ...paths[ p ].toShapes() ); } return shapes; } } function createPaths( text, size, data ) { const chars = Array.from( text ); const scale = size / data.resolution; const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; const paths = []; let offsetX = 0, offsetY = 0; for ( let i = 0; i < chars.length; i ++ ) { const char = chars[ i ]; if ( char === '\n' ) { offsetX = 0; offsetY -= line_height; } else { const ret = createPath( char, scale, offsetX, offsetY, data ); offsetX += ret.offsetX; paths.push( ret.path ); } } return paths; } function createPath( char, scale, offsetX, offsetY, data ) { const glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; if ( ! glyph ) { console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' ); return; } const path = new ShapePath(); let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; if ( glyph.o ) { const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); for ( let i = 0, l = outline.length; i < l; ) { const action = outline[ i ++ ]; switch ( action ) { case 'm': // moveTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.moveTo( x, y ); break; case 'l': // lineTo x = outline[ i ++ ] * scale + offsetX; y = outline[ i ++ ] * scale + offsetY; path.lineTo( x, y ); break; case 'q': // quadraticCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); break; case 'b': // bezierCurveTo cpx = outline[ i ++ ] * scale + offsetX; cpy = outline[ i ++ ] * scale + offsetY; cpx1 = outline[ i ++ ] * scale + offsetX; cpy1 = outline[ i ++ ] * scale + offsetY; cpx2 = outline[ i ++ ] * scale + offsetX; cpy2 = outline[ i ++ ] * scale + offsetY; path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); break; } } } return { offsetX: glyph.ha * scale, path: path }; } export { FontLoader, Font };