• Stars
    star
    716
  • Rank 61,002 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 12 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

This library adds a new Font() object to the JavaScript toolbox, similar to new Image() for images

LibFont - lifting the hood on your fonts

If you're looking for the (really) old ES5 version of LibFont, when it was still called "Font.js", you should be able to find it here.

td;dr:

Node installation

Use npm install lib-font, after which the package can be imported using import { Font } from "lib-font".

Note that there is no legacy commonjs version of this library available. Node LTS 14 and above have native support for ES modules - Have a look at babel's transform-modules-commonjs plugin if you really have no choice but to use commonjs.

Browser "installation"

You can either use the pre-built lib-font.browser.js bundle, or you can download the code and unpack it into a vendor directory, then point to the lib-font.js file in that directory. Either way, remember to tell the browser that this is modern ES module code by using type="module" in the script tag, and be kind to your users and remember the async attribute, too:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>...</title>
    ...
    <script type="module" src=".../lib-font.js" async></script>
  </head>

  <body>
    ...
  </body>
</html>

(Although you'll generally want to use this in something so your HTML would look more like <script type="module" src="./js/myscript.js" async></script> with the myscript.js file having an import { Font } from "./lib-font"; instead)

Note that there is no legacy ES5 version of this library available, because there are no ES5 browsers anymore, every single still in support browser supports ES Modules, so if you really need to target dead browsers have a look at babel.

Introduction

What if you could actually inspect your fonts? In the same context that you actually use those fonts?

That's what this is for:

// On the Node side: import { Font } from "lib-font";
// In browser-ESM context: import { Font } from "your/js/dir/lib-font-browser.js";
//
// Also note that on the browser side, you can either use the ESM import 
// or you can load the library "on its own" using its own script tag,
// which sets up a global `Font` object for you to use.

// Create a font object
const myFont = new Font(`Adobe Source Code Pro`);

// Assign event handling (.addEventListener version supported too, of course)
myFont.onerror = evt => console.error(evt);
myFont.onload = evt => doSomeFontThings(evt);

// Kick off the font load by setting a source file, exactly as you would
// for an <img> or <script> element when building them as nodes in JS.
myFont.src = `./fonts/SourceCodeVariable-Roman.otf`;

// When the font's up and loaded in, let's do some testing!
function doSomeFontThings(evt) {
    // We can either rely on scoped access to font, but because the onload function
    // is not guaranteed to live in the same scope, the font is in the event, too.
    const font = evt.detail.font;

    // First, let's test some characters:
    [`a`, `→`, `嬉`].forEach(char => console.log(`Font supports '${char}': ${
        font.supports(char)
    }`));

    // Then, let's check some OpenType things
    const GSUB = font.opentype.tables.GSUB;

    // Let's figure out which writing scripts this font supports:
    console.log(`This font supports the following scripts: ${
        `"${GSUB.getSupportedScripts().join(`", "`)}"`
    }`);

    // DFLT is a given, but let's see if `latn` has any special language/system rules...
    const latn = GSUB.getScriptTable('latn');
    console.log(`Special langsys for "latn": ${
        `"${GSUB.getSupportedLangSys(latn).join(`", "`)}"`
    }`);

    // Wow, "Northern Sami" support? Really? Which OpenType features does that use?
    const nsm = GSUB.getLangSysTable(latn, "NSM ");
    console.log(`OpenType features for the Northern Sami version of latin script:`,
        `"${GSUB.getFeatures(nsm).map(f => f.featureTag).join(`", "`)}"`
    );

    // Oh wait, this is a variable font, isn't it.
    console.log(`This is a variable font: ${!!font.opentype.tables.fvar}`);

    // Which axes does it support?
    console.log(`This variable font supposed the following axes: ${
        `"${font.opentype.tables.fvar.getSupportedAxes().join(`", "`)}"`
    }`);
}

You can also pass in a file directly, e.g. using the HTML Drag and Drop API. In order to deal with the font as bytecode, you can use the font.fromDataBuffer(bytecode, filename) function to kick off the font loading:

const myFont = new Font(`Adobe Source Code Pro`);

// Grab file frop drop event or file upload
const file = e.target.files[0];

// Use FileReader to, well, read the file
const reader = new FileReader();
reader.readAsArrayBuffer(file);

reader.onload = function() {
    // Pass the buffer, and the original filename
    myFont.fromDataBuffer(reader.result, file.name);
    myFont.onload = e => {
        // ...
    };
};

What about a tag/element?

This library does not offer a <font src="..." ...> tag, in part because proper custom elements must have a hyphen in their name, but primarily because the only DOM related work that a <font> tag would be useful for is already handled by <style> (for declaring a @font-face) and <link> (for importing a @font-face stylesheet).

API

The uplifted library API is still pending... As of right now, a lot of the functions and properties are pretty easily found if you know your way around an OpenType font already by looking at the source as well as the tests, but that's not ideal - API docs will be forthcoming but can always use help.

That said, this section will keep getting expanded as the API gets consolidated.

Font

const f = new Font(fontFamilyName, optionsObject)

The family name is use for stylesheet building, and the options object can contain the following fields:

fieldname type default description
skipStyleSheet boolean false Determines whether not to build the @font-face stylesheet.
styleRules object {} A set of key/value pairs representing CSS rules to add to the @font-face declaration
errorOnStyle boolean false Whether to error out, or merely warn, if the font type cannot be determined from the src assignment.

f.onerror = evt => ... / f.addEventListener('error', evt => ...)

Error handling for anything font related

f.onload = evt => ... / f.addEventListener('load', evt => ...)

Load handling for anything font related

f.src = ...

Bind a font to a source URL. This can be a fully qualified URL, a relative URL, a Blob URL, or a Data-URL.

const actualFont = f.opentype

The actual opentype font representation is font in the .opentype property.

const fontTables = f.opentype.tables

This is the main access point for any font table, where each table is accessed directly by name. E.g. in order to access the cmap table, you use const cmap = f.opentype.tables.cmap, GSUB is const GSUB = f.opentype.tables.GSUB, etc.

Development

The main entry point for this library is ./lib-font.js, with all code dependencies found in the ./src directory,.

Testing

The npm test command should be all you need in order to run the tests, provided you ran npm install first, of course.

  • Node based testing uses Jest tests, found in the ./testing/node dir.
  • Browser based testing uses Puppetter, found in the ./testing/browser/tests dir.

Compatibility

This library is designed to run both in any browser and version of Node.js versions that supports es modules.

  • Browsers: see the caniuse matrix (tl;dr: basically everything except IE11).
  • Node: native support as of v14 (--experimental-modules runtime option as of v12).

Preemptive answers to questions

Why don't woff/woff2 work?

They do, but they rely on having the gzip inflater and brotli decoder libraries loaded. You can find those in the ./lib dir, as they are optional: without them regular parsing still works fine, but with inflate loaded, woff parsing will succeed, and with unbrotli loaded, woff2 parsing will succeed.

To make this work on your own pages, add the following bit to your document head, with the appropriate path to these two libraries, of course:

    <script src="./lib/inflate.js" defer></script>
    <script src="./lib/unbrotli.js" defer></script>

Why can't this draw stuff??

Because you already have lots of text shaping engines available. In the browser, it's literally your browser (you can already draw all the text you need, properly shaped and typeset, both in HTML and on a Canvas). In node, it's whatever graphics library you're using to already draw everything else you need to draw.

Proper OpenType text shaping is incredibly complex and requires a lot of specialized code; there is no reason for this library to pretend it supports text shaping when it's guaranteed to do it worse than other technologies you're already using.

Why would I use this instead of OpenType.js or Fontkit or something?

I don't have a good answer to that. Those are some great projects, you probably should use them if they do what you need? The reason I needed this is because it doesn't do text shaping: it just lets me query the opentype data to get me the information I need, without being too big of a library. And I've written enough OpenType parsers to know how much code goes into the actual shaping.

How do I use this with webpack?

By first remembering that bundling was born out of JS not having a module system, where we needed to deliver code in a way that required a single <script> element instead of tens or even hundreds of script tags that all had to load in a specific order - an order that could not be guaranteed so needed additional code to manage execution dependencies. Bundling solved that problem at a terrible price: it broke browser caching. Your well organized code, consisting of individual files for each role, was now a single megabyt-plus monster file and if you needed to change even a single letter, you'd invalidate the cache entry for that entire bundle. Chunking tried to solve that, but was a classic "deal with the symptom instead of solving the problem" solution. The real solution landed quite a while ago now: JS has a module system called ESM, and every single browser on the market supports it. Bundling has not been necessary since the summer of 2022, when Microsoft finally killed off the Internet Explorer line of browsers, removing the last reason people still had for using things like Browserify or Webpack (and really, it hasn't been necessary since a few years prior, because plenty of sites had the luxury of just not bothering to support IE, because even Microsoft was vocal about the fact that IE was going to get killed off because it was hopelessly out of date for years prior).

