Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MODIFICATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
- Also removing the `brotli` dependency
- Remove DFont format support
- Simplify the published TypeScript types by inlining the concrete format exports (`TTFFont`/`TrueTypeCollection`) in place of the old aliases (`Font`/`FontCollection`).
- Remove the dependencies below by replacing them with new internal helpers (no API changes):
- `clone` (used by `TTFSubset`)
- `fast-deep-equal` (used by `CFFDict`)

## [2.0.4-mod.2025.2]

Expand Down
17 changes: 6 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
],
"scripts": {
"test": "run-s build mocha",
"mocha": "mocha",
"mocha": "mocha \"test/**/*.js\"",
"build": "run-s build:*",
"build:js": "parcel build",
"build:types": "tsc --project tsconfig-types.json",
Expand All @@ -22,7 +22,7 @@
"trie:use": "node src/opentype/shapers/gen-use.js",
"trie:indic": "node src/opentype/shapers/gen-indic.js",
"clean": "shx rm -rf src/opentype/shapers/data.trie src/opentype/shapers/use.trie src/opentype/shapers/use.json src/opentype/shapers/indic.trie src/opentype/shapers/indic.json dist types",
"coverage": "c8 mocha"
"coverage": "c8 mocha \"test/**/*.js\""
},
"type": "module",
"main": "dist/main.cjs",
Expand Down Expand Up @@ -83,9 +83,7 @@
},
"dependencies": {
"@swc/helpers": "^0.5.12",
"clone": "^2.1.2",
"dfa": "^1.2.0",
"fast-deep-equal": "^3.1.3",
"restructure": "^3.0.0",
"unicode-properties": "^1.4.0",
"unicode-trie": "^2.0.0"
Expand All @@ -99,6 +97,6 @@
"npm-run-all": "^4.1.5",
"parcel": "2.0.0-canary.1713",
"shx": "^0.3.4",
"typescript": "^5.7.2"
"typescript": "^5.9.3"
}
}
3 changes: 1 addition & 2 deletions src/CmapProcessor.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { binarySearch } from './utils';
import { binarySearch, range } from './utils/arrays';
import { encodingExists, getEncoding, getEncodingMapping } from './encodings';
import { cache } from './decorators';
import { range } from './utils';

