当前位置: 首页 > news >正文

通过连字从纯ASCII渲染化学式 - Fan

化学式利用上下标来表示物质中的原子组成和电荷状态。在通常情况下,在排版时输入化学式需要使用额外的Unicode字符(⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻₀₁₂₃₄₅₆₇₈₉),或者手动应用上下标样式,或者使用专门的语法(比如TeX)。

然而,使用常规键盘输入上下标字符并不方便,渲染TeX则往往需要繁重的依赖。有没有办法可以在不用TeX的情况下使用类似TeX语法绘制上下标呢?答案是有的,我们可以借助TrueType/OpenType字体的连字功能实现。

使用连字渲染化学式

现代字体技术允许我们通过字体的连字(ligature)功能来根据上下文替换字符的字形。该功能最初是用于 f + i = fi 这样的排版渲染需求,以及用于渲染像阿拉伯语这样字形和上下文强相关的文字。许多编程字体中也通过连字来渲染多字符操作符(比如将!=渲染为 )。

通过连字特性,我们可以实现纯ASCII化学式的渲染,只要定义以下连字规则:

  • 当数字出现下标标记符_之后时,使用下标字形
  • 当数字或+-符号出现在上标标记符^之后时,使用上标字形

为了方便,我们还可以规定

  • 当数字出现在下标字形之后时,维持下标字形
  • 当数字或+-符号出现在上标字形的数字之后时,维持上标字形

我们可以通过FontTools库来为现有字体添加这些连字规则。下面的Python脚本实现了这个功能:

build_chem_font.py
#!/usr/bin/env python3
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables._g_l_y_f import Glyph, GlyphComponent
from fontTools.feaLib.builder import addOpenTypeFeatures
from fontTools.pens.recordingPen import RecordingPen
from fontTools.pens.transformPen import TransformPen
from fontTools.pens.ttGlyphPen import TTGlyphPen
import iodef build_chem_font(font):glyf, hmtx = font["glyf"], font["hmtx"]gord, cmap = font.getGlyphOrder(), font.getBestCmap()gset = font.getGlyphSet()upm = font["head"].unitsPerEmdef register_glyph(name, glyph_obj, width, lsb):glyf[name] = glyph_objhmtx[name] = (width, lsb)if name not in gord:gord.append(name)empty_glyph = Glyph()empty_glyph.numberOfContours = 0register_glyph("hide.glyph", empty_glyph, 0, 0)def build_derivative(src, scale=1, xoff=0, yoff=0):rec = RecordingPen()gset[src].draw(rec)tt_pen = TTGlyphPen(gset)t_pen = TransformPen(tt_pen, (scale, 0, 0, scale, xoff, yoff))rec.replay(t_pen)return tt_pen.glyph()caret, lowline = cmap.get(ord("^")), cmap.get(ord("_"))nums = [cmap[ord(c)] for c in "0123456789"]pm = [cmap[ord(c)] for c in "+-"]for g in nums + pm:gl = build_derivative(g, 0.6, 0, int(upm * 0.35))register_glyph(f"{g}.sup", gl, int(hmtx[g][0] * 0.6), 0)for g in nums:gl = build_derivative(g, 0.6, 0, int(upm * -0.1))register_glyph(f"{g}.sub", gl, int(hmtx[g][0] * 0.6), 0)font.setGlyphOrder(gord)feature = f"""@supprefix = [{caret} {" ".join([c+ ".sup" for c in nums])}];@subprefix = [{lowline} {" ".join([c+ ".sub" for c in nums])}];@supchar0 = [{" ".join(nums + pm)}];@subchar0 = [{" ".join(nums)}];@supchar = [{" ".join([c+ ".sup" for c in nums + pm])}];@subchar = [{" ".join([c+ ".sub" for c in nums])}];feature calt {{lookup SUB_CHAIN {{ sub @subprefix @subchar0' by @subchar; }} SUB_CHAIN;lookup SUP_CHAIN {{ sub @supprefix @supchar0' by @supchar; }} SUP_CHAIN;lookup HIDE_CARET {{ sub {caret}' @supchar by hide.glyph; }} HIDE_CARET;lookup HIDE_LOWLINE {{ sub {lowline}' @subchar by hide.glyph; }} HIDE_LOWLINE;}} calt;"""with io.StringIO(feature) as fea:addOpenTypeFeatures(font, fea)if __name__ == "__main__":import argparseparser = argparse.ArgumentParser(description="Create chemical symbols font.")parser.add_argument("input", help="Path to input file (TTF font)")parser.add_argument("output", help="Path to output file")args = parser.parse_args()font = TTFont(args.input)build_chem_font(font)font.save(args.output)

通过python3 build_chem_font.py input.ttf chem.ttf命令运行该脚本,即可生成一个支持化学式渲染的字体文件chem.ttf。之后可以在浏览器环境中通过css@font-face加载字体查看结果:

