Search code examples
javascriptthree.jsgoogle-fonts

How to convert a google font for use with ThreeJS


How does one convert a downloaded google font from ttf to json for use with ThreeJS FontLoader / TextGeometry?

import LatoFont from '../assets/fonts/lato-bold.json'

const loader = new FontLoader();
const font = loader.parse(LatoFont);

        loader.load(font, font => {
            const textGeo = new TextGeometry("Krypton", {
                font: font,
                size: 200,
                height: 50,
                curveSegments: 12,
                bevelEnabled: false,
                bevelThickness: 0.5,
                bevelSize: 0.3,
                bevelOffset: 0,
                bevelSegments: 5,
            })

            const materials = [
                new THREE.MeshPhongMaterial({ color: 0x00ff00, flatShading: true }), // front
                new THREE.MeshPhongMaterial({ color: 0x00ff00 }) // side
            ]
            const textMesh = new THREE.Mesh(textGeo, materials);

            textGeo.computeBoundingBox();
            const centerOffset = - 0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x);

            textMesh.position.x = centerOffset;
            textMesh.position.y = 100;
            textMesh.position.z = 0;

            textMesh.rotation.x = 0;
            textMesh.rotation.y = Math.PI * 2;

            group.add(textMesh);
        })

Using a popular online convertor I am getting:

Uncaught (in promise) SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

UPDATE:

I updated my vite config to have the static directory. But when I build the library and use it in another project the font still has loading issues.

const path = require('path')
const { defineConfig } = require('vite')

module.exports = defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, 'lib/main.ts'),
      name: 'logo-threejs',
      fileName: (format) => `logo-threejs.${format}.js`
    }
  },
  publicDir: './static',
  assetsInclude: ['**/*.typeface.json'],
});

Project Structure:

enter image description here


Solution

  • I assume that you checked all troubleshooting like path, imports, converting font, json structure…

    Using a popular online convertor I am getting:

    Quite unusually, you load this font using parsing, I rarely come across this method, maybe there is a problem there, I'm not sure.

    Anyway, try this approach.

    import { TextGeometry } from "https://esm.sh/three/addons/geometries/TextGeometry.js";
    import { FontLoader } from "https://esm.sh/three/addons/loaders/FontLoader.js";
    
    //…
    
    const loader = new FontLoader();
    loader.load(
        'https://esm.sh/@compai/font-lato/data/typefaces/normal-900.json',
        function (font) {
            const textGeometry = new TextGeometry("Hiii", {
                font: font,
                size: 2,
                height: 0.2,
            });
            const textMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
            const textMesh = new THREE.Mesh(textGeometry, textMaterial);
            textMesh.position.set(-2, 0, 0);
            scene.add(textMesh);
        }
    );
    

    https://codepen.io/Lucas_Mas/pen/xxMoPwq

    Her you have all fonts:

    https://github.com/components-ai/typefaces/tree/main/packages

    UPDATE 0

    If I had to guess, the problem lies in the location of this font in your tree... Because if you did it like in my example and downloaded this font in ready-made json or converted it here http://gero3.github.io/facetype.js/ then there's probably something wrong with the location. As for the bundler, I use Vite.

    threejs-vite-project/
    ├── node_modules/
    ├── src/
    │   ├── index.html
    │   ├── app.js
    │   └── style.css
    ├── static/
    │   └── font/
    │       └── latoSans.json
    ├── package-lock.json
    ├── package.json
    └── vite.config.js
    

    Ensure your vite.config.js(if you use this one) file is correctly set to serve the static directory.

    import { defineConfig } from 'vite';
    
    export default defineConfig({
      root: 'src',
      build: {
        outDir: '../dist'
      },
      publicDir: '../static'
    });
    

    And then, path should look like this:

    const loader = new FontLoader();
    loader.load(
      '/static/font/latoSans.json',
      function (font) {
        const textGeometry = new TextGeometry("Hiii", {
          font: font,
          size: 2,
          height: 0.2,
        });
    

    Then imports should look like this

    import { FontLoader } from 'three/addons/loaders/FontLoader.js';
    import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
    

    UPDATE 1

    1. I don't know what the name of the font is, but make sure you have the appropriate extension .typeface.json - you wrote that you downloaded this font from the packages from my link. By default, fonts from there does not have this extension.
    2. If you leave default name of font, try change on assetsInclude: ['**/*.json']
    3. Double check path of publicDir (especially path)
    4. Double check import, especially export - because you imported successfully that font before.

    UPDATE 2

    Try this approach

    Project A1

    Tree

    project-Threejs-Vite
    ├── dist
    │   ├── krypton-logo-threejs.es.js
    │   ├── krypton-logo-threejs.umd.js
    │   ├── lato-bold.json
    ├── lib
    │   └── main.ts
    ├── node_modules
    ├── static
    │   └── lato-bold.json
    ├── .gitignore
    ├── index.html
    ├── package-lock.json
    ├── package.json
    ├── README.md
    ├── style.css
    └── tsconfig.json
    

    Vite

    const path = require('path');
    const { defineConfig } = require('vite');
    
    module.exports = defineConfig({
      build: {
        lib: {
          entry: path.resolve(__dirname, 'lib/main.ts'),
          name: 'logo-threejs',
          fileName: (format) => `logo-threejs.${format}.js`
        }
      },
      publicDir: './static',
      assetsInclude: ['**/*.json'],
    });
    

    Project A2

    Tree

    project-B2
    ├── dist
    ├── lib
    ├── node_modules
    ├── public
    │   └── lato-bold.json
    ├── src
    │   └── main.js
    ├── .gitignore
    ├── index.html
    ├── package-lock.json
    ├── package.json
    ├── README.md
    ├── style.css
    └── vite.config.js
    

    Vite

    import { defineConfig } from 'vite';
    
    export default defineConfig({
      publicDir: './public',
      assetsInclude: ['**/*.json'],
    });
    

    Font existing…

    Make SURE that folders contain this font. Because this is common problem with Vite, that for instance, static files (glb, etc.) you have to move manually.

    Path Project A2

    const loader = new FontLoader();
    loader.load('/public/lato-bold.json', …
    

    Absolute path

    If things above didn’t help try set absolute path from public

    const aPath = '/lato-bold.json';
    const loader = new FontLoader();
    loader.load(aPath, …
    

    UPDATE 3

    I don’t think so, that ts has something to do, with fonts loading, more with extensions and files in general. But you never know…

    Try maybe set "resolveJsonModule": true, because default is disabled.

    And BTW, what says devtools network requests, or any clue in console?