Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Connum authored Oct 4, 2023
2 parents 0428d1d + bcdd238 commit 8f3861d
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 61 deletions.
121 changes: 62 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# opentype.js
# opentype.js

JavaScript parser and writer for TrueType and OpenType fonts.

Expand Down Expand Up @@ -63,10 +63,11 @@ If you plan on improving or debugging opentype.js, you can:
- clone your fork `git clone git://github.com/yourname/opentype.js.git`
- move into the project `cd opentype.js`
- install needed dependencies with `npm install`
- **option A:** for a simple build use `npm run build`
- **option B:** for a development server use `npm run start` and navigate to the `/docs` folder
- check if all still work fine with `npm run test`
- commit and pull requesting your changes. Thanks you !
- make your changes
- **option A:** for a simple build, use `npm run build`
- **option B:** for a development server, use `npm run start` and navigate to the `/docs` folder
- check if all still works fine with `npm run test`
- commit and open a Pull Request with your changes. Thank you!

## Usage

Expand Down Expand Up @@ -94,7 +95,7 @@ buffer.then(data => {
### Loading a WOFF2 font

WOFF2 Brotli compression perform [29% better](https://www.w3.org/TR/WOFF20ER/#appendixB) than it WOFF predecessor.
But this compression is also more complex, and would result having a much heavier opentype.js library (~120KB => ~1400KB).
But this compression is also more complex, and would result in a much heavier (>10×!) opentype.js library (120KB => 1400KB).

To solve this: Decompress the font beforehand (for example with [fontello/wawoff2](https://github.com/fontello/wawoff2)).

Expand All @@ -106,7 +107,7 @@ const loadScript = (src) => new Promise((onload) => document.documentElement.app

const buffer = //...same as previous example...

// load wawoff2 if needed and wait (!) for it to be ready
// load wawoff2 if needed, and wait (!) for it to be ready
if (!window.Module) {
const path = 'https://unpkg.com/[email protected]/build/decompress_binding.js'
const init = new Promise((done) => window.Module = { onRuntimeInitialized: done});
Expand All @@ -118,7 +119,7 @@ const font = opentype.parse(Module.decompress(await buffer));

### Loading a font (1.x style)

This example rely on the deprecated `.load()` method
This example relies on the deprecated `.load()` method

```js
// case 1: from an URL
Expand All @@ -131,7 +132,7 @@ console.log(font.supported);
```

### Writing a font
Once you have a `Font` object (either by using `opentype.load` or by creating a new one from scratch) you can write it
Once you have a `Font` object (either by using `opentype.load()` or by creating a new one from scratch) you can write it
back out as a binary file.

In the browser, you can use `Font.download()` to instruct the browser to download a binary .OTF file. The name is based
Expand Down Expand Up @@ -176,39 +177,39 @@ If you want to get an `ArrayBuffer`, use `font.toArrayBuffer()`.
A Font represents a loaded OpenType font file. It contains a set of glyphs and methods to draw text on a drawing context, or to get a path representing the text.

* `glyphs`: an indexed list of Glyph objects.
* `unitsPerEm`: X/Y coordinates in fonts are stored as integers. This value determines the size of the grid. Common values are 2048 and 4096.
* `unitsPerEm`: X/Y coordinates in fonts are stored as integers. This value determines the size of the grid. Common values are `2048` and `4096`.
* `ascender`: Distance from baseline of highest ascender. In font units, not pixels.
* `descender`: Distance from baseline of lowest descender. In font units, not pixels.

#### `Font.getPath(text, x, y, fontSize, options)`
Create a Path that represents the given text.
* `x`: Horizontal position of the beginning of the text. (default: 0)
* `y`: Vertical position of the *baseline* of the text. (default: 0)
* `fontSize`: Size of the text in pixels (default: 72).
* `x`: Horizontal position of the beginning of the text. (default: `0`)
* `y`: Vertical position of the *baseline* of the text. (default: `0`)
* `fontSize`: Size of the text in pixels (default: `72`).

Options is an optional object containing:
* `kerning`: if true takes kerning information into account (default: true)
* `kerning`: if true takes kerning information into account (default: `true`)
* `features`: an object with [OpenType feature tags](https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags) as keys, and a boolean value to enable each feature.
Currently only ligature features "liga" and "rlig" are supported (default: true).
* `hinting`: if true uses TrueType font hinting if available (default: false).
Currently only ligature features `"liga"` and `"rlig"` are supported (default: `true`).
* `hinting`: if true uses TrueType font hinting if available (default: `false`).

_Note: there is also `Font.getPaths` with the same arguments which returns a list of Paths._
_**Note:** there is also `Font.getPaths()` with the same arguments, which returns a list of Paths._

#### `Font.draw(ctx, text, x, y, fontSize, options)`
Create a Path that represents the given text.
* `ctx`: A 2D drawing context, like Canvas.
* `x`: Horizontal position of the beginning of the text. (default: 0)
* `y`: Vertical position of the *baseline* of the text. (default: 0)
* `fontSize`: Size of the text in pixels (default: 72).
* `x`: Horizontal position of the beginning of the text. (default: `0`)
* `y`: Vertical position of the *baseline* of the text. (default: `0`)
* `fontSize`: Size of the text in pixels (default: `72`).

Options is an optional object containing:
* `kerning`: if true takes kerning information into account (default: true)
* `kerning`: if `true`, takes kerning information into account (default: `true`)
* `features`: an object with [OpenType feature tags](https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags) as keys, and a boolean value to enable each feature.
Currently only ligature features "liga" and "rlig" are supported (default: true).
* `hinting`: if true uses TrueType font hinting if available (default: false).
Currently only ligature features `"liga"` and `"rlig"` are supported (default: `true`).
* `hinting`: if true uses TrueType font hinting if available (default: `false`).

#### `Font.drawPoints(ctx, text, x, y, fontSize, options)`
Draw the points of all glyphs in the text. On-curve points will be drawn in blue, off-curve points will be drawn in red. The arguments are the same as `Font.draw`.
Draw the points of all glyphs in the text. On-curve points will be drawn in blue, off-curve points will be drawn in red. The arguments are the same as `Font.draw()`.

#### `Font.drawMetrics(ctx, text, x, y, fontSize, options)`
Draw lines indicating important font measurements for all glyphs in the text.
Expand All @@ -222,113 +223,115 @@ Note that there is no strict 1-to-1 correspondence between the string and glyph
possible substitutions such as ligatures. The list of returned glyphs can be larger or smaller than the length of the given string.

#### `Font.charToGlyph(char)`
Convert the character to a `Glyph` object. Returns null if the glyph could not be found. Note that this function assumes that there is a one-to-one mapping between the given character and a glyph; for complex scripts this might not be the case.
Convert the character to a Glyph object. Returns `null` if the glyph could not be found. Note that this function assumes that there is a one-to-one mapping between the given character and a glyph; for complex scripts, this might not be the case.

#### `Font.getKerningValue(leftGlyph, rightGlyph)`
Retrieve the value of the [kerning pair](https://en.wikipedia.org/wiki/Kerning) between the left glyph (or its index) and the right glyph (or its index). If no kerning pair is found, return 0. The kerning value gets added to the advance width when calculating the spacing between glyphs.
Retrieve the value of the [kerning pair](https://en.wikipedia.org/wiki/Kerning) between the left glyph (or its index) and the right glyph (or its index). If no kerning pair is found, return `0`. The kerning value gets added to the advance width when calculating the spacing between glyphs.

#### `Font.getAdvanceWidth(text, fontSize, options)`
Returns the advance width of a text.

This is something different than Path.getBoundingBox() as for example a
This is something different than `Path.getBoundingBox()`; for example a
suffixed whitespace increases the advancewidth but not the bounding box
or an overhanging letter like a calligraphic 'f' might have a quite larger
bounding box than its advance width.

This corresponds to canvas2dContext.measureText(text).width
* `fontSize`: Size of the text in pixels (default: 72).
* `options`: See Font.getPath
This corresponds to `canvas2dContext.measureText(text).width`
* `fontSize`: Size of the text in pixels (default: `72`).
* `options`: See `Font.getPath()`

#### The Glyph object
A Glyph is an individual mark that often corresponds to a character. Some glyphs, such as ligatures, are a combination of many characters. Glyphs are the basic building blocks of a font.

* `font`: A reference to the `Font` object.
* `name`: The glyph name (e.g. "Aring", "five")
* `font`: A reference to the Font object.
* `name`: The glyph name (e.g. `"Aring"`, `"five"`)
* `unicode`: The primary unicode value of this glyph (can be `undefined`).
* `unicodes`: The list of unicode values for this glyph (most of the time this will be 1, can also be empty).
* `unicodes`: The list of unicode values for this glyph (most of the time this will be `1`, can also be empty).
* `index`: The index number of the glyph.
* `advanceWidth`: The width to advance the pen when drawing this glyph.
* `leftSideBearing`: The horizontal distance from the previous character to the origin (`0, 0`); a negative value indicates an overhang
* `xMin`, `yMin`, `xMax`, `yMax`: The bounding box of the glyph.
* `path`: The raw, unscaled path of the glyph.

##### `Glyph.getPath(x, y, fontSize)`
Get a scaled glyph Path object we can draw on a drawing context.
* `x`: Horizontal position of the glyph. (default: 0)
* `y`: Vertical position of the *baseline* of the glyph. (default: 0)
* `fontSize`: Font size in pixels (default: 72).
Get a scaled glyph Path object for use on a drawing context.
* `x`: Horizontal position of the glyph. (default: `0`)
* `y`: Vertical position of the *baseline* of the glyph. (default: `0`)
* `fontSize`: Font size in pixels (default: `72`).

##### `Glyph.getBoundingBox()`
Calculate the minimum bounding box for the unscaled path of the given glyph. Returns an `opentype.BoundingBox` object that contains x1/y1/x2/y2.
Calculate the minimum bounding box for the unscaled path of the given glyph. Returns an `opentype.BoundingBox` object that contains `x1`/`y1`/`x2`/`y2`.
If the glyph has no points (e.g. a space character), all coordinates will be zero.

##### `Glyph.draw(ctx, x, y, fontSize)`
Draw the glyph on the given context.
* `ctx`: The drawing context.
* `x`: Horizontal position of the glyph. (default: 0)
* `y`: Vertical position of the *baseline* of the glyph. (default: 0)
* `fontSize`: Font size, in pixels (default: 72).
* `x`: Horizontal position of the glyph. (default: `0`)
* `y`: Vertical position of the *baseline* of the glyph. (default: `0`)
* `fontSize`: Font size, in pixels (default: `72`).

##### `Glyph.drawPoints(ctx, x, y, fontSize)`
Draw the points of the glyph on the given context.
On-curve points will be drawn in blue, off-curve points will be drawn in red.
The arguments are the same as `Glyph.draw`.
The arguments are the same as `Glyph.draw()`.

##### `Glyph.drawMetrics(ctx, x, y, fontSize)`
Draw lines indicating important font measurements for all glyphs in the text.
Black lines indicate the origin of the coordinate system (point 0,0).
Blue lines indicate the glyph bounding box.
Green line indicates the advance width of the glyph.
The arguments are the same as `Glyph.draw`.
The arguments are the same as `Glyph.draw()`.

##### `Glyph.toPathData(options)`, `Glyph.toDOMElement(options)`, `Glyph.toSVG(options)`, `Glyph.fromSVG(pathData, options)`,
These are currently only wrapper functions for their counterparts on Path objects (see documentation there), but may be extended in the future to pass on Glyph data for automatic calculation.

### The Path object
Once you have a path through `Font.getPath` or `Glyph.getPath`, you can use it.
Once you have a path through `Font.getPath()` or `Glyph.getPath()`, you can use it.

* `commands`: The path commands. Each command is a dictionary containing a type and coordinates. See below for examples.
* `fill`: The fill color of the `Path`. Color is a string representing a [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). (default: 'black')
* `stroke`: The stroke color of the `Path`. Color is a string representing a [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). (default: `null`: the path will not be stroked)
* `strokeWidth`: The line thickness of the `Path`. (default: 1, but since the `stroke` is null no stroke will be drawn)
* `fill`: The fill color of the Path. Color is a string representing a [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). (default: `'black'`)
* `stroke`: The stroke color of the `Path`. Color is a string representing a [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). (default: `null`; the path will not be stroked)
* `strokeWidth`: The line thickness of the `Path`. (default: `1`, but if `stroke` is `null` no stroke will be drawn)

##### `Path.draw(ctx)`
Draw the path on the given 2D context. This uses the `fill`, `stroke` and `strokeWidth` properties of the `Path` object.
Draw the path on the given 2D context. This uses the `fill`, `stroke`, and `strokeWidth` properties of the Path object.
* `ctx`: The drawing context.

##### `Path.getBoundingBox()`
Calculate the minimum bounding box for the given path. Returns an `opentype.BoundingBox` object that contains x1/y1/x2/y2.
Calculate the minimum bounding box for the given path. Returns an `opentype.BoundingBox` object that contains `x1`/`y1`/`x2`/`y2`.
If the path is empty (e.g. a space character), all coordinates will be zero.

##### `Path.toPathData(options)`
Convert the Path to a string of path data instructions.
See https://www.w3.org/TR/SVG/paths.html#PathData
* `options`:
* `decimalPlaces`: The amount of decimal places for floating-point values. (default: 2)
* `optimize`: apply some optimizations to the path data, e.g. removing unnecessary/duplicate commands (true/false, default: true)
* `flipY`: whether to flip the Y axis of the path data, because SVG and font paths use inverted Y axes. (true: calculate from bounding box, false: disable; default: true)
* `decimalPlaces`: The amount of decimal places for floating-point values. (default: `2`)
* `optimize`: apply some optimizations to the path data, e.g. removing unnecessary/duplicate commands (true/false, default: `true`)
* `flipY`: whether to flip the Y axis of the path data, because SVG and font paths use inverted Y axes. (`true`: calculate from bounding box, `false`: disable; default: `true`)
* `flipYBase`: Base value for the base flipping calculation. You'll probably want to calculate this from the font's ascender and descender values. (default: automatically calculate from the path data's bounding box)


##### `Path.toSVG(options)`
Convert the path to a SVG <path> element, as a string.
* `options`: see Path.toPathData
Convert the path to an SVG `<path>` element, as a string.
* `options`: see `Path.toPathData()`

##### `Path.fromSVG(pathData, options)`
Retrieve path from SVG path data. Either overwriting the path data for an existing path
Retrieve path from SVG path data.

Either overwriting the path data for an existing path:
```js
const path = new Path();
path.fromSVG('M0 0');
```
or creating a new Path directly:
Or creating a new Path directly:
```js
const path = Path.fromSVG('M0 0');
```
* `pathData`: Either a string of SVG path commands, or (only in browser context) an `SVGPathElement`
* `options`:
* `decimalPlaces`, `optimize`, `flipY`, `flipYBase`: see Path.toPathData
* `scale`: scaling value applied to all command coordinates (default: 1)
* `x`/`y`: offset applied to all command coordinates on the x or y axis (default: 0)
* `decimalPlaces`, `optimize`, `flipY`, `flipYBase`: see `Path.toPathData()`
* `scale`: scaling value applied to all command coordinates (default: `1`)
* `x`/`y`: offset applied to all command coordinates on the x or y axis (default: `0`)

#### Path commands
* **Move To**: Move to a new position. This creates a new contour. Example: `{type: 'M', x: 100, y: 200}`
Expand Down
2 changes: 1 addition & 1 deletion src/glyph.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Glyph.prototype.bindConstructorValues = function(options) {
// These three values cannot be deferred for memory optimization:
this.name = options.name || null;
this.unicode = options.unicode;
this.unicodes = options.unicodes || options.unicode !== undefined ? [options.unicode] : [];
this.unicodes = options.unicodes || (options.unicode !== undefined ? [options.unicode] : []);

// But by binding these values only when necessary, we reduce can
// the memory requirements by almost 3% for larger fonts.
Expand Down
5 changes: 5 additions & 0 deletions src/opentype.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import _name from './tables/name.js';
import os2 from './tables/os2.js';
import post from './tables/post.js';
import meta from './tables/meta.js';
import gasp from './tables/gasp.js';
/**
* The opentype library.
* @namespace opentype
Expand Down Expand Up @@ -338,6 +339,10 @@ function parseBuffer(buffer, opt={}) {
case 'meta':
metaTableEntry = tableEntry;
break;
case 'gasp':
table = uncompressTable(data, tableEntry);
font.tables.gasp = gasp.parse(table.data, table.offset);
break;
}
}

Expand Down
46 changes: 46 additions & 0 deletions src/tables/gasp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// The `gasp` table contains global information about the font.
// https://learn.microsoft.com/de-de/typography/opentype/spec/gasp

import check from '../check.js';
import parse from '../parse.js';
import table from '../table.js';

//const GASP_SYMMETRIC_GRIDFIT = 0x0004
//const GASP_SYMMETRIC_SMOOTHING = 0x0008
//const GASP_DOGRAY = 0x0002
//const GASP_GRIDFIT = 0x0001

// Parse the `gasp` table
function parseGaspTable(data, start) {
const gasp = {};
const p = new parse.Parser(data, start);
gasp.version = p.parseUShort();
check.argument(gasp.version <= 0x0001, 'Unsupported gasp table version.');
gasp.numRanges = p.parseUShort();
gasp.gaspRanges = [];
for (let i = 0; i < gasp.numRanges; i++) {
gasp.gaspRanges[i] = {
rangeMaxPPEM: p.parseUShort(),
rangeGaspBehavior: p.parseUShort(),
};
}
return gasp;
}


function makeGaspTable(gasp) {
const result = new table.Table('gasp', [
{name: 'version', type: 'USHORT', value: 0x0001},
{name: 'numRanges', type: 'USHORT', value: gasp.numRanges},
]);

for (let i in gasp.numRanges) {
result.fields.push({name: 'rangeMaxPPEM', type: 'USHORT', value: gasp.numRanges[i].rangeMaxPPEM});
result.fields.push({name: 'rangeGaspBehavior', type: 'USHORT', value: gasp.numRanges[i].rangeGaspBehavior});
}

return result;
}

export default { parse: parseGaspTable, make: makeGaspTable };

6 changes: 6 additions & 0 deletions src/tables/sfnt.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import cpal from './cpal.js';
import fvar from './fvar.js';
import stat from './stat.js';
import avar from './avar.js';
import gasp from './gasp.js';

function log2(v) {
return Math.log(v) / Math.log(2) | 0;
Expand Down Expand Up @@ -327,6 +328,7 @@ function fontToSfntTable(font) {

// we have to handle fvar before name, because it may modify name IDs
const fvarTable = font.tables.fvar ? fvar.make(font.tables.fvar, font.names) : undefined;
const gaspTable = font.tables.gasp ? gasp.make(font.tables.gasp) : undefined;

const languageTags = [];
const nameTable = _name.make(names, languageTags);
Expand Down Expand Up @@ -380,6 +382,10 @@ function fontToSfntTable(font) {
tables.push(metaTable);
}

if (gaspTable) {
tables.push(gaspTable);
}

const sfntTable = makeSfntTable(tables);

// Compute the font's checkSum and store it in head.checkSumAdjustment.
Expand Down
Loading

0 comments on commit 8f3861d

Please sign in to comment.