preview.html
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>font preview</title><style>@font-face {font-family: 'ChemFont';src: url('chem.ttf') format('truetype');}body {display: flex;width: 100vw;height: 100vh;box-sizing: border-box;margin: 0;padding: 8px;}#preview {font-family: 'ChemFont', monospace;flex: 1;font-size: 24px;}</style>
</head>
<body><textarea id="preview">H^+ + OH^- = H_2O
Ag^+ + Cl^- = AgCl
Ba^2+ + SO_4^2- = BaSO_4
Fe^3+ + 3OH^- = Fe(OH)_3
CO_3^2- + 2H^+ = H_2O + CO_2
2Al + 6H^+ = 2Al^3+ + 3H_2
Cu^2+ + Fe = Fe^2+ + Cu
CH_4 + 2O_2 = CO_2 + 2H_2O
C_2H_5OH + 3O_2 = 2CO_2 + 3H_2O
CH_2=CH_2 + Br_2 = CH_2BrCH_2Br
CH_3COOH + C_2H_5OH = CH_3COOC_2H_5 + H_2O
C_6H_6 + HNO_3 = C_6H_5NO_2 + H_2O
C_6H_12O_6 + 6O_2 = 6CO_2 + 6H_2O
2CH_3OH + 3O_2 = 2CO_2 + 4H_2O
NH_4^+ + OH^- = NH_3 + H_2O
2I^- + Cl_2 = I_2 + 2Cl^-
MnO_4^- + 5Fe^2+ + 8H^+ = Mn^2+ + 5Fe^3+ + 4H_2O
Cr_2O_7^2- + 6Fe^2+ + 14H^+ = 2Cr^3+ + 6Fe^3+ + 7H_2O
CH_3CH_2OH + CuO = CH_3CHO + Cu + H_2O</textarea>
</body>
</html>

最终的渲染结果如下:

image

注:本方法修改了字体文件,操作前请先参考字体的授权规则和用户协议,避免未授权的编辑。

http://www.jsqmd.com/news/354874/

相关文章:

  • 魔搭平台AI免费资源list
  • 2026年博士论文降AI率10%以下:去AIGC痕迹的专业方案 - 我要发一区
  • LabVIEW条码追踪系统:一场代码与效率的优雅 dance
  • 探秘德恩特快热式电热水器:从技术内核到使用体验 - 速递信息
  • 2026年中国库存管理系统厂家发布:以网上管家婆为代表的标杆企业深度解析。 - 十大品牌推荐
  • 开启全新SEO之旅,从零基础成长为流量提升专家
  • AI聊天助手的SSE流式输出实现过程
  • 如何高效回收永辉超市购物卡?最全的变现指南来了! - 团团收购物卡回收
  • 互联网大厂Java面试实战:Spring Boot、微服务与Kafka在电商场景中的应用
  • 赶deadline必备!顶流之选的降AI率软件 —— 千笔·降AI率助手
  • 2026年度权威发布:最新库存管理系统厂家实力榜单与选型深度解析 - 十大品牌推荐
  • 2026年度库存管理系统厂家推荐榜单:技术适配与降本增效双维度综合评估。 - 十大品牌推荐
  • 2026年广州婚纱影楼公司口碑推荐榜/婚纱影楼优选,婚纱影楼找便宜的,婚纱影楼服务到位婚纱摄影,拍婚纱照 - 品牌策略师
  • 详细介绍:uni-app 原生 App 打包全攻略:Android/iOS 从配置到发布完整流程
  • 2026年度库存管理系统厂家推荐榜单:技术适配与降本增效双维度综合评估 - 十大品牌推荐
  • 2026年中国库存管理系统厂家发布:以网上管家婆为代表的标杆企业深度解析 - 十大品牌推荐
  • 揭秘永辉超市购物卡回收最优选择,教你如何快速变现! - 团团收购物卡回收
  • 《计算机网络》深入学:移动 IP 技术原理与应用
  • 告别付费困扰!免费PPT生成工具大盘点|高效办公必备 - 品牌测评鉴赏家
  • 2026年分析高性能桥梁PVC排水管价格及靠谱企业 - mypinpai
  • 分享一套优质的微信小程序校园志愿者系统(SpringBoot后端+Vue3管理端)
  • 告别繁琐排版!5款操作零门槛的AI PPT生成工具推荐 - 品牌测评鉴赏家
  • 口碑好的桥梁PVC排水管源头厂家有哪些 - 工业推荐榜
  • 2026年广州婚纱工作室公司口碑推荐:实力强婚纱工作室/稳定的婚纱工作室/声誉好的婚纱工作室婚纱影楼/拍婚纱照 - 品牌策略师
  • 手写mybatis
  • 新手PPT救星!5款零门槛生成工具实测:教育党职场人闭眼入,省时80% - 品牌测评鉴赏家
  • 分析2026年别墅门厂家十大排名,看哪家费用更合理 - 工业品牌热点
  • 分析法式装修设计费用多少钱,重庆哪家性价比高 - 工业品网
  • Java 中使用 Alibaba Fastjson 解析泛型类型 JsonResult<SysUserDTO> 的问题
  • 讲讲风机进风口价格合理的供应商,怎么选择有攻略 - mypinpai