This is something you should give serious thought: bundling is a legacy practice, and if your codebase permits it: stop.

Second, consider switching to esbuild, which is remarkably straight forward to switch to even with complex webpack configurations, and puts your project on a modern and both (much) faster and better documented bundler instead. JS tooling like formatters, bundlers, and minifiers should not themselves be written in JS.

However, if you're absolutely stuck with webpack, be on the latest version of Webpack and tell it to ignore fs and zlib.

// webpack.config.js
module.exports = {
    ...
    resolve: {
        fallback: {
            "fs": false,
            "zlib": false
        },
    }
}

These modules are not loaded "up front", they are dynamic imports and if you're running in the browser they will never trigger. Simply make webpack ignore them, because the browser will, too.

Alright, what if I have opinions?

Drop me a message! @TheRealPomax should do nicely, but if you want to have an in-depth discussion, I'd recommend filing an issue, since 280 characters per message is not really sufficient to dig into OpenType details.

And if I just want to use this?

This code is MIT licensed, do whatever you want with it.

More Repositories

1

BezierInfo-2

The development repo for the Primer on Bézier curves, https://pomax.github.io/bezierinfo
HTML
2,264
star
2

react-onclickoutside

An onClickOutside wrapper for React components
JavaScript
1,818
star
3

bezierjs

