资讯动态

opentype.js 官方示例完全指南:本地服务器启动、浏览器字体读写与 Node.js 造字实战

发布时间:2026/10/15 20:17:11 来源:尧图企业网站定制
图形学【免费下载链接】opentype.jsRead and write OpenType fonts using JavaScript.项目地址https://gitcode.com/gh_mirrors/op/opentype.js点击查看免费下载opentype.js 是一个使用 JavaScript 读写 OpenType/TrueType 字体的开源库仓库在 docs/examples 目录下附带了一批可直接运行的示例示例说明文档位于 docs/examples/README.md。本文以该示例说明文档为骨架结合仓库源码与测试系统讲解示例的启动方式、浏览器端读取—序列化—再解析—绘制的完整链路、从零构造字体的方法以及 Node.js 环境下生成.otf字体文件的实战流程。读完本文你将能够搭建本地演示环境、读懂每个示例的底层调用关系并直接复用这些代码生成自己的字体。示例总览与目录结构在开始之前先弄清docs/examples目录下各文件扮演的角色文件运行环境演示内容reading-writing.html浏览器读取 WOFF 字体 → 序列化 → 再次解析 → 逐字形绘制到 Canvascreating-fonts.html浏览器用Path/Glyph从零绘制字形并导出字体font-editor.html浏览器实验性交互式字形编辑器拖点、增删点、实时预览generate-font-node.jsNode.js纯代码构造一套Latin字体并输出.otf文件typescriptNode.js/浏览器TypeScript 环境下如何引入dist/opentype.mjsdocs/index.html、docs/font-inspector.html、docs/glyph-inspector.html浏览器主演示页与字形检视器位于仓库根目录的docs下所有浏览器示例都通过 ES Module 语句import * as opentype from /dist/opentype.mjs引入库本身因此它们依赖dist目录下的构建产物——这解释了为什么必须先启动构建/开发服务器。为什么这些示例必须跑在本地 Web 服务器上原文档的第一句就给出了硬性约束Due to security issues, these examples only work when running a local web server出于安全问题这些示例只能在本地 Web 服务器上运行。背后的原因可以从代码层面得到印证fetch()加载字体文件reading-writing.html 通过fetch(../fonts/FiraSansMedium.woff)获取 docs/fonts/FiraSansMedium.woff。在file://协议下浏览器会出于同源策略直接拒绝此类请求只有 HTTP(S) 协议下的同源或 CORS 许可请求才会被放行。ES Module 的限制示例全部使用script typemodule而 ES Modules 在file://环境下同样会因 CORS 策略无法加载模块脚本。Blob 下载示例通过URL.createObjectURL(...)生成可下载的字体链接这一整套浏览器能力在裸文件协议下体验并不完整。因此无论在任何操作系统上第一步都是先把仓库根目录跑成一个静态 HTTP 服务。启动本地开发服务器npm start原文档给出的启动命令是在仓库根目录执行npm start然后访问http://localhost:8080/examples/reading-writing.html。对照当前仓库的 package.jsonnpm start脚本的实现是esbuild --bundle src/opentype.mjs --outdirdist --targetes2018 --formatesm \ --out-extension:.js.mjs --define:DEBUGfalse --global-nameopentype \ --watch --servedir. --footer:js...这条命令由四个动作组成恰好解释了原文档的每一步打包以 src/opentype.mjs 为入口将其与src/font.mjs、src/glyph.mjs、src/parse.mjs、src/tables/*.mjs等模块一起打包成dist/opentype.mjsESM 格式targetes2018供示例页面import使用监视--watch让 esbuild 在源码变更时自动重新打包方便在 docs/font-editor.html 这类示例上迭代调试静态服务--servedir.把仓库根目录当作静态文件根目录所有示例 HTML 及其依赖的字体文件都能被直接访问UMD 兼容垫片--footer:js注入一段 UMD 包装代码让产物在 AMD、CommonJS、浏览器全局变量opentype三种环境里都能被识别。实际操作步骤如下cd opentype.js npm install # 安装 esbuild 等 devDependencies npm start # 构建并启动开发服务器两个需要留意的版本差异原文档给出的示例地址是http://localhost:8080/examples/reading-writing.html。而当前仓库中示例文件实际位于docs/examples目录且--servedir.以仓库根为服务根目录因此对应的实际访问路径通常应为http://host/docs/examples/reading-writing.html当前start脚本并未显式指定--portesbuild 静态服务的默认端口为 8000README 中的 8080 属于历史写法。请以终端启动时打印的 Local URL 为准不必纠结于 README 里写的端口号。浏览器示例拆解一reading-writing.html —— 字体的读取—写出—重读往返reading-writing.html 演示了 opentype.js 最核心的能力闭环parse → toArrayBuffer → parse。核心代码只有几行import * as opentype from /dist/opentype.mjs; const buf await fetch(../fonts/FiraSansMedium.woff); const font1 opentype.parse(await buf.arrayBuffer()); // 第 1 代从 WOFF 解析 const font2 opentype.parse(font1.toArrayBuffer()); // 第 2 代序列化后再次解析这里每一步都对应明确的源码实现opentype.parse(arrayBuffer)入口实现在 src/opentype.mjs。parseBuffer先读取文件头签名——\x00\x01\x00\x00/true/typ1判定为 TrueType 轮廓glyfOTTO判定为 CFF 轮廓wOFF则进入 WOFF 表目录解析分支并由内置的tiny-inflate见 src/opentype.mjs 的inflate导入对压缩表做解压。这就是示例能直接fetch一个.woff文件的原因。font.toArrayBuffer()实现在 src/font.mjs。它先调用toTables()内部是sfnt.fontToTable(this)见 src/font.mjs把Font对象编码回 SFNT 表结构再逐字节复制为ArrayBuffer。这一过程与 test/font.spec.mjs 中describe(toTables)覆盖的序列化测试对应。往返之后示例为每个字形创建一块 Canvas并调用字形级的三个绘制方法reading-writing.htmlglyph.draw(ctx, x, y, fontSize, {}, font2); // 绘制字形轮廓 glyph.drawPoints(ctx, x, y, fontSize); // 标出曲线上/曲线外控制点 glyph.drawMetrics(ctx, x, y, fontSize); // 绘制度量参考线这三个方法的语义在 src/glyph.mjs 中都有明确定义Glyph.drawsrc/glyph.mjs通过getPath()生成缩放后的 Path 再交给path.draw(ctx)Glyph.drawPointssrc/glyph.mjs曲线上on-curve点画成蓝色曲线外off-curve控制点画成红色Glyph.drawMetricssrc/glyph.mjs黑色线表示原点坐标轴蓝色线表示字形包围盒bounding box绿色线表示字形进深advance width。最后页面用BlobcreateObjectURL把序列化结果重新包装为可下载的.otf文件reading-writing.html完整演示了读进来—改一改—写出去的浏览器端闭环。浏览器示例拆解二creating-fonts.html —— 从零构造一个最小字体creating-fonts.html 示范了不用任何现成字体、纯靠路径指令画出一套字体的流程。它的核心思路与 README.md 的 Craft a font 一节完全一致用Path的moveTo/lineTo勾勒轮廓。例如字母 A 的路径由内外两个矩形外框100,0 → 100,700 → 600,700 → 600,0挖空200,400 → 500,400 → 500,600 → 200,600组成即一个标准的反白 Avar aPath new opentype.Path(); aPath.moveTo(100, 0); aPath.lineTo(100, 700); aPath.lineTo(600, 700); aPath.lineTo(600, 0); aPath.lineTo(500, 0); aPath.lineTo(500, 300); aPath.lineTo(200, 300); aPath.lineTo(200, 0); aPath.moveTo(200, 400); aPath.lineTo(500, 400); aPath.lineTo(500, 600); aPath.lineTo(200, 600);用Glyph把路径包装成字形。每个字形都需提供name、unicode、advanceWidth和pathvar aGlyph new opentype.Glyph({ name: A, unicode: 65, // A 的 Unicode 码点 advanceWidth: 650, // 进深即笔移动的宽度 path: aPath });注意.notdef字形是必须的它通常作为字体的第一个字形索引 0。从 src/glyph.mjs 的构造函数可以看出一个细节当name为.notdef时unicode会被强制置为undefined而 unicode0被保留给名为.null的字形其他字形不允许占用。用Font聚合字形并设置全局度量var font new opentype.Font({ familyName: OpenTypeSans, styleName: Medium, unitsPerEm: 1000, // 设计网格1000 单位/em ascender: 800, // 上伸部 descender: -200, // 下伸部可为负 glyphs: [notdefGlyph, aGlyph, bGlyph] });序列化并验证。示例调用font.toTables()打印表结构font.toArrayBuffer()得到字节再opentype.parse(buffer)解析回来渲染——通过自己写的自己读来验证字节流是合法的。浏览器示例拆解三font-editor.html —— 一个 100 行核心的交互式字形编辑器font-editor.html 自述是 Experimental font editor它的可玩性在于把上面两节的逻辑变成了实时交互字形以点阵坐标形式存在gGlyphMap中与 Node 版示例共享同一套A–Z/_轮廓数据鼠标拖拽点直接修改坐标onMouseDown/onMouseMove中的STATE_DRAGGINGShift点击删除点 Add Point / - Remove Last Point 按钮实时增删路径点画布右侧实时预览 HAMBURGEVONS 样张dumpGlyphMap()会把当前点阵导出为 JS 代码文本方便直接复制回源码。关键之处在于downloadFont()函数font-editor.html它把gGlyphMap中的点阵坐标乘以TTF_SCALE80转换成字体内坐标逐点moveTo/lineTo生成Path构建Glyph列表最后组装成Font并导出.otfvar oFont new opentype.Font({ familyName: Pyramid, styleName: Regular, unitsPerEm: 1000, ascender: 800, descender: -200, glyphs: otGlyphs }); const a document.getElementById(download-font); a.href window.URL.createObjectURL(new Blob([oFont.toArrayBuffer()], {type: font/otf}));这与 Node 版示例下文共享同一套造字配方是理解 opentype.js路径 → 字形 → 字体 → 字节流对象模型的活教材。Node.js 造字实战generate-font-node.js 全解原文档给出的 Node.js 运行方式是cd opentype.js/examples node generate-font-node.js # 期望生成 Pyramid-Regular.otf对应到当前仓库的实际目录第一步应调整为cd docs/examples或直接在仓库根执行node docs/examples/generate-font-node.js。脚本开头require(../dist/opentype)意味着它依赖已构建的 UMD 产物所以请先执行npm start或npm run build生成dist目录再运行本脚本。generate-font-node.js 演示了用几 KB 代码生成一套能被 macOS Font Book 识别为 Latin 字体的.otf。整段逻辑可拆为四步第一步定义全局度量var UNITS_PER_EM 1000; // 设计网格大小 var ASCENDER 900; // 上伸部基线以上最高点 var DESCENDER -100; // 下伸部基线以下最低点 var SCALE 80; // 轮廓坐标缩放系数字形轮廓先用 0–10 的粗略网格描述写入字体时统一乘SCALE得到 0–800 范围的字体内坐标——正好适配unitsPerEm 1000的网格。第二步用坐标点阵描述每个字形GLYPH_MAP以字符为键、点列[x, y]为值描述单轮廓字形。例如A: [[0, 0], [4, 10], [8, 0]], // 三角 A B: [[0, 0], [0, 10], [5, 10], [5, 5], [7, 5], [7, 0]], // 阶梯形 B注释特别说明这套最简点集是让 macOS Font Book 将其识别为 Latin 字体并出现在字体选单中的最小必需集合。同时脚本用TTF_NAME_MAP把_、.这类在 TrueType 命名规则中不受欢迎的字符映射为underscore、periodvar TTF_NAME_MAP { _: underscore, .: period };第三步把点阵转为 Glyph空间字形与.notdef不能由点阵表达单独构造.notdef使用空路径它是必需的占位字形空格则只设置advanceWidth: 10 * SCALE。其余字形在循环中完成点 → 路径 → 字形的转换var path new Path(); for (var j 0; j points.length; j) { if (j 0) path.moveTo(x * SCALE, y * SCALE); else path.lineTo(x * SCALE, y * SCALE); } var glyph new Glyph({ name: ttfName, unicode: glyphName.charCodeAt(0), advanceWidth: (w 1) * SCALE, // 最宽处 1 个单位的留白 path: path });第四步组装 Font 并导出var font new Font({ familyName: Pyramid, styleName: Regular, unitsPerEm: UNITS_PER_EM, ascender: ASCENDER, descender: DESCENDER, glyphs: glyphs}); font.download(); // 见下方废弃说明字体文件名的生成规则为FamilyName-StyleName.ext即Pyramid-Regular.otf。⚠️ 关于font.download()的重要版本说明示例脚本结尾仍调用font.download()但查看 src/font.mjs 会发现当前版本该方法已被标记为DEPRECATED函数体只打印console.error(DEPRECATED: platform-specific actions are to be implemented on user-side)不会再实际写出文件。原文档中运行后生成 Pyramid-Regular.otf的描述基于历史版本行为。在当前版本下正确做法是在用户侧自行写文件例如const fs require(fs); fs.writeFileSync(Pyramid-Regular.otf, Buffer.from(font.toArrayBuffer()));或使用异步方式fs.promises.writeFile(Pyramid-Regular.otf, Buffer.from(font.toArrayBuffer()))。浏览器侧则对应Bloba download的方案README.md 中同时给出了 Node 与浏览器两种保存字体的推荐写法。TypeScript 示例三种引入方式README.md 指出 TypeScript 用户可参考 docs/examples/typescript。其中 index.ts 演示了引入dist/opentype.mjs的三种合法写法import opentype from ../../dist/opentype.mjs // 默认导入 import { load } from ../../dist/opentype.mjs // 命名导入 import * as mySpecialOpentype from ../../dist/opentype.mjs // 命名空间导入配套配置方面示例的 package.json 声明了types/opentype.js依赖提供类型声明tsconfig.json 采用commonjs模块 es2019目标 esModuleInterop方便 CommonJS 工程平滑使用 ESM 构建产物。延伸主演示页与检视器里的更多能力除了docs/examples仓库根目录docs下还有三个更高阶的演示与上述示例共享同一套服务环境docs/index.html主演示页。支持上传本地字体、调节字号、切换 Draw Points/Draw Metrics、开关 Kerning 与 Ligatures、可变字体轴滑块以及一个网格吸附snapping实验——把字形路径的坐标按指定距离取整见 docs/site.js 的snap逻辑。docs/font-inspector.html 与 docs/glyph-inspector.html字体级与字形级检视器可直观查看解析后的表结构与单个字形的点线信息。这些演示共同依赖font.draw(ctx, text, x, y, fontSize, options)、font.drawPoints、font.drawMetrics等Font级 API实现在 src/font.mjs渲染选项包括kerning、features如liga/rlig连字开关、hinting、fill与colorFormat等完整参数说明见 README.md。从示例到生产API 速查与下一步综合上述示例可以提炼出一套在任何项目中可复用的调用模式目标浏览器Node.js加载字体fetch(url)→arrayBuffer()→opentype.parse(buffer)fs.promises.readFile(file)→opentype.parse(buffer)渲染文本font.draw(ctx, text, x, y, fontSize, options)font.getPath(text, x, y, fontSize)→ 输出 SVG/PDF获得字形路径font.getPath(text, 0, 200, 72, {kerning:true})同上序列化输出new Blob([font.toArrayBuffer()])createObjectURLfs.writeFileSync(out.otf, Buffer.from(font.toArrayBuffer()))从零造字new Path()→new Glyph()→new Font({...})同左底层支撑Font.parse的签名分支与 WOFF 解压见 src/opentype.mjsFont.toTables/toArrayBuffer见 src/font.mjsGlyph.getPath/draw/drawPoints/drawMetrics见 src/glyph.mjs 与 src/glyph.mjs序列化正确性由 test/font.spec.mjs 的测试持续守护。一句话总结本文的关键操作结论先npm install npm start拿到dist产物和静态服务浏览器端访问/docs/examples/下的各 HTML 示例Node 端则按cd docs/examples node generate-font-node.js运行注意把已废弃的font.download()换成显式的fs.writeFileSync。在此基础上你可以直接用示例中的点阵造字法、round-trip 校验法和 Blob 下载法搭建自己的字体生成、检视与定制工具。赞分享图形学【免费下载链接】opentype.jsRead and write OpenType fonts using JavaScript.项目地址https://gitcode.com/gh_mirrors/op/opentype.js点击查看免费下载相关推荐Thymeleaf在WebFlux中的应用响应式模板开发实战指南Thymeleaf在WebFlux中的应用响应式模板开发实战指南 Thymeleaf作为现代化的服务器端Java模板引擎在响应式编程领域展现了强大的能力。本后端wpcom.js 官方示例指南服务端、浏览器 CORS 与 Node.js 三种接入方式实战wpcom.js 官方示例指南服务端、浏览器 CORS 与 Node.js 三种接入方式实战 wpcom.js 是 WordPress.com 的 JavaS前端CMSCANN/asc-devkit Ascend C asc_brcb函数asc_brcb 产品支持情况 | 产品 | 是否支持 | | : | : : | | term Atlas A3 训练系列产品/Atlas A3 推理系列产人工智能深度学习算子库CANNAscend上一篇RedwoodJS 集成 Nhost 认证从 CLI 安装到 GraphQL 鉴权链路全解析下一篇AI伴侣技术终极指南companion-app如何重新定义人机交互的未来创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