面向手写笔的 Windows PDF 阅读与批注工具。
InkHorn 将连续页面阅读、压感书写与 PDF 批注保存放在一个原生桌面应用中,适合阅读讲义、标记资料和手写笔记。采用 C++20、Win32、Direct2D / DirectWrite 与 PDFium 构建,中文界面,支持单 EXE 便携运行。
当前版本:1.0.1 · 开发预览版。 阅读、书写和保存的基础流程已实现;真实设备手感、复杂文档性能及跨阅读器兼容性仍在持续验证。见 V1.0.1 更新说明。
| 方向 | 已支持 |
|---|---|
| PDF 阅读 | 默认同窗口打开多份 PDF、文件列表拖出新窗口、悬停标题拖入合并、多文档切换、连续页面、页码跳转、缩放、全屏与阅读位置记忆 |
| 正文文字 | 右上角搜索框、全文逐页查找与高亮、上下项跳转、指针单页拖选与复制 |
| 手写批注 | 两支独立配置的压感笔、线宽与颜色设置、自由荧光笔、自定义 HEX 颜色 |
| 图形与编辑 | 矩形、椭圆、直线、箭头,整笔擦除,选中后移动或删除,撤销与重做 |
| 输入与布局 | Windows Pointer / Windows Ink、笔侧键临时橡皮、鼠标与笔自动切换工具、左手模式、可拖动工具栏 |
| 阅读手感 | 平滑滚轮、页面空白区域拖动、触摸平移与惯性滚动 |
| 保存与恢复 | 保存原文件或另存为 PDF、批注显隐、后台恢复记录、工具偏好记忆 |
笔迹会随批注写入 PDF,同时保存标准 Ink 轨迹、矢量外观和 InkHorn 的可编辑数据。重新用 InkHorn 打开后可继续编辑;在其他阅读器中的显示仍需结合具体软件验证。
使用构建得到的 InkHorn.exe,将它放在可写目录中双击运行。正式 Release 构建静态链接 PDFium 和 MSVC 运行库,图标与第三方许可文本嵌入程序,运行时无需另装 Python、PDFium DLL 或 VC++ 运行库。
当前主要验证平台为 Windows 11 x64;Windows 10 x64 是兼容目标,ARM64 尚未验证。源码仓库不包含已忽略的 dist/ 和构建产物,源码编译步骤见下文。
- 点击首页“打开 PDF”,或按
Ctrl+O选择文档。 - 选择笔、荧光笔或形状工具,在页面上批注。再次点击已选工具可展开颜色、线宽或形状设置。
- 使用整笔橡皮擦除笔迹,或切换选择工具后移动、删除已有对象。
- 按
Ctrl+S保存,或按Ctrl+Shift+S另存为。
再次双击 PDF 时,同一路径的 InkHorn 程序会将文件交给已有窗口;同一份 PDF 已打开时会定位到它所在的窗口。点击顶部文件名打开“已打开文件”列表,将文件项拖出列表即可移到独立窗口,保留未保存笔迹、撤销重做与阅读位置。未最大化的普通窗口拖出时保持原窗口尺寸;系统最大化或 F11 全屏时,新窗口的宽、高各为原窗口的 60%。新窗口以鼠标为锚点,鼠标落在窗口宽度约 1/4、标题栏垂直中间的位置;靠近屏幕边缘时优先保留这个落点,窗口可部分超出屏幕。关闭原窗口后,独立窗口仍可继续使用;文档正在打开或保存时暂不可拖出。
普通窗口支持用鼠标直接拖动文件标题来移动整个窗口:点击松手才展开列表,移动超过系统拖动阈值则开始拖窗,不会同时展开列表。再次点击已展开列表的标题可关闭列表,按 Esc 可取消尚未开始的标题拖动。
拖动独立窗口的标题栏,把鼠标指针移到另一窗口左上角的文件标题处,目标标题会高亮,拖动窗口略微缩小并半透明;停留 300 毫秒后,文件列表自动向下展开,拖动窗口保持在目标窗口前面。列表中的蓝色横线指示插入位置,在文件项的上半部或下半部移动可选择插到该文件之前或之后。松手放到列表内,即可按横线位置将拖入窗口的全部文件连续加入,保留两边文件各自的顺序,并激活拖入窗口原本正在阅读的文件。离开目标、按 Esc 取消或松手在列表外,会恢复窗口外观并保留独立窗口。任一待合并文档正在打开或保存,或窗口正在处理编辑/关闭确认时,暂不合并。
拖动仅做标题与列表命中检测,悬停期间复用同一个列表面板;窗口缩小预览不改变文档倍率,也暂停发起新的 PDF 渲染和文字提取。合并直接移动内存中的会话,共享 PDF 与存储队列,不通过保存再重开来传递文档。窗口创建、首次菜单绘制与移入后的图形资源重建仍有一次性开销,复杂文档与多屏不同 DPI 的实际拖动体验需真机确认。
不同目录的 InkHorn EXE 各自管理窗口会话,开发版和便携分发版互不接管。系统 PDF 文件关联也需要指向本次使用的新版 EXE。
使用压感笔时,请在驱动中启用 Windows Ink。侧键临时橡皮依赖设备报告的按键标志;若侧键被驱动映射为快捷键,需检查驱动配置。
鼠标处于指针工具时,笔进入感应范围会自动切换到笔 1;若已手动选择其他绘图工具,则沿用所选工具。笔输入后重新使用鼠标或触摸板会回到指针工具。触摸板按 Windows 报告的输入来源识别;笔接触期间混入的鼠标消息不会切断笔画,具体驱动行为仍需真机确认。
按 Ctrl+F 或点击工具栏的放大镜打开右上角搜索框,输入关键词后自动逐页查找;框内 Ctrl+A 全选输入内容,Enter / Shift+Enter 或上下箭头切换匹配项,Esc 关闭。匹配计数位于输入区域右侧,箭头和关闭按钮与输入框垂直居中。支持普通英文换行、中文跨行和英文行末断词;显式空行及明显段间距按段落边界处理,不跨页拼接搜索。PDF 通常没有可靠的段落语义,多栏、特殊排版和缺失字符映射的文档可能需要调整关键词。
使用指针工具在正文上拖动可选择同一页的文字,按 Ctrl+C 复制(保留 PDF 文字层中的换行,并遵守文档复制权限)。已有笔记优先支持选中移动;在文字以外拖动可平移页面。扫描件及手写笔迹需要 OCR 才能作为正文搜索或复制,当前未提供 OCR。
文字按页在 PDF 后台队列读取,渲染与保存任务优先,正在书写时暂停安排下一页文字任务;不会在打开文档时建立全文索引。每个文档最多缓存 8 页文字;单页超过 100,000 字符会跳过,搜索最多显示 2,000 项,跳过页与结果上限会显示在搜索框中。
| 快捷键 | 操作 |
|---|---|
Ctrl+O |
打开 PDF |
Ctrl+S / Ctrl+Shift+S |
保存 / 另存为 |
Ctrl+W |
关闭当前文档 |
Ctrl+Tab / Ctrl+Shift+Tab |
切换到下一个 / 上一个文档 |
Ctrl+Z / Ctrl+Y |
撤销 / 重做 |
Ctrl+G |
输入页码 |
Ctrl+F |
打开正文搜索 |
搜索框内 Ctrl+A |
全选搜索内容 |
Enter / Shift+Enter、F3 / Shift+F3 |
搜索时跳到下一项 / 上一项 |
Ctrl+C |
复制选中的正文文字 |
Ctrl+滚轮 |
缩放页面 |
1 / 2 / 3 |
选择工具 / 笔 1 / 笔 2 |
E / H / S |
橡皮 / 荧光笔 / 形状 |
Delete |
删除选中对象 |
F11 |
切换全屏 |
Esc |
关闭面板、取消当前操作或退出全屏 |
设置、最近文件、阅读位置、恢复记录和诊断日志保存在 EXE 同级的 InkHorn-data/,因此程序所在目录需要可写。迁移个人状态时,将这个目录与 EXE 一起复制;分享已保存的批注时,只需分享 PDF。
保存采用临时文件写入、重开检查和替换原文件的流程。恢复记录用于异常退出后找回未保存笔记,不能代替主动保存;源 PDF 在外部发生变化时,程序会拒绝静默覆盖。应用内隐藏批注只影响当前查看,正常保存的 PDF 仍显示笔记。
- Windows x64。
- Visual Studio 或 Build Tools,安装“使用 C++ 的桌面开发”与 MSVC x64 工具链。当前开发环境使用 Visual Studio 2026。
- Windows SDK 10.0.28000.0:当前锁定的 PDFium 构建脚本会检查此版本。
- CMake 3.25 或更新版本、Ninja、Git for Windows、Python 3。
- PowerShell;CLion 可选。
正式 PDF 功能使用 MSVC ABI 的静态 PDFium,CLion 中应选择 Visual Studio / amd64 工具链。
在仓库根目录(即包含 CMakeLists.txt 的 InkHorn 目录)打开 PowerShell,执行:
.\scripts\Prepare-Pdfium.ps1 -CheckOnly
.\scripts\Prepare-Pdfium.ps1 -Jobs 4脚本根据 dependencies.lock.json 获取锁定版本的 PDFium 与 depot_tools,并从源码构建静态库。首次准备需要联网下载源码及工具,耗时和磁盘占用明显高于主程序构建。
依赖源码和缓存位于 third_party/source/,生成的 SDK 位于 third_party/pdfium/,两者都已加入忽略规则。依赖源码路径不能含空格;必要时通过 -DependencyDirectory 指定其他目录。需要代理时可传入 -Proxy http://127.0.0.1:端口;依赖已完整同步、仅重新编译时可使用 -SkipSync。
继续在仓库根目录执行:
.\scripts\Build.ps1 -Preset release -Test脚本会载入 Visual Studio amd64 开发环境,执行 CMake 配置、编译和 CTest。CMake 与 Ninja 可放入 PATH,也可通过 CLION_HOME 或脚本参数 -CMake、-Ninja 指定位置。
Release 程序输出到:
build/release/bin/InkHorn.exe
需要调试版本时执行:
.\scripts\Build.ps1 -Preset debug -TestDebug 输出到 build/clion-debug/。在 CLion 中打开仓库根目录的 CMakeLists.txt,选择 Visual Studio / amd64 工具链,加载 release 或 debug 预设并运行 InkHorn 目标。
在仓库根目录执行以下命令;cmake 需在 PATH 中,也可替换为其完整路径:
cmake --install build/release --prefix dist生成的 dist/InkHorn.exe 可单独复制运行。首次分发无需携带源码、构建缓存或个人 InkHorn-data/。
- 橡皮目前为整笔擦除;局部擦除、套索和多选尚未完成。
- 已有笔迹支持移动、删除,暂不支持缩放、旋转、复制或修改颜色与线宽。
- 正文选择目前限单页,不支持跨页拖选;扫描件 OCR、文字吸附批注高亮、目录与书签、干净副本导出、停顿拉直、双指缩放、完整无障碍支持及外来批注编辑尚未完成。
- 加密 PDF 的密码输入界面尚未实现;签名 PDF 或不允许批注的文档会进入保护性拒绝保存状态。
- 真机长时间书写、复杂 PDF 性能以及 Adobe / Edge 等阅读器的批注显示仍需继续验证,历史报告的偶发停顿尚不能视为完全解决。
2026-10-30 的开发验证记录为 Release、Debug 各 12 项自动检查通过,覆盖笔迹与历史、恢复存储、输入、窗口实例转交、PDF 保存重开、文字搜索与选择、界面冒烟及绘制质量等。新增检查覆盖独立子进程转交中文/空格路径、搜索框全选与控件对齐、工具栏搜索,以及拆分后保留笔迹历史和原窗口关闭后的生命周期。合成性能测试不代表复杂文档或真实手写设备到屏幕的端到端延迟;当前检出的代码可通过上述 -Test 命令重新验证。
2026-11-03 拖出与悬停合并调整后,Release、Debug 各 12 项自动检查通过。界面冒烟新增覆盖文件列表点击/拖出、300 毫秒展开、离开后重新计时、6% 缩小与透明度恢复、取消/列表外释放、多文档整体合并与忙碌文档保护、激活文件及历史/文字缓存保留、全屏拖出尺寸。Release 中已展开列表的 500 次命中更新合计约 0.06 毫秒,期间面板句柄保持不变;该测量不包含菜单首次绘制、系统窗口合成或真实鼠标拖动。视觉手感、多屏 DPI 与复杂 PDF 拖动仍待人工确认;本次未更新 dist。
随后针对实际拖动不触发的反馈,补充了原生移动循环内的定时回调与目标发现处理,避免透明/隐藏桌面覆盖层阻挡目标判断;增加标题点击/拖动区分与鼠标落点检查。检测定时器只在拖窗期间运行,指针未移动时不重复查找窗口;悬停到时单独触发展开。Release、Debug 各 12 项检查再次通过,新增覆盖真实可见窗口的层叠命中、源窗口遮挡目标、普通覆盖窗口阻止合并、透明覆盖层穿透、标题消息路由及新窗口锚点。当前环境的真实鼠标输入注入返回 0,因此未完成实际系统拖动的端到端自动验证,仍需人工确认触发及视觉效果;未更新 dist。
用户已确认能够触发合并,随后修正列表展开后目标窗口遮住拖动窗口的问题:合并菜单独立、不激活地显示,拖动窗口维持在目标窗口前面。增加蓝色插入横线,插入位置改变时才请求菜单重绘,横线通过一次直线绘制完成;松手后按指示位置迁移整个文件块。Release、Debug 各 12 项检查通过,新增实际窗口层级/前台窗口保持、普通菜单所有者不变、蓝线像素、空列表/首尾/中间插入及滚动坐标计算、取消后菜单清理和多文档实际插入顺序检查。真实鼠标拖动及跨屏 DPI 效果仍待人工复测,未更新 dist。
随后在两块真实显示器上复现副屏无法展开:任务栏窗口的外接矩形跨入副屏顶部,实际窗口区域却不包含文件标题位置。目标发现改为同时检查窗口区域,覆盖窄区域及复杂区域中的空洞;常规矩形窗口无需创建区域对象。补齐系统最大化拖出也使用 60% 尺寸,F11 全屏通过文件列表拖出的路径另有检查。搜索计数右侧内边距从 4 增至 10 个逻辑像素,保留计数显示宽度与文字输入间隔。Release、Debug 各 12 项检查通过,含主副屏真实坐标下的发现/展开/消息派发合并、窗口区域遮挡、最大化/F11 尺寸及搜索布局;当前两屏均为 150% 缩放,真实鼠标拖动、不同缩放比例的组合仍待人工复测,未更新 dist。
.
├── README.md
├── .gitignore
├── src/ # 原生界面、输入、笔迹、PDF 与存储
├── assets/ # 图标、应用资源与许可资源模板
├── scripts/ # 依赖准备、构建与 SVG 资源编译
├── tests/ # 核心、输入、存储与 PDF 等测试
├── docs/ # 本版上传步骤、Release 说明与便携使用说明
├── CMakeLists.txt
├── CMakePresets.json
├── dependencies.lock.json
├── THIRD_PARTY_NOTICES.txt
└── LICENSE # MIT 许可证
需要诊断笔侧键或触摸板输入时,按 Ctrl+Shift+D 启停记录,按 Ctrl+Shift+L 打开日志目录。日志包含 MOUSE_SOURCE=TOUCHPAD/MOUSE/PEN/TOUCH/UNAVAILABLE 及消息是否接收;位于 EXE 同级的 InkHorn-data/diagnostics/,属于本地运行数据,已忽略。
GitHub 仓库以本目录为根目录;只提交本目录下的源码和资源即可。构建目录、依赖缓存、个人运行数据和本地配置由 .gitignore 排除,发布 EXE 可单独上传到 GitHub Releases。
项目代码采用 MIT License。
PDFium 及其第三方依赖遵循各自的许可证,见 第三方声明。依赖准备脚本会收集实际检出的许可文本,正式构建将完整声明嵌入 EXE,可从应用功能菜单查看。