A nodejs and client-side library for (cubic) Bezier curve work
JavaScript
1,650
star
4

bezierinfo

A Primer on Bezier Curves
HTML
733
star
5

nrGrammar

The Nihongo Resources grammar book: "An Introduction to Japanese; Syntax, Grammar & Language"
TeX
327
star
6

node-flickrapi

A node.js (and client-library) implementation of the Flickr API with oauth API key authentication and API method proxying
JavaScript
177
star
7

Pjs-2D-Game-Engine

Processing.js 2D Game Engine
JavaScript
83
star
8

mahjong

Proper mahjong. In Javascript. Because I love this "game".
JavaScript
75
star
9

js-svg-path

A parser that turns SVG path strings into a JS object you can mess with.
JavaScript
62
star
10

music-theory-js

For when you need to do music theory things. In javascript. Don't look at me you know what you need this for.
JavaScript
60
star
11

fontmetrics.js

A JavaScript library that extends the Canvas2D context.measureText(<String>) with additional typographical metrics
JavaScript
56
star
12

svg-path-reverse

This is a JavaScript SVG path to "something" converter, turning a path into hookable graphics instructions for arbitrary conversion
JavaScript
56
star
13

CFF-glyphlet-fonts

Making tiny fonts, because making tiny fonts.
JavaScript
50
star
14

react-component-visibility

For when your components need to know whether users can see them or not
JavaScript
47
star
15

PHP-Font-Parser

This is a PHP code library for parsing TTF/OTF fonts from file.
PHP
45
star
16

A-binary-parser-generator

This project aims to create a tool that can turn a spec file into a parser skeleton for binary data files such as OpenType fonts, PNG images, etc.
JavaScript
38
star
17

nihongoresources.com

A repository of nihongoresources.com related materials.
CSS
37
star
18

