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?
已存檔?
是復刻?
已鎖定?
是鏡像?
是私有?