use-font-metrics - React hooks for font metrics
Features
- Measure text width, font baseline, and other font metrics, for Win or Typo metrics on node.js and in the browser
- Metrics extracted from font files using @pdf-lib/fontkit. Does not use the HTML5 Canvas API
Install
npm install use-font-metrics
Quickstart
Lets write a React component that measures the width of Hello World
rendered in Inter 16px
:
- Place a stylesheet referencing the font at
src/fonts.css
:@font-face { font-family: Inter; src: url(./fonts/Inter-Regular.otf); }
- Add the font file to your bundle - place the
otf
font file atsrc/fonts/Inter-Regular.otf
. You can download it from google fonts. You could also set the font source to some cloud font provider. Metrics will be available for every font found in a@font-face
rule. - Wrap your component with a
FontManager
context. Using the top-levelsrc/index.tsx
for example:import { withFontManager, FontProvider } from 'use-font-metrics'; import './fonts.css'; // @font-face fonts will be loaded withFontManager( fm => void ReactDOM.render( <FontProvider fontManager={fm}> <App /> </FontProvider> ), document.getElementById('root'), );
- With a font manager in context, any component under
<FontProvider>
can use the font metric hooks:
import { useHMetrics } from 'use-font-metrics';
export const MyComponent = () => {
const { width } = useHMetrics({
fontFamily: 'Inter',
fontSize: 16, // px
text: 'Hello World',
});
return <div>“Hello World” width: {width}px</div>;
}
Demos
- Quickstart - the Quickstart example source
-
Horizontal Metrics - measure text width and chop off
advanceWidth
source -
Vertical Metrics - display font
baseline
andxHeight
source -
Fit Text Height - Resize the browser window to see text
capHeight
sizing to DOM element height source
Motivation
Some HTML layout problems require font metrics. For example:
- Fit text to an area
- Align text horizontally despite the tiny
advanceLeft
/advanceRight
browsers add around glyphs - Resize multi-font text, required because equal
fontSize
does not imply equalcapHeight
There are hacks for doing this with no font metrics, and without hurting accuracy much, but there are also hacks that allow you to scratch your upper back with your foot. These are all highly sub-optimal, so W3C added text metrics to the Canvas API.
Support as of 2021 AD is still incomplete, and there are some other issues:
- Requires Canvas. Would be nice to get these directly from the font file. Should not cost anything because we are surely loading the font if we need its metrics
- API encapsulates away the distinction between the two sets of metrics browsers use, as explained here
- Sometime you need all the metrics
This is a stopgap until browsers do this.
How It Works
A tiny React/Typescript wrapper around this fork of the fontkit library, wired to load remote fonts, and pass their metrics to any inquiring hooks.
Usage
Loading Fonts
The hooks expect a FontContext
in scope, holding a FontManager
. The font manager caches loaded fonts.
The easiest way to load the fonts and provide the context is shown in the Quickstart above: add your fonts to a stylesheet, use withFontManager
to create a font manager, and <FontProvider>
to inject it into the context. For other use cases you may want to create your own font manager and explicitly set loaded fonts.
Node
In Node you can load local fonts:
import { FetchFont, FontManager, loadLocalFonts } from 'use-font-metrics';
const fonts: FetchFont[] = [
{
fontFamily: 'myfont',
src: 'path/to/font.ttf',
},
];
const fontManager: FontManager = loadLocalFonts(...fonts);
Browser
In the browser you can load remote fonts using loadFontsAsync
:
import { FontManager, loadFontsAsync } from 'use-font-metrics';
const fonts: FetchFont[] = [
{
fontFamily: 'myfont',
src: 'http://url/of/font.ttf',
},
];
const fontManager: Promise<FontManager> = loadFontsAsync(...fonts);
If your fonts are already defined in the @font-face
rules of some loaded stylesheet, you can create a font manager from all the rules of all loaded stylesheets using:
import { FontManager, loadStyleSheetFonts } from 'use-font-metrics';
const fontManager: FontManager = loadStyleSheetFonts();
Hooks
You can access the loaded fonts and their metrics directly from the font manager, as in this demo, or you can use one of the hooks:
-
useWinBaseline/useMacBaseline(fontFamily: string, fontSize: number, lineHeight: number = 1)
- distance between top of text line and font baseline -
useHMertics({fontFamily: string; fontSize: number, text: string})
- returns text horizontal measurement in a record with numericwidth
andtextIndent
keys in pixel -
useHMerticsStyle({fontFamily: string; fontSize: number; text: string;})
- same asuseHMetrics
but returns a CSS Style object that you can use for the element of the measured text