Usered

A minimal PHP user authentication system
PHP
36
star
19

midi-with-node

Turn your browser into a MIDI device through the magic of Nodejs, easymidi, and socket.io
JavaScript
31
star
20

photoshop-paths-to-SVG

This is a photoshop "jsx" script that turns all paths in the active document into a single SVG file for use in something else.
JavaScript
30
star
21

node-jp-conjugations

A Japanese verb conjugator and unconjugator
JavaScript
30
star
22

arduino-midi-recorder

Let's build an Arduino-based MIDI recorder!
C++
26
star
23

react-positionable-component

A <Positionable> React component to turn arbitrary content into easy to CSS transform content
JavaScript
22
star
24

socketless

A framework and methodology for writing web socket RPC programs, without writing a single line of web socket or RPC code.
JavaScript
20
star
25

flickrmirror

Mirror your Flickr account to your HD. Then write a new site based on it.
JavaScript
17
star
26

the-cff-table

A markdown conversion of the Adobe Tech notes 5176 and 5177 as a stub for "what the OpenType chapter on the CFF table should look like", for specifically-for-OpenType-only CFF blocks.
17
star
27

RGBAnalyse

An RGB-HC histogram library for same-origin images
JavaScript
16
star
28

EasyPageComments

A simple instant-comments-section for pages based on PHP at the server, and _optionally_ JavaScript at the front end
PHP
16
star
29

LUT-builder

A web based LUT builder for OBS 64x64x64 LUTs
JavaScript
15
star
30

custom-graphics-element

Because what if you could just... write graphics sketches? On the web? Like, directly?
JavaScript
15
star
31

lightroom-catalog-helper

A lightroom catalogue helper utility
JavaScript
15
star
32

node-jp-conversion

Japenese input conversion, turning any string into {kanji, katakana, hiragana, romaji} where possible.
JavaScript
15
star
33

pomax.github.io

My blog, hosted via github
Mathematica
14
star
34

react-formbuilder

A relatively easy to use form builder (single or multipage), with error reporting and lots of room for customization
JavaScript
14
star
35

ucharclasses

A XeLaTeX package that lets you insert arbitrary code between characters from different unicode blocks
TeX
14
star
36

WebGLdraw

LDraw .dat to .obj converter for rendering LEGO using WebGL
JavaScript
13
star
37

msfs-simconnect-api-wrapper

A JavaScripty wrapper around node-simconnect
JavaScript
13
star
38

Minimal-font-generator

This bit of JavaScript generates fonts that implement a single character, as a zero-width glyph.
JavaScript
13
star
39

css-node-graph

A React based, pure CSS graphing thing (note: just graphs. Not charts or plots)
JavaScript
11
star
40

pocketknife

A small JS tookit -- like jQuery, but smaller, not backward compatible, and without the powerhouse functions.
JavaScript
10
star
41

terminal-menu-program

Terminal menu programs, using retro ansi terminal menus for serious 80s technicolor business
JavaScript
10
star
42

filebrowser_s3

an AWS S3 fix for Mezzanine's media manager
Python
9
star
43

tex2utf

A UTF8 massaging of the `tex2mail` utility, found on https://ctan.org/pkg/tex2mail
Perl
9
star
44

CSSmacros

Start writing CSS with macros ("constants" or "set-once variables"), because sometimes classes and/or just elements doesn't cut it
JavaScript
8
star
45

KeyTrap.js

Trap keyboard events in HTML elements to prevent default browser actions (backspace, cursor, etc)
JavaScript
7
star
46

are-we-flying

Writing auto-pilots in JavaScript for MSFS
JavaScript
7
star
47

cff-opcode-fonts

OTF fonts for testing CFF opcode support
7
star
48

webpack-block-loader

A webpack loader for doing generic block-processing. You provide the delimiters and processor, block-loader will find your data and pass it through your processor.
JavaScript
7
star
49

mini-daw

A web based mini-DAW implementation (using the Web Audio and Web MIDI APIs)
JavaScript
7
star
50