export default class CmapProcessor {
constructor(cmapTable) {
Expand Down
2 changes: 1 addition & 1 deletion src/TTFFont.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import GlyphVariationProcessor from './glyph/GlyphVariationProcessor';
import TTFSubset from './subset/TTFSubset';
import CFFSubset from './subset/CFFSubset';
import BBox from './glyph/BBox';
import { asciiDecoder } from './utils';
import { asciiDecoder } from './utils/decode';

/**
* This is the base class for all SFNT-based font formats in fontkit.
Expand Down
2 changes: 1 addition & 1 deletion src/TrueTypeCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as r from 'restructure';
import TTFFont from './TTFFont';
import Directory from './tables/directory';
import tables from './tables';
import { asciiDecoder } from './utils';
import { asciiDecoder } from './utils/decode';

let TTCHeader = new r.VersionedStruct(r.uint32, {
0x00010000: {
Expand Down
2 changes: 1 addition & 1 deletion src/aat/AATLookupTable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {cache} from '../decorators';
import {range} from '../utils';
import {range} from '../utils/arrays';

export default class AATLookupTable {
constructor(table) {
Expand Down
7 changes: 3 additions & 4 deletions src/cff/CFFDict.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import isEqual from 'fast-deep-equal';
import * as r from 'restructure';
import CFFOperand from './CFFOperand';
import { PropertyDescriptor } from 'restructure';
import { equalArray } from '../utils/deep-equal';

export default class CFFDict {
constructor(ops = []) {
Expand Down Expand Up @@ -108,7 +107,7 @@ export default class CFFDict {
for (let k in this.fields) {
let field = this.fields[k];
let val = dict[field[1]];
if (val == null || isEqual(val, field[3])) {
if (val == null || equalArray(val, field[3])) {
continue;
}

Expand Down Expand Up @@ -141,7 +140,7 @@ export default class CFFDict {

for (let field of this.ops) {
let val = dict[field[1]];
if (val == null || isEqual(val, field[3])) {
if (val == null || equalArray(val, field[3])) {
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion src/layout/KernProcessor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @ts-check

import { binarySearch } from '../utils';
import { binarySearch } from '../utils/arrays';

export default class KernProcessor {
/**
Expand Down
2 changes: 1 addition & 1 deletion src/opentype/shapers/ArabicShaper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import DefaultShaper from './DefaultShaper';
import {getCategory} from 'unicode-properties';
import UnicodeTrie from 'unicode-trie';
import { decodeBase64 } from '../../utils';
import { decodeBase64 } from '../../utils/decode';

const trie = new UnicodeTrie(decodeBase64(require('fs').readFileSync(__dirname + '/data.trie', 'base64')));
const FEATURES = ['isol', 'fina', 'fin2', 'fin3', 'medi', 'med2', 'init'];
Expand Down
2 changes: 1 addition & 1 deletion src/opentype/shapers/IndicShaper.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
HALANT_OR_COENG_FLAGS, INDIC_CONFIGS,
INDIC_DECOMPOSITIONS
} from './indic-data';
import { decodeBase64 } from '../../utils';
import { decodeBase64 } from '../../utils/decode';

const {decompositions} = useData;
const trie = new UnicodeTrie(decodeBase64(require('fs').readFileSync(__dirname + '/indic.trie', 'base64')));
Expand Down
2 changes: 1 addition & 1 deletion src/opentype/shapers/UniversalShaper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import StateMachine from 'dfa';
import UnicodeTrie from 'unicode-trie';
import GlyphInfo from '../GlyphInfo';
import useData from './use.json';
import { decodeBase64 } from '../../utils';
import { decodeBase64 } from '../../utils/decode';

const {categories, decompositions} = useData;
const trie = new UnicodeTrie(decodeBase64(require('fs').readFileSync(__dirname + '/use.trie', 'base64')));
Expand Down
2 changes: 1 addition & 1 deletion src/subset/TTFSubset.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import cloneDeep from 'clone';
import { cloneDeep } from '../utils/clone';
import Subset from './Subset';
import Directory from '../tables/directory';
import Tables from '../tables';
Expand Down
26 changes: 26 additions & 0 deletions src/utils/arrays.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export function binarySearch(arr, cmp) {
let min = 0;
let max = arr.length - 1;
while (min <= max) {
let mid = (min + max) >> 1;
let res = cmp(arr[mid]);

if (res < 0) {
max = mid - 1;
} else if (res > 0) {
min = mid + 1;
} else {
return mid;
}
}

return -1;
}

export function range(index, end) {
let range = [];
while (index < end) {
range.push(index++);
}
return range;
}
114 changes: 114 additions & 0 deletions src/utils/clone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// @ts-check

import { isPrimitive } from './primitive.js';

/**
* Deeply clones the value.
*
* - Supports primitives, arrays, and plain objects.
* - Handles circular references by tracking already-cloned objects.
* - Preserves object prototypes (Object.prototype or null).
* - Clones only enumerable properties (both string keys and symbols).
* - Does not support functions, class instances, or built-in objects
* (Date, RegExp, Map, Set, Buffer, etc.).
*
* @template T
* @param {T} value - The value to clone.
* @returns {T} A deep clone of the value.
* @throws {TypeError} If `value` contains unsupported types.
*/
export function cloneDeep(value) {
return cloneValue(value, new Map());
}

/**
* Recursively clones a value, tracking seen objects to handle circular references.
*
* @param {unknown} value
* @param {Map<object, any>} seen
* @returns {any}
*/
function cloneValue(value, seen) {
if (isPrimitive(value)) {
return value;
}

if (Array.isArray(value)) {
return cloneArray(value, seen);
}

if (isCloneableObject(value)) {
return cloneObject(value, seen);
}

throw new TypeError('cloneDeep only supports primitives, arrays, and plain objects');
}

/**
* Checks if a value is a plain object (prototype is Object.prototype or null).
*
* @param {unknown} value
* @returns {value is Record<string | symbol, any>}
*/
function isCloneableObject(value) {
if (value === null || typeof value !== 'object') {
return false;
}

const proto = Object.getPrototypeOf(value);
return proto === Object.prototype || proto === null;
}

/**
* Clones an array and recursively clones all its elements.
*
* @param {any[]} value
* @param {Map<object, any>} seen
* @returns {any[]}
*/
function cloneArray(value, seen) {
if (seen.has(value)) {
return seen.get(value);
}

const result = new Array(value.length);
seen.set(value, result);

for (let i = 0; i < value.length; i++) {
result[i] = cloneValue(value[i], seen);
}

return result;
}

/**
* Clones a plain object, preserving its prototype and
* recursively cloning all properties and enumerable symbols.
*
* @param {Record<string | symbol, any>} value
* @param {Map<object, any>} seen
* @returns {Record<string | symbol, any>}
*/
function cloneObject(value, seen) {
if (seen.has(value)) {
return seen.get(value);
}

const result = Object.create(Object.getPrototypeOf(value));
seen.set(value, result);

for (const key of Object.keys(value)) {
result[key] = cloneValue(value[key], seen);
}

const symbols = Object.getOwnPropertySymbols(value);
for (const symbol of symbols) {
const descriptor = Object.getOwnPropertyDescriptor(value, symbol);
if (descriptor && !descriptor.enumerable) {
continue;
}
result[symbol] = cloneValue(value[symbol], seen);
}

return result;
}
27 changes: 0 additions & 27 deletions src/utils.js → src/utils/decode.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,3 @@
export function binarySearch(arr, cmp) {
let min = 0;
let max = arr.length - 1;
while (min <= max) {
let mid = (min + max) >> 1;
let res = cmp(arr[mid]);

if (res < 0) {
max = mid - 1;
} else if (res > 0) {
min = mid + 1;
} else {
return mid;
}
}

return -1;
}

export function range(index, end) {
let range = [];
while (index < end) {
range.push(index++);
}
return range;
}

export const asciiDecoder = new TextDecoder('ascii');

// Based on https://github.com/niklasvh/base64-arraybuffer. MIT license.
Expand Down
Loading