不依赖任何库打开老旧.doc和.rtf文件:Gander自研Word 97-2003二进制解析器深度剖析 【免费下载链接】ganderTake a gander at any file. Offline, zero-permission Android viewer for PDF, Word, Excel, PowerPoint, ODT, RTF, ZIP, STL, photos, video, audio, Markdown and code.项目地址https://gitcode.com/gh_mirrors/gander1/gander点击查看免费下载Gander 是一款完全离线、零权限的 Android 文件查看器。面对上世纪遗留下来的 Word 97-2003.doc二进制文件和.rtf富文本它不捆绑任何第三方解析库而是用自研的纯 JavaScript 解析器把这两种老古董格式直接读进屏幕。本文带你拆解这个解析器背后的思路看看一个没有 FAT 表、没有 sprm 编码表的文档是如何变成手机上整齐排版的页面的。为什么老旧文档这么难打开先说说背景。.docx、.xlsx这类新式 Office 文件本质是 ZIP 压缩包里面装着 XML很多开源库都能解析。但 2003 年之前的.doc完全不同.doc是一个小文件系统它采用复合文档compound file格式内部有 FAT 扇区表和目录流正文、格式、图片分别藏在不同的流里.rtf是一整行指令流反斜杠开头的是指令\b表示加粗、\par表示换段花括号界定格式作用域图片甚至被写成十六进制文本名字会说谎大量.doc文件内部其实是 RTF 文本这是二十多年来非 Word 程序生成 Word 文件的偷懒方式。所以 Gander 的策略很直白看文件的前几个字节而不是看扩展名。这段判断逻辑在 prose.js 里只有十几行开头是{\\rtf就走 RTF 解析器是PK或?xml就是 ODT而D0 CF 11 E0这四个魔数则是二进制.doc的身份证。.doc 解析器把一个小文件系统拆开读真正的重头戏在 prose-doc.js它全程自己手写没有借助任何 OLE/复合文档库。第一步自写复合文档读取器解析器第一关是docCompoundprose-doc.js它要从原始字节里还原出FAT 扇区表从文件头 109 个条目和 DIFAT 链中收集并限制数量不超过实际扇区数目录128 字节一个条目读出WordDocument、0Table/1Table、Data三个关键流每个流的数据小流藏在根条目的迷你流里大流沿 FAT 链逐扇区拼接。值得注意的是它的防御姿态链不能重复经过同一个扇区、目录不能指向自己、流不能超过文件大小。注释里写着原因——一个损坏的 8KB 文件曾经能让读取卡住好几秒甚至被反复读进数百 MB 内存。对手机上的纯 JS 解析器来说这种每个数字都要限制在文件长度能解释的范围内的纪律是生存关键。第二步FIB 与片段表piece table复合文档打开后WordDocument流开头的 FIB文件信息块是一张偏移量索引表指向所有其他结构的位置prose-doc.js。但正文并不一定连续存放。Word 的快速保存会把新输入的文字追加在文件末尾于是读取顺序和磁盘顺序对不上。连接两者的就是片段表每一片记录一段字符在文档中的位置CPcharacter position对应到WordDocument流中的字节偏移FC。解析器在 prose-doc.js 中一次性把各片解码成文本之后要给某个字符找格式时用二分法prose-doc.js通过它所在的片段把 CP 换算回 FC。源码注释里有一句很精辟的话这种格式的所有 bug几乎都是把 CP 和 FC 两种位置搞混造成的。第三步应用 sprm 格式指令.doc的格式不是每段存一份属性而是压缩过的差分指令——sprm两字节编码加操作数按顺序叠加到基线上。段落格式 段落样式 自己的 sprm字符格式 段落样式 字符样式 自己的 sprm。两个应用函数docApplyChpprose-doc.js和docApplyPapprose-doc.js逐条解释这些十六进制指令加粗、斜体、字号、颜色、行距、缩进、边框、底纹、表格单元格……解析器最终能画出的东西包括任意文字脚本、标题、带正确编号的列表、合并单元格的表格、图文并排、文本框、脚注尾注、页眉页脚、分节和分页。Word 6 / Word 95 这种更早的变体则以纯文本段落的形式降级展示密码保护的文档会被明确拒绝打开。.rtf 解析器一次遍历状态栈打底RTF 解析器 prose-rtf.js 的思路与.doc相反简单得多它不建任何树只做对字节流的一次线性扫描prose-rtf.js每个花括号一层状态格式组内的设置在}处被丢弃靠一个状态栈管理目的地destination机制{\fonttbl ...}字体表、{\colortbl ...}颜色表、{\footnote ...}脚注、{\pict ...}图片……组内文本送去别处不会混进正文编码兜底文件声明的是哪个 Windows 代码页俄文 1251、日文 932、GB2312 体系……就交给浏览器自带的TextDecoder解码连TextDecoder都不认识的 IBM PC 437/850 代码页解析器内置了完整的上半区字符表prose-rtf.js东亚双字节字符rtfIsLead判断某字节是否为两字节字符的前导字节防止把日文拆成乱码prose-rtf.js嵌套表格用\itap深度管理上限 16 层恶意文件也无法让解析器套娃失控prose-rtf.js列表编号不自己数RTF 的写入方会把算好的编号以纯文本形式写在\listtext组里解析器直接采用写进去是什么就显示什么。RTF 里无法渲染的 Windows 元文件图片会被替换成一块说明框图形里的文字仍会保留。三个解析器共用一套纸面.doc、.odt、.rtf三个自研解析器都不直接画界面而是产出一份中间结构交给共享的 prose-draw.js 铺成纸张页面纸张尺寸、页边距、页码、夜间模式翻转都由它统一负责。这也是为什么 README 里说Word 文档能像 PDF 一样显示页码、支持跳到第几页。文件类型的分发在 Android 侧完成FileKind.kt 把odt、rtf、doc、dot等扩展名统一指到prose.html再由前文说的字节嗅探决定交给哪个解析器——所以伪装成 .doc 的 RTF同样能被正确打开。这套自研解析器值不值得看对普通用户体验是手机离线、零权限、秒开二十年前的合同和会议纪要格式基本不丢。对想读源码的人这两个文件也是一份难得的教材展示了几件工程上很有价值的事魔数嗅探优于扩展名——prose.js 十几行代码就是完整方案对不可信输入的全面设防——FAT 链去环、深度上限、大小截断损坏文件只会打开失败而不会拖垮设备一次遍历、不建中间树的解析纪律内存占用可预期降级策略清晰——读不了的部件元文件图片、老版本格式给出明确提示或简化展示而不是静默出错。如果你对具体某一部分比如 sprm 编码、片段表二分查找、代码页解码想深挖建议从 prose-doc.js 和 prose-rtf.js 顶部的整段注释读起——作者把每个格式陷阱都写成了故事。测试素材在 tests/fixtures/files/legacy.doc 和 tests/fixtures/files/memo.rtf浏览器端的行为测试在 tests/viewer/test_prose_rtf.py 附近方便你验证自己的理解。赞分享【免费下载链接】ganderTake a gander at any file. Offline, zero-permission Android viewer for PDF, Word, Excel, PowerPoint, ODT, RTF, ZIP, STL, photos, video, audio, Markdown and code.项目地址https://gitcode.com/gh_mirrors/gander1/gander点击查看免费下载相关推荐LlamaIndex LegacyOfficeReader 实战借助 Apache Tika 解析 Word 97.doc等旧版 Office 文档LlamaIndex LegacyOfficeReader 实战借助 Apache Tika 解析 Word 97.doc等旧版 Office 文档 导读人工智能RAG大模型Xmake项目中共享库依赖的自动处理机制解析Xmake项目中共享库依赖的自动处理机制解析 在C/C项目开发过程中依赖管理是一个重要且复杂的环节。Xmake作为一款现代化的构建工具提供了完善的依赖管构建工具开发工具包管理器终极指南如何使用LIEF库深度解析和修改二进制文件终极指南如何使用LIEF库深度解析和修改二进制文件 LIEFLibrary to Instrument Executable Formats是一款功能强大逆向工程开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考