Font.js

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

  • 所有者: Pomax/lib-font
  • 平台:
  • 许可证: MIT License
  • 分类:
  • 主题:
  • 喜欢:
    0
      比较:

Github星跟踪图

Font.js - lifting the hood on your fonts

If you're looking for the (really) old version of Font.js, you should be able to find it here.

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:

// 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
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 that case, you'll need to use fromDataBuffer instead of loadFont, and pass the original filename explicity so that the type of font can be determined from the extension:

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 => {
        // ...
    };
};

API

The API is currently lacking documentation. 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.

Building this code

While the whole point of ES modules is that you don't need to bundle anything because both the browser and node can be told to load the main file, and dependencies are automatically resolved, there is a dist build task that can be run using npm run build that builds a rolled up version of the library as a single file.

This is equivalent to running the following command:

$ npx rollup --no-treeshake --format=esm Font.js > dist/font.js

Note that this does not include the inflate and unbrotli libraries from the ./lib directory: as optional dependencies, they're intentionally left out when you roll up the code. Without them, plain opentype parsing will still work perfectly fine, but woff and woff2 parsing obviously won't.

Also note that this is not minified code: gzip is already pretty great at making things small, and if you need things even smaller than that, your project presumably has its own minification task(s) in place.

Testing

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

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: v14 and up.

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 that 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.

Alright, what if I have opinions?

Tweet at me! @TheRealPomax or @TheRealPomax@Mastodon.social 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.

主要指标

概览
名称与所有者Pomax/lib-font
主编程语言JavaScript
编程语言JavaScript (语言数: 2)
平台
许可证MIT License
所有者活动
创建于2012-01-21 22:13:39
推送于2024-07-04 04:33:16
最后一次提交2024-07-03 21:33:11
发布数30
最新版本名称v2.4.3 (发布于 2024-07-03 21:33:11)
第一版名称v2015 (发布于 2019-11-15 12:42:48)
用户参与
星数738
关注者数26
派生数72
提交数245
已启用问题?
问题数101
打开的问题数26
拉请求数30
打开的拉请求数0
关闭的拉请求数7
项目设置
已启用Wiki?
已存档?
是复刻?
已锁定?
是镜像?
是私有?