{"_id":"@adam-rocska/font-anatomy","_rev":"4-9503e022abf5d19fdd5895cfccdc57d2","name":"@adam-rocska/font-anatomy","dist-tags":{"latest":"1.1.4"},"versions":{"1.0.0":{"name":"@adam-rocska/font-anatomy","version":"1.0.0","keywords":["font","anatomy","opentype","typography","typeface","fontface","font-family","typescript","ts","types","js","javascript","node","nodejs"],"author":{"url":"https://adam-rocska.github.io","name":"Ádám László Rocska"},"license":"MIT","_id":"@adam-rocska/font-anatomy@1.0.0","maintainers":[{"name":"adam-rocska","email":"rocska.adam@gmail.com"}],"homepage":"https://github.com/adam-rocska/font-anatomy-typescript#readme","bugs":{"url":"https://github.com/adam-rocska/font-anatomy-typescript/issues"},"bin":{"font-anatomy":"dist/bin/font-anatomy.js"},"dist":{"shasum":"6aef7326d72d1cb97f439b0111795ae24efd51ce","tarball":"https://registry.npmjs.org/@adam-rocska/font-anatomy/-/font-anatomy-1.0.0.tgz","fileCount":8,"integrity":"sha512-njLmk4iHnhuFeGf5xMvPw968kwrBMcBGcQbAqGdcxa6+4+kzStBWpcVedGJMrY3Yh79YzM5i57PeZGn5eujZdg==","signatures":[{"sig":"MEUCIBLXB8HageM8/E+R3TWwxztqgjefda6ljQsTNqNowhnsAiEA3LC3iuJ1ZjLvW8Uz/kWQzpGbeqrJUGl5VwL7OLxf7TI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":242203},"jest":{"transform":{"^.+\\.(t|j)sx?$":["@swc/jest"]},"testTimeout":1000,"moduleNameMapper":{"!(.*)":"<rootDir>/$1","@adam-rocska/font-anatomy":"<rootDir>/src/index.ts"},"moduleDirectories":["node_modules"],"testPathIgnorePatterns":["/node_modules/"]},"main":"./dist/cjs/index.js","_from":"file:adam-rocska-font-anatomy-1.0.0.tgz","types":"./dist/cjs/index.d.ts","module":"./dist/es/index.mjs","exports":{".":{"import":{"types":"./dist/es/index.d.mts","default":"./dist/es/index.mjs"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}}},"scripts":{"test":"jest --env node","build":"bunchee && chmod +x dist/bin/font-anatomy.js","check":"pnpm eslint && pnpm attw --pack","clean":"rimraf dist"},"_npmUser":{"name":"adam-rocska","email":"rocska.adam@gmail.com"},"_resolved":"/tmp/372f72789413e99328c0dca80406eee4/adam-rocska-font-anatomy-1.0.0.tgz","_integrity":"sha512-njLmk4iHnhuFeGf5xMvPw968kwrBMcBGcQbAqGdcxa6+4+kzStBWpcVedGJMrY3Yh79YzM5i57PeZGn5eujZdg==","repository":{"url":"git+https://github.com/adam-rocska/font-anatomy-typescript.git","type":"git"},"_npmVersion":"10.8.1","description":"A collection of tools for working with font anatomy in Typescript.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"@types/node":"^22.2.0","opentype.js":"^1.3.4","@adam-rocska/markdown-table":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"add":"^2.0.6","jest":"^29.7.0","pnpm":"^9.7.0","yargs":"^17.7.2","eslint":"^8.57.0","rimraf":"^6.0.1","bunchee":"^5.3.1","@swc/jest":"^0.2.36","type-fest":"^4.24.0","typescript":"^5.5.4","@types/jest":"^29.5.12","@types/yargs":"^17.0.33","@types/opentype.js":"^1.3.8","eslint-config-next":"14.2.5","@arethetypeswrong/cli":"^0.15.4"},"_npmOperationalInternal":{"tmp":"tmp/font-anatomy_1.0.0_1723247498436_0.8310428699864214","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"@adam-rocska/font-anatomy","version":"1.1.0","keywords":["font","anatomy","opentype","typography","typeface","fontface","font-family","typescript","ts","types","js","javascript","node","nodejs","font-metrics","font-tools","font-processing","font-format","font-rendering","font-utils","typography-tools","type-design","font-management","font-analysis"],"author":{"url":"https://adam-rocska.github.io","name":"Ádám László Rocska"},"license":"MIT","_id":"@adam-rocska/font-anatomy@1.1.0","maintainers":[{"name":"adam-rocska","email":"rocska.adam@gmail.com"}],"homepage":"https://github.com/adam-rocska/font-anatomy-typescript#readme","bugs":{"url":"https://github.com/adam-rocska/font-anatomy-typescript/issues"},"bin":{"font-anatomy":"dist/bin/font-anatomy.js"},"dist":{"shasum":"d0efd968a0ac39b2cde617f5fb44b589e621d8b5","tarball":"https://registry.npmjs.org/@adam-rocska/font-anatomy/-/font-anatomy-1.1.0.tgz","fileCount":8,"integrity":"sha512-7kV0+JcHCVx4PjQanesT6jSMr6TCg8pV0RSJvY+v03ukBeczzz0ajXU8tiD4njYRZJ4GnOmv6mjihI53yMdjqA==","signatures":[{"sig":"MEYCIQCyB2OaYKxc/yPbW2ZFeX7qzU8E2bOrWA+IbsqFeilkRQIhAJsDtndYVPlfdNx0kXO6bnS7Pg07fcRBdMV/JIfRb/z5","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":250501},"jest":{"transform":{"^.+\\.(t|j)sx?$":["@swc/jest"]},"testTimeout":1000,"moduleNameMapper":{"!(.*)":"<rootDir>/$1","@adam-rocska/font-anatomy":"<rootDir>/src/index.ts"},"moduleDirectories":["node_modules"],"testPathIgnorePatterns":["/node_modules/"]},"main":"./dist/cjs/index.js","_from":"file:adam-rocska-font-anatomy-1.1.0.tgz","types":"./dist/cjs/index.d.ts","module":"./dist/es/index.mjs","exports":{".":{"import":{"types":"./dist/es/index.d.mts","default":"./dist/es/index.mjs"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}}},"scripts":{"test":"jest --env node","build":"bunchee && chmod +x dist/bin/font-anatomy.js","check":"pnpm eslint && pnpm attw --pack","clean":"rimraf dist"},"_npmUser":{"name":"adam-rocska","email":"rocska.adam@gmail.com"},"_resolved":"/tmp/3f158256ec034961cc517ac9f5c4a1d1/adam-rocska-font-anatomy-1.1.0.tgz","_integrity":"sha512-7kV0+JcHCVx4PjQanesT6jSMr6TCg8pV0RSJvY+v03ukBeczzz0ajXU8tiD4njYRZJ4GnOmv6mjihI53yMdjqA==","repository":{"url":"git+https://github.com/adam-rocska/font-anatomy-typescript.git","type":"git"},"_npmVersion":"10.8.1","description":"A collection of tools for working with font anatomy in Typescript.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"wawoff2":"^2.0.1","@types/node":"^22.2.0","opentype.js":"^1.3.4","@adam-rocska/markdown-table":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"add":"^2.0.6","jest":"^29.7.0","pnpm":"^9.7.0","yargs":"^17.7.2","eslint":"^8.57.0","rimraf":"^6.0.1","bunchee":"^5.3.1","@swc/jest":"^0.2.36","type-fest":"^4.24.0","typescript":"^5.5.4","@types/jest":"^29.5.12","@types/yargs":"^17.0.33","@types/wawoff2":"^1.0.2","@types/opentype.js":"^1.3.8","eslint-config-next":"14.2.5","@arethetypeswrong/cli":"^0.15.4"},"_npmOperationalInternal":{"tmp":"tmp/font-anatomy_1.1.0_1723363786245_0.4006554645029172","host":"s3://npm-registry-packages"}},"1.1.1":{"name":"@adam-rocska/font-anatomy","version":"1.1.1","keywords":["font","anatomy","opentype","typography","typeface","fontface","font-family","typescript","ts","types","js","javascript","node","nodejs","font-metrics","font-tools","font-processing","font-format","font-rendering","font-utils","typography-tools","type-design","font-management","font-analysis"],"author":{"url":"https://adam-rocska.github.io","name":"Ádám László Rocska"},"license":"MIT","_id":"@adam-rocska/font-anatomy@1.1.1","maintainers":[{"name":"adam-rocska","email":"rocska.adam@gmail.com"}],"homepage":"https://github.com/adam-rocska/font-anatomy-typescript#readme","bugs":{"url":"https://github.com/adam-rocska/font-anatomy-typescript/issues"},"bin":{"font-anatomy":"dist/bin/font-anatomy.js"},"dist":{"shasum":"ec99e3d92d9e4312f7467733042fb1e16271605d","tarball":"https://registry.npmjs.org/@adam-rocska/font-anatomy/-/font-anatomy-1.1.1.tgz","fileCount":8,"integrity":"sha512-gLLqWsblIFi2Gyx2oYAgu0Et3WWuNysfqdfMP/oX5TGVmJeiSUM0F+ih1SbTHbbz0FfLAoYQzIaQuqJBhk+ImA==","signatures":[{"sig":"MEYCIQDdbOpWqVW0QuYPHy1DOozgldpcqVymyWCj1rxLjRBGnAIhAK0+W6MDzFehqXtAbdHDD1Re1PoqfQbfP4HoqsKlEvLn","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":250464},"jest":{"transform":{"^.+\\.(t|j)sx?$":["@swc/jest"]},"testTimeout":1000,"moduleNameMapper":{"!(.*)":"<rootDir>/$1","@adam-rocska/font-anatomy":"<rootDir>/src/index.ts"},"moduleDirectories":["node_modules"],"testPathIgnorePatterns":["/node_modules/"]},"main":"./dist/cjs/index.js","_from":"file:adam-rocska-font-anatomy-1.1.1.tgz","types":"./dist/cjs/index.d.ts","module":"./dist/es/index.mjs","exports":{".":{"import":{"types":"./dist/es/index.d.mts","default":"./dist/es/index.mjs"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}}},"scripts":{"test":"jest --env node","build":"bunchee && chmod +x dist/bin/font-anatomy.js","check":"pnpm eslint && pnpm attw --pack","clean":"rimraf dist"},"_npmUser":{"name":"adam-rocska","email":"rocska.adam@gmail.com"},"_resolved":"/tmp/ef452f8c74eec2f6237bdb8a055fa449/adam-rocska-font-anatomy-1.1.1.tgz","_integrity":"sha512-gLLqWsblIFi2Gyx2oYAgu0Et3WWuNysfqdfMP/oX5TGVmJeiSUM0F+ih1SbTHbbz0FfLAoYQzIaQuqJBhk+ImA==","repository":{"url":"git+https://github.com/adam-rocska/font-anatomy-typescript.git","type":"git"},"_npmVersion":"10.8.1","description":"A collection of tools for working with font anatomy in Typescript.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"wawoff2":"^2.0.1","opentype.js":"^1.3.4","@adam-rocska/markdown-table":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","pnpm":"^9.7.0","yargs":"^17.7.2","eslint":"^8.57.0","rimraf":"^6.0.1","bunchee":"^5.3.1","@swc/jest":"^0.2.36","type-fest":"^4.24.0","typescript":"^5.5.4","@types/jest":"^29.5.12","@types/node":"^22.2.0","@types/yargs":"^17.0.33","@types/wawoff2":"^1.0.2","@types/opentype.js":"^1.3.8","eslint-config-next":"14.2.5","@arethetypeswrong/cli":"^0.15.4"},"_npmOperationalInternal":{"tmp":"tmp/font-anatomy_1.1.1_1723454756950_0.8095010117733563","host":"s3://npm-registry-packages"}},"1.1.4":{"name":"@adam-rocska/font-anatomy","version":"1.1.4","description":"Extract font anatomy metrics from TTF, OTF, and WOFF2 in TypeScript and from the command line.","keywords":["font","font-anatomy","font-metrics","vertical-metrics","x-height","cap-height","ascender","descender","opentype","ttf","otf","woff2","typography","typeface","typescript","font-cli"],"author":{"name":"Ádám László Rocska","url":"https://adam-rocska.github.io"},"license":"MIT","engines":{"node":">=20.0.0"},"repository":{"type":"git","url":"git+https://github.com/adam-rocska/font-anatomy-typescript.git"},"homepage":"https://github.com/adam-rocska/font-anatomy-typescript#readme","bugs":{"url":"https://github.com/adam-rocska/font-anatomy-typescript/issues"},"funding":[{"type":"github","url":"https://github.com/sponsors/adam-rocska"}],"scripts":{"clean":"rimraf dist","build":"bunchee && chmod +x dist/bin/font-anatomy.js","lint":"eslint \"src/**/*.ts\" \"test/**/*.ts\" \"bin/**/*.mjs\" \"eslint.config.mjs\" \"vitest.config.mts\"","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","typecheck":"tsc --noEmit","check":"pnpm lint && pnpm typecheck && npm_config_cache=.npm-cache pnpm attw --pack"},"devDependencies":{"@arethetypeswrong/cli":"^0.18.2","@types/node":"^24.12.2","@types/opentype.js":"^1.3.9","@types/wawoff2":"^1.0.2","@types/yargs":"^17.0.35","@typescript-eslint/parser":"^8.58.2","@vitest/coverage-v8":"^4.1.5","bunchee":"^6.10.0","eslint":"^10.2.1","pnpm":"^10.33.0","rimraf":"^6.1.3","type-fest":"^4.41.0","typescript":"^6.0.3","vitest":"^4.1.5","yargs":"^18.0.0"},"main":"./dist/cjs/index.js","module":"./dist/es/index.mjs","types":"./dist/cjs/index.d.ts","exports":{".":{"import":{"types":"./dist/es/index.d.mts","default":"./dist/es/index.mjs"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}}},"bin":{"font-anatomy":"dist/bin/font-anatomy.js"},"dependencies":{"@adam-rocska/markdown-table":"^1.0.2","opentype.js":"^1.3.4","wawoff2":"^2.0.1"},"publishConfig":{"access":"public"},"packageManager":"pnpm@10.33.0","gitHead":"20ea905e090fc16219f41bbd633dfe5512a61567","_id":"@adam-rocska/font-anatomy@1.1.4","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-WV9L+qmCtjjnvDMJtORhHEGhPXR7EIR19D51iCDLUSZTp5c1iBSYt4URcxPkn6mqfRezxyxi6yJUdkl+C4f7PQ==","shasum":"ad85553ecf54b45aa0a11f89a687e18c9549d0c8","tarball":"https://registry.npmjs.org/@adam-rocska/font-anatomy/-/font-anatomy-1.1.4.tgz","fileCount":8,"unpackedSize":300505,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adam-rocska%2ffont-anatomy@1.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEa4xTEsXgL7IuwyHP9kulAgE92iq8wq/dNtG+OlSx1+AiBsoJ+gy79cRRucpFBG5B0ss9IAV3/mgacBETxGe6Bjhg=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:efaeedd2-2056-4840-9f38-3d44ec07f42b"}},"directories":{},"maintainers":[{"name":"adam-rocska","email":"rocska.adam@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/font-anatomy_1.1.4_1776803809185_0.1748118481712544"},"_hasShrinkwrap":false}},"time":{"created":"2024-08-09T23:51:38.332Z","modified":"2026-04-21T20:36:49.691Z","1.0.0":"2024-08-09T23:51:38.615Z","1.1.0":"2024-08-11T08:09:46.420Z","1.1.1":"2024-08-12T09:25:57.135Z","1.1.4":"2026-04-21T20:36:49.413Z"},"bugs":{"url":"https://github.com/adam-rocska/font-anatomy-typescript/issues"},"author":{"name":"Ádám László Rocska","url":"https://adam-rocska.github.io"},"license":"MIT","homepage":"https://github.com/adam-rocska/font-anatomy-typescript#readme","keywords":["font","font-anatomy","font-metrics","vertical-metrics","x-height","cap-height","ascender","descender","opentype","ttf","otf","woff2","typography","typeface","typescript","font-cli"],"repository":{"type":"git","url":"git+https://github.com/adam-rocska/font-anatomy-typescript.git"},"description":"Extract font anatomy metrics from TTF, OTF, and WOFF2 in TypeScript and from the command line.","maintainers":[{"name":"adam-rocska","email":"rocska.adam@gmail.com"}],"readme":"# Font Anatomy for TypeScript\n\n[![NPM Version](https://img.shields.io/npm/v/@adam-rocska/font-anatomy.svg)](https://www.npmjs.com/package/@adam-rocska/font-anatomy)\n[![License](https://img.shields.io/npm/l/@adam-rocska/font-anatomy)](https://github.com/adam-rocska/font-anatomy-typescript/blob/master/LICENSE)\n\n| Aspect               | Badge                                                                                                                                                         |\n| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Minified             | [![Minified](https://badgen.net/bundlephobia/min/@adam-rocska/font-anatomy)](https://bundlephobia.com/package/@adam-rocska/font-anatomy)                      |\n| Minified + gzip      | [![Minified + gzip](https://badgen.net/bundlephobia/minzip/@adam-rocska/font-anatomy)](https://bundlephobia.com/package/@adam-rocska/font-anatomy)            |\n| Dependency Count     | [![Dependency Count](https://badgen.net/bundlephobia/dependency-count/@adam-rocska/font-anatomy)](https://bundlephobia.com/package/@adam-rocska/font-anatomy) |\n| Tree-shaking Support | [![Tree-shaking Support](https://badgen.net/bundlephobia/tree-shaking/@adam-rocska/font-anatomy)](https://bundlephobia.com/package/@adam-rocska/font-anatomy) |\n\nFont anatomy is one of those things you keep needing in real projects and somehow still end up rebuilding from scratch.\n\n`@adam-rocska/font-anatomy` is a small Node.js library and CLI for extracting the most useful vertical font metrics from real font binaries:\n\n- `unitsPerEm`\n- `ascender`\n- `descender`\n- `xHeight`\n- `capHeight`\n\nIt is built for the practical use case:\n\n- inspect a font file quickly\n- serialize the anatomy as clean JSON\n- generate a Markdown report for docs or audits\n- normalize metrics relative to `unitsPerEm` or another metric\n- keep working in TypeScript without inventing your own shape every time\n\n## Why This Exists\n\nMost font tooling libraries focus on parsing, rendering, shaping, or editing.\nThat is useful, but if what you actually need is \"give me the anatomy of this font and let me compare it\", they are usually too low-level.\n\nThis package sits exactly at that layer.\n\nIt is intentionally narrow:\n\n- parse a font\n- extract anatomy\n- keep the API small\n- make the output easy to pipe, store, compare, and reuse\n\n## Requirements\n\n- Node.js 20 or newer\n\n## Supported Input Formats\n\nThe library currently accepts:\n\n- TTF\n- OTF\n- WOFF2\n\nWOFF2 input is decompressed automatically before parsing.\n\n## What You Get Back\n\nThe core output type is:\n\n```ts\ntype FontAnatomy<Value extends { valueOf(): number }> = {\n  font?: Font;\n  unitsPerEm: Value;\n  ascender: Value;\n  descender: Value;\n  xHeight: Value;\n  capHeight: Value;\n};\n```\n\nFor the library API, the parsed `opentype.js` `Font` instance is kept on `anatomy.font` when available.\nFor CLI JSON output, the result is serialized as plain metric data without the embedded font object.\n\n## Installation\n\n```sh\npnpm add @adam-rocska/font-anatomy\n```\n\n```sh\nnpm install @adam-rocska/font-anatomy\n```\n\n## CLI\n\nThe CLI reads the font from `stdin` and writes to `stdout`, which makes it easy to compose with normal shell tooling.\n\n### JSON output\n\n```sh\ncat Poppins-BoldItalic.ttf | font-anatomy\n```\n\nExample output:\n\n```json\n{\n  \"unitsPerEm\": 1000,\n  \"ascender\": 1050,\n  \"descender\": -350,\n  \"xHeight\": 558,\n  \"capHeight\": 705\n}\n```\n\n### Markdown output\n\n```sh\ncat Poppins-BoldItalic.ttf | font-anatomy --output-format=md > Poppins-BoldItalic.md\n```\n\nYou can also use the short flag:\n\n```sh\npnpm dlx @adam-rocska/font-anatomy -o md < Poppins-BoldItalic.ttf\n```\n\nMarkdown output includes:\n\n- localized naming metadata when available\n- absolute anatomy values\n- relative anatomy values normalized to `unitsPerEm`\n\n### Heading level\n\nFor Markdown output, you can control the top heading level:\n\n```sh\ncat Poppins-BoldItalic.ttf | font-anatomy -o md --heading-level=2\n```\n\nValid heading levels are `1` through `5`.\n\n## Library API\n\nThe public surface is intentionally small:\n\n- `fromFontBinary(buffer)`\n- `fromFontFile(path)`\n- `fromFont(opentypeFont)`\n- `relativize(basedOn, anatomy, numberToValue?)`\n- `isFontAnatomy(candidate, isValue?)`\n- `FontAnatomyInstance`\n\n### Read directly from a file\n\n```ts\nimport { fromFontFile, relativize } from \"@adam-rocska/font-anatomy\";\n\nasync function main() {\n  const anatomy = await fromFontFile(\"path/to/font.ttf\");\n  if (!anatomy) throw new Error(\"Could not extract anatomy.\");\n\n  console.log(anatomy);\n  console.log(relativize(\"unitsPerEm\", anatomy));\n}\n\nawait main();\n```\n\n### Read from a binary buffer\n\n```ts\nimport { readFile } from \"node:fs/promises\";\nimport { fromFontBinary } from \"@adam-rocska/font-anatomy\";\n\nconst file = await readFile(\"path/to/font.woff2\");\nconst anatomy = await fromFontBinary(file);\n\nif (!anatomy) {\n  throw new Error(\"Could not extract anatomy.\");\n}\n\nconsole.log(anatomy.xHeight);\n```\n\n### Normalize metrics relatively\n\n`relativize` turns an absolute anatomy into a relative one, usually against `unitsPerEm`:\n\n```ts\nimport { relativize } from \"@adam-rocska/font-anatomy\";\n\nconst anatomy = {\n  unitsPerEm: 1000,\n  ascender: 800,\n  descender: -200,\n  xHeight: 500,\n  capHeight: 700,\n};\n\nconst relative = relativize(\"unitsPerEm\", anatomy);\n\nconsole.log(relative);\n// {\n//   unitsPerEm: 1,\n//   ascender: 0.8,\n//   descender: -0.2,\n//   xHeight: 0.5,\n//   capHeight: 0.7,\n// }\n```\n\nYou can also preserve your own numeric wrapper type by passing a converter:\n\n```ts\nimport { relativize } from \"@adam-rocska/font-anatomy\";\n\nclass Points {\n  constructor(private readonly value: number) {}\n  valueOf() { return this.value; }\n  static fromNumber(n: number) { return new Points(n); }\n}\n\nconst anatomy = {\n  unitsPerEm: new Points(1000),\n  ascender: new Points(800),\n  descender: new Points(-200),\n  xHeight: new Points(500),\n  capHeight: new Points(700),\n};\n\nconst relative = relativize(\"unitsPerEm\", anatomy, Points.fromNumber);\n```\n\n### Validate a candidate value\n\nIf you are reading anatomy data from JSON, caches, APIs, or other tools, `isFontAnatomy` gives you a dedicated type guard:\n\n```ts\nimport { isFontAnatomy } from \"@adam-rocska/font-anatomy\";\n\nconst candidate: unknown = JSON.parse(someJsonString);\n\nif (isFontAnatomy(candidate)) {\n  console.log(candidate.capHeight);\n}\n```\n\nIt also supports custom value guards for wrapped numeric types.\n\n## Behavior and Failure Model\n\nIf extraction fails, the library functions return `undefined` and write a message to `stderr`.\n\nThat means the package is easy to use in scripts and pipelines where you want:\n\n- a narrow success type\n- no giant custom error hierarchy\n- human-readable diagnostics when parsing fails\n\nThe CLI exits with an error if:\n\n- no input is provided\n- the input is not a supported font binary\n- the font cannot be parsed\n- the font does not expose the expected OS/2 anatomy fields\n\n## Reliability\n\nThis package is not just a thin wrapper around `opentype.js`.\nIt is backed by a large golden-output test corpus.\n\nAt the time of writing, the repository validates the CLI against:\n\n- 615 specimen fonts\n- 599 `.ttf` fixtures\n- 16 `.woff2` fixtures\n- JSON and Markdown output for every specimen\n\nThe corpus includes:\n\n- static fonts\n- variable fonts\n- different families, weights, and styles\n- very long real-world file names\n\nThere are also direct unit tests for relative normalization, including custom wrapped numeric values.\n\n## When To Use It\n\nThis package is a good fit when you need to:\n\n- compare vertical metrics across fonts\n- build typographic tooling\n- audit design-system font choices\n- generate docs from font files\n- normalize font metrics for layout work\n- extract anatomy in scripts, CLIs, or build pipelines\n\nIt is probably not the right package if you need:\n\n- glyph outline editing\n- shaping\n- text rendering\n- subsetting\n- a full font editor\n\n## Example Markdown Output\n\nThe generated Markdown is designed to be readable in docs, issues, wikis, and generated reports.\n\nIt looks like this:\n\n```md\n# Poppins Bold Italic\n\nExtracted using `font-anatomy`, a CLI utility of\n[`@adam-rocska/font-anatomy`](https://github.com/adam-rocska/font-anatomy)\n\n## Things to know\n\n| Attribute      | Value |\n| -------------- | ----- |\n| Font Family    | Poppins |\n| Font Subfamily | Bold Italic |\n| Full Name      | Poppins Bold Italic |\n| Version        | 4.004 |\n\n## Anatomy\n\n| Trait        | Absolute Value | Relative Value |\n| ------------ | -------------- | -------------- |\n| Units Per Em | 1000           | 1              |\n| Ascender     | 1050           | 1.05           |\n| Descender    | -350           | -0.35          |\n| X-Height     | 558            | 0.558          |\n| Cap Height   | 705            | 0.705          |\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}