Mike-s-Mahjong

A Java implementation of Mahjong (the real game, not the solitaire nonsense)
Java
6
star
51

arctic-redpoll

A JavaScript Tree library
JavaScript
6
star
52

three-point-perspective

Implementing strict three point perspective is ridiculous. You'll never need it. Let's do it anyway.
6
star
53

quick-glyph-maker

A quick glyph maker that I need for writing a blog post on fonts
JavaScript
5
star
54

pagemixer

Like an in-browser dev tools for normal people to play with
JavaScript
5
star
55

FlickrFindr

a javascript flickr search module
JavaScript
5
star
56

Inkcyclopedia

A simple inkcyclopedia based on (at least) 228 ink sample tests
JavaScript
5
star
57

inkdb.org

React overhaul of inkcyclopedia.org
JavaScript
5
star
58

use-models-for-data

Work with your data through well-defined, always-consistent models, instead of rando plain JS objects
JavaScript
4
star
59

processing-bezier-master

Master repo for a generic Processing sketch for Bezier curve work.
Processing
4
star
60

inkdb-data

Open Source Fountain Pen Ink Data
JavaScript
4
star
61

ReactRoutedExample

An example app using React and React Router 2, with webpack bundling, watching, and server-side page generation.
JavaScript
4
star
62

node-type2-charstring

A simple converter for Type2 charstrings in human readable and byte form
JavaScript
4
star
63

gh-weblog

A simple weblog that runs entirely off of gh-pages
CSS
3
star
64

console-plot

Add plotting to the browser console, because data viz is life.
JavaScript
3
star
65

simple-cff-builder

A simple CFF builder with global subroutines and human readable charstring input for generating test fonts.
JavaScript
3
star
66

walking-bass

A walking bass generator.
JavaScript
3
star
67

node-kanji-relations

A node.js module for getting parent/child grapheme relations for Japanese kanji
JavaScript
3
star
68

mjscoring.js

html5 mahjong scoring app (work in progress)
JavaScript
3
star
69

react-circletree

A circle tree component for React applications
JavaScript
3
star
70

reddit-image-catch-up

Catch up on all your favourite image subreddits with one call.
JavaScript
3
star
71

node-jp-giongo

A Japanese onomatopoeia and mimesis module for node.js
JavaScript
3
star
72

mathparser

A math parser for Processing - give it a string, it gives back an evaluator
JavaScript
2
star
73

processing-stubs

Code that I constantly end up writing when using Processing
Processing
2
star
74

socketless-demo

Demos for the socketless library, https://github.com/Pomax/socketless
JavaScript
2
star
75

nrAPI

nihongoresources REST API
JavaScript
2
star
76

canonical-cubic-curve

Canonical cubic curve analysis based on the 1989 paper "A Geometric Characterization of Parametric Cubic Curves"
Processing
2
star
77

php-MJ-Scoring

MJ scoring JS/PHP script/page thingy
PHP
1
star
78

mox-server

A tiny express server that will host mox (emulated S3) data as text/html content
JavaScript
1
star
79

Sheet-slicer

A sheet slicer using react and tiny REST API server
JavaScript
1
star
80

JSON-object-traverse

JS "mix in" to add string based route traversal to a JS object
JavaScript
1
star
81

node-rtltr-for-less

An automatic rtl/ltr extension for less-middleware
JavaScript
1
star
82

NRAPI-React

A React UI implementation for api.nihongoresources.com
JavaScript
1
star
83

Twitter-Personal-Feed-Curator

For bulk deleting about a thousand tweets at a time.
JavaScript
1
star
84

noodle-doodle

What I'd like to see in a piano roll/sequencer when it comes to keyboard usability
JavaScript
1
star
85

Bookstyle

Making text files look pretty and paginated for your reading pleasure. In HTML
1
star
86

Cursive-swash-typeface

I'm desiging a cursive, swashed typeface, and will try to implement it as OTF. We'll see how that goes
1
star
87

Online-stack-validator

This project offers an online stack validator for programming code, analysing matched pairs
JavaScript
1
star