{"id":13194,"library":"fontkit","title":"Fontkit Advanced Font Engine","description":"Fontkit is an advanced, cross-platform font engine designed for both Node.js and browser environments, currently stable at version 2.0.4. It offers comprehensive support for numerous font formats, including TrueType, OpenType, WOFF, WOFF2, and TrueType Collections. Its core capabilities encompass accurate character-to-glyph mapping, supporting advanced substitution features like ligatures, alongside robust glyph metrics and layout functionalities such as kerning. The library deeply integrates with OpenType (GSUB, GPOS) and Apple Advanced Typography (AAT) features, providing tools for extracting glyph vector paths for SVG conversion, handling color emoji glyphs (via SBIX and COLR tables), and supporting AAT variation glyphs for dynamic design control. A significant differentiator is its efficient font subsetting mechanism, which enables developers to create optimized font files containing only the necessary glyphs. The 2.0.0 release marked a substantial modernization, migrating from Node.js-specific `Buffer` usage to `Uint8Array` and `TextDecoder`, significantly enhancing its browser compatibility and changing the subset encoding API from streaming to a direct `Uint8Array` return. It is notably used as a core component within the PDFKit library.","status":"active","version":"2.0.4","language":"javascript","source_language":"en","source_url":"git://github.com/foliojs/fontkit","tags":["javascript","opentype","font","typography","subset","emoji","glyph","layout"],"install":[{"cmd":"npm install fontkit","lang":"bash","label":"npm"},{"cmd":"yarn add fontkit","lang":"bash","label":"yarn"},{"cmd":"pnpm add fontkit","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"ESM import is preferred in modern Node.js and browser environments. The CommonJS `require` syntax is still supported for older Node.js projects but is less common in new development.","wrong":"const fontkit = require('fontkit');","symbol":"fontkit","correct":"import fontkit from 'fontkit';"},{"note":"`openSync` is a method on the default `fontkit` export, not a named export directly from the package. It's used for synchronous font loading, primarily in Node.js environments.","wrong":"import { openSync } from 'fontkit';","symbol":"fontkit.openSync","correct":"import fontkit from 'fontkit'; const font = fontkit.openSync('path/to/font.ttf');"},{"note":"`create` is the primary method for loading fonts from a `Uint8Array` buffer, suitable for both Node.js (e.g., after `fs.readFile`) and browser environments (e.g., after `fetch().arrayBuffer()`).","symbol":"fontkit.create","correct":"import fontkit from 'fontkit'; const buffer = new Uint8Array(...); const font = fontkit.create(buffer);"}],"quickstart":{"code":"import fontkit from 'fontkit';\nimport { readFileSync, writeFileSync } from 'fs';\nimport path from 'path';\n\n// Ensure a font file exists for this example, e.g., 'NotoSans-Regular.ttf'\n// You can download one from Google Fonts for testing purposes.\nconst fontFilePath = path.join(process.cwd(), 'NotoSans-Regular.ttf');\n\ntry {\n  // Open a font synchronously (Node.js environment).\n  // In a browser, you would typically fetch an ArrayBuffer and use fontkit.create(arrayBuffer).\n  const font = fontkit.openSync(fontFilePath);\n\n  console.log(`Opened font: ${font.fullName} (v${font.version})`);\n\n  // Layout a string to get glyphs and their positions.\n  const run = font.layout('Hello, fontkit!');\n  console.log(`Glyphs in \"Hello, fontkit!\": ${run.glyphs.length}`);\n\n  // Extract an SVG path for the first glyph.\n  if (run.glyphs.length > 0) {\n    const firstGlyphSVG = run.glyphs[0].path.toSVG();\n    console.log(`SVG path for first glyph (truncated): ${firstGlyphSVG.substring(0, 50)}...`);\n  }\n\n  // Create a font subset containing only the glyphs used in the laid-out string.\n  const subset = font.createSubset();\n  run.glyphs.forEach(glyph => {\n    subset.includeGlyph(glyph);\n  });\n\n  // Encode the subset into a Uint8Array.\n  const subsetBuffer = subset.encode();\n  console.log(`Created subset font with ${subset.glyphs.length} glyphs, size: ${subsetBuffer.byteLength} bytes.`);\n\n  // Optionally, save the subset to a new file.\n  const subsetFilePath = path.join(process.cwd(), 'subset-font.ttf');\n  writeFileSync(subsetFilePath, subsetBuffer);\n  console.log(`Subset font saved to ${subsetFilePath}`);\n\n} catch (error) {\n  console.error(`An error occurred: ${error.message}`);\n  console.warn(\"Please ensure a font file (e.g., 'NotoSans-Regular.ttf') exists in your current directory for this example to run.\");\n}","lang":"javascript","description":"Demonstrates opening a font, laying out text, extracting SVG paths for individual glyphs, and creating/encoding a font subset, saving it to a new file."},"warnings":[{"fix":"Migrate any direct `Buffer` manipulations to use `Uint8Array` and `TextEncoder`/`TextDecoder` as appropriate for text encoding/decoding.","message":"Version 2.0.0 replaced all internal usages of Node.js `Buffer` with `Uint8Array`/`TextEncoder`/`TextDecoder` for improved browser compatibility. Code directly interacting with internal `Buffer` instances will break.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Replace calls to `subset.encodeStream()` with `subset.encode()`. Handle the returned `Uint8Array` directly instead of piping it through a stream.","message":"The `subset.encodeStream()` method was removed in version 2.0.0. Subset encoding is no longer stream-based; `subset.encode()` now directly returns a `Uint8Array`.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Remove `iconv-lite` from your project dependencies if it was only used for `fontkit`. `fontkit` now handles encodings internally using standard Web APIs.","message":"The optional dependency `iconv-lite` has been dropped in version 2.0.0. `TextDecoder` is now used for character encoding.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"In browser environments, use `fontkit.create(buffer)` with a `Uint8Array` obtained from fetching font data (e.g., `fetch().arrayBuffer()`). Ensure your build process (e.g., Webpack, Rollup) is configured to use the browser-specific build of `fontkit` if it's not automatically picked up.","message":"While fontkit supports both Node.js and browser environments, the Node.js build includes `fs` (file system) dependencies for `open` and `openSync` methods. Using these methods directly in a browser context will result in errors.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure you are using the browser build of `fontkit` if in the browser. For Node.js, ensure your environment correctly defines `Buffer` or update your code to use `Uint8Array` where `Buffer` was previously expected, especially if migrating from pre-v2 `fontkit` versions.","cause":"Attempting to use `fontkit` v2.x or later in a browser environment or a Node.js context where `Buffer` is not globally available (e.g., an ESM module in some older configurations) and `fontkit` expects it for legacy interop, or custom code using `Buffer` for operations `fontkit` used to handle.","error":"ReferenceError: Buffer is not defined"},{"fix":"Replace `subset.encodeStream()` with `subset.encode()`. The new method returns a `Uint8Array` directly, so adjust your subsequent code to handle a `Uint8Array` instead of a stream.","cause":"This error occurs when migrating from `fontkit` v1.x to v2.x. The `encodeStream` method was removed in v2.0.0 in favor of a direct `encode` method.","error":"TypeError: subset.encodeStream is not a function"},{"fix":"In browser environments, you must load font data as an `ArrayBuffer` (e.g., via `fetch`) and then use `fontkit.create(new Uint8Array(arrayBuffer))` to instantiate the font object.","cause":"Attempting to use `fontkit.open()` or `fontkit.openSync()` in a browser environment, which are Node.js-specific methods relying on the `fs` module.","error":"Error: Cannot find module 'fs' or similar file system errors in browser"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":null,"cli_name":""}