这半个月来,一直都在做 PDF 编辑,因为是公司项目,并不打算多说,但多少总结一下。毕竟从一开始毫无头绪,到现在成品已出,中间阻力还是不算小的。
免费的 PDF 编辑库
之前有调研过,直到目前做成,pdfium 已经被证明可以很好的实现常规的 pdf 操作,高亮、下划线、波浪线、手写、印章、文字等。
关于底层库的编译,移步:浅谈 Android pdfium.so 编译
我看到有人说无法编译成功,这里请确保网络通畅,并且请多尝试,我大概下了一整天加一晚上,完成后再更新就简单很多了。
中间层 JNI
关于 SO 库与 上层交互的部分,我是使用的:
https://github.com/benjinus/android-support-pdfium
主要原因是,首先他的代码比较新,其次JNI代码写得不错,很多方法,传参的方式等等都能用这个 demo 中得知。(如果又恰好不是那么懂 JNI 的话,这个可以说是刚刚好,模仿就完事儿了)
上层 PDF阅读
关于阅读以及编辑的基础,也就是底子,这里选的是:
https://github.com/barteksc/AndroidPdfViewer
我将其 PDF SDK具体实现逻辑改成了上面的 android-support-pdfium,也证明了这个库在抽象上做得也很好,大概花了1个小时左右,就完成了替换。
PDF 的编辑
AndroidPdfViewer 这个库代码比较清晰,逻辑没有那么复杂,做编辑则直接继承其阅读的 View 进行扩展即可。当然,这只是开始,另外还有两个难点:
- 读懂渲染逻辑,做到编辑后实时渲染,笔记不闪烁。
- 读懂坐标与缩放逻辑,完成 pdf 与屏幕坐标的转换。
关于坐标转换,这里是三层:
- 具备缩放与位移的屏幕位置 => 原始的屏幕位置
- 手机原始的屏幕位置 => PDF 原始的页面位置
不知道好不好理解,就是 缩放和位移后的屏幕坐标 => 不缩放也不位移的屏幕坐标 => PDF 页面本身的坐标。
一些坑
在PDF 中,有些很骚的操作,比如有一个 AP 模式。
简单说明:有一个获取 annot_color 的接口,如果不将 AP 模式置空,则无法获取颜色,但一旦将 AP 置空,则会导致 annot 中添加的对象 object 丢失(也就是前一秒通过 annot_get_object_count 获取到的是有对象,在经过 annot_get_color后就变成了0),如果恰好在遍历每个 annot 时都进行了 color 的获取,那么在擦除其中一个 annot 的时候,将会丢失所有 annot 的对象,也就是屏幕上将不会存在任何能看见的东西。
解决办法:对于 SDK 中定义好的28种 annot,均可通过设置 AP 来进行颜色获取。但对与需要 appendObject 的 annot,不要使用设置 AP 来进行颜色获取,而是使用 annot_get_object 再 get_object_stroke/fill_color 进行获取。
接口主要参考:fpdf_annot.h 、fpdf_edit.h
一些关系
PDF 每一页上面每个元素都是 pageObject。添加的批注 annot 实际上也是 pageObject,对于 TEXT,IMAGE,PATH,RECT,可以依附于 STAMP、INK 类型的 annot,也可以直接加入 page 中作为一个 pageObject。
对于每一个对象的操作,都是依赖于 page 的,对于各元素的获取,都是先获取 pageObejectCount 或者 pageAnnotCount 进行getByIndex遍历拿到的。
如果玩熟了,就会很简单,否则就是一头雾水。
其他问题
- 一些类型比如 FREE_TEXT的正确使用方式 并不知道
- 画笔的笔头实现(或许是多个 object 拼接而成,很是复杂)
- 图像、音频、视频插入(实现方式未曾尝试)
主要参考实现
主要参考源码中的 TEST 用例:
https://pdfium.googlesource.com/pdfium/+/refs/heads/master/fpdfsdk/fpdf_annot_embeddertest.cpp
另外这里要特别感谢一位大佬以及他的项目,若不是他的逻辑让我看到了实现的可能,那我很可能没有勇气去做:
https://github.com/KnIfER/PolymPic
这个项目可以得到一些对于批注等操作的使用经验,比如文本传入底层需要追加”0″、文件的保存、又或者是编辑比如高亮等批注操作需要传入怎样的参数等等。另外,可以看这位大佬的博客:
https://www.jianshu.com/u/77921c0f8d4f
在这个项目中使用到的 API 应该有一百多个~ 很累。
本站由以下主机服务商提供服务支持:
ANDROIDER
博主可否分享一下如何把AndroidPdfViewer中的屏幕坐标转换成pdf中的坐标 ?
Mosaic-C
抱歉,这个不通用,没法分享,大概思路就是按 view 大小和 page size 去缩放,再使用 sdk 转换一次即可。
ANDROIDER
我看你也是用 AndroidPdfViewer 所以才这么问, 🙂 “大概思路就是按 view 大小和 page size 去缩放,再使用 sdk 转换一次即可” 这里可以稍微再细说一下么 ~
Mosaic-C
具体到 API 就是 sdk 中那几个 map 开头的方法(辨识度很高的),传入的是未缩放的坐标,对应得到的就是 page 中的坐标,另外,你可以参考文末PolymPic项目,其中基于放缩的下划线和标记逻辑中可以看到转换相关的调用。你多试试就知道了。
LinJ
总共花了多久 ? 我想知道大概的工作量
Mosaic-C
自行评估,非一人所为。如果你熟悉 pdfium 那只需要评估上层,如果要自己封底层,代码修改什么的,看你手速了~
dayang
嗨喽楼主 我个人也在开发一款 PDF 编辑的软件 走的也是Pdfium这条路 。 在看到这篇文章之前 已经走完了 “免费的 PDF 编辑库” “中间层 JNI” “ 上层 PDF阅读” 这三步。 开始深耕源码里面的 public api 文档,说实话 我也是一边看 一边评估时间, 感觉api 这条路 不是那么好走。 我看了你的博客 从你发了一篇介绍 PDFium 源码连接介绍的文章 到这篇文章似乎有个月的时间间隔, 我可否理解为 一个团队 做了四个月? 你们团队有几个人 可否告知下 , 因为我这边是我个人开发, 到时候测试和设计UI 和交互也要我自己来。。。。 感觉任务艰巨啊 啊啊啊啊 。 另外你们有没有想过一些比较走捷径的路子, 我有一些想法, 方便的话咱们交流下
Mosaic-C
邮件已回,注意查收。
dayang
似乎发表不了评论
Mosaic-C
根据互联网监管要求,评论均需审核,不会直接放出来。
yangang
https://github.com/yangtianzheng1/learnpdf.git 自己按照楼主的思路实现了一波,还有许多其他功能没来的及实现,大家可以参考。
aiden
楼主好,我用pdfium时,添加的annot都直接写入了/Page对象。按理说应该生成一个/Annot对象,然后/Page对象将引用这个/Annot对象才对。这里你有遇到过吗
Mosaic-C
没有细纠过,过去太久也已经记不太清了,抱歉~
苏三州
楼主好,想问下既然自己都实现了,为啥不考虑自己开发一个pdf编辑器出来卖呢?
Mosaic-C
因为我所能做的,可能只是其中的5%,后面还有 95% 的无法处理的问题。所以此行业任一能做出来的,都能养活一群人,甚至到上市,行业壁垒只有处于其中方能感受,我当初所处的公司最终不过做了一个满是广告的垃圾于是最后破产。