pdf.js跳转bookmark链接后端生成
使用https://mozilla.github.io打开一个带bookmark的pdf文件,我们可以复制出一个bookmark的链接,通过这个链接访问pdf就可以直接跳转到对应的bookmark位置,
链接形式如下:
https://mozilla.github.io/web/viewer.html#%5B%7B%22num%22%3A93%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2Cnull%2C506.63281%2Cnull%5D
https://mozilla.github.io/web/viewer.html#[{"num":93,"gen":0},{"name":"XYZ"},null,506.63281,null]
后面的那么和浮点数还好理解,但是num和gen到底是怎么来的呢,很明显的可以观察出num并不是页码,
网上可以找到这样的分析,www.ishenping.com/ArtInfo/3499828.html+&cd=6&hl=zh-CN&ct=clnk&gl=sg
但是这篇文章里只是在前端进行了记录并没有说明num是如何产生的,
使用chrome可以观察追踪num的产生来源,可以看到下面这段代码
getObj ... if (Number.isInteger(buf1)) { if (Number.isInteger(this.buf1) && (0, _primitives.isCmd)(this.buf2, 'R')) { var ref = _primitives.Ref.get(buf1, this.buf1); this.shift(); this.shift(); return ref; } return buf1; }
还有这段代码
processXRefTable: function XRef_processXRefTable(parser) { if (!('tableState' in this)) { this.tableState = { entryNum: 0, streamPos: parser.lexer.stream.pos, parserBuf1: parser.buf1, parserBuf2: parser.buf2 }; } var obj = this.readXRefTable(parser); if (!(0, _primitives.isCmd)(obj, 'trailer')) { throw new _util.FormatError('Invalid XRef table: could not find trailer dictionary'); } var dict = parser.getObj();
即这个num是从解析pdf的xreftable得出的,
关于xreftable可以参考,https://blog.csdn.net/steve_cui/article/details/82152721
然后就是看后端的pdf库是否有相关的api了,aspose中似乎没有,不过pdfbox里面很容易找到相关的api
写一个简单的测试类dump一下相关的信息
public static void main(String[] args) { PDFParser parser = null; PDDocument pdDoc = null; try { RandomAccessRead accessRead = new RandomAccessFile(new File("xxxx.pdf"), "rw"); parser = new PDFParser(accessRead); parser.parse(); PDDocument doc = parser.getPDDocument(); logger.info("xsize="+doc.getDocument().getXrefTable().size()); doc.getDocument().getXrefTable().forEach((key, value) -> { logger.info("Key : " + key + " Value : " + value); }); PDDocumentOutline outline = doc.getDocumentCatalog().getDocumentOutline(); printBookmark(outline, ""); }catch (Exception e) { logger.info(e.getMessage()); } } public static void printBookmark(PDOutlineNode bookmark, String indentation) throws IOException{ PDOutlineItem current = bookmark.getFirstChild(); while (current != null){ COSDictionary cos = current.getCOSObject(); /*for ( Map.Entry<COSName, COSBase> entry : cos.entrySet() ) { logger.info("Key : " + entry.getKey() + " Value : " + entry.getValue()); }*/ List ret = new ArrayList<Object>(); COSArray arr = cos.getCOSArray(COSName.getPDFName("Dest")); COSObject ob = (COSObject)arr.get(0); Map<String,Object> ret0 = new HashMap<>(); ret0.put("num", ob.getObjectNumber()); ret0.put("gen", ob.getGenerationNumber()); ret.add(ret0); COSName oN = (COSName)arr.get(1); ret0 = new HashMap<>(); ret0.put("name", oN.getName()); ret.add(ret0); ret.add(null); if(arr.get(3) instanceof COSFloat) { COSFloat oF = (COSFloat)arr.get(3); ret.add(oF.floatValue()); }else { COSInteger oF = (COSInteger)arr.get(3); ret.add(oF.intValue()); } ret.add(null); logger.info(indentation + current.getTitle()+","+cos.getCOSArray(COSName.getPDFName("Dest")));//getCOSObject Parent logger.info(indentation + current.getTitle()+","+ret); printBookmark(current, indentation + " "); current = current.getNextSibling(); } }
//[{"num":78,"gen":0},{"name":"XYZ"},null,224.5144,null] //COSArray{[COSObject{78, 0}, COSName{XYZ}, COSNull{}, COSFloat{224.5144}, COSNull{}]}
上面的是dump出来的信息,很容易就可以比照出相似的结构;
由于我的后端pdf分析的主要工作是使用aspose完成的,所以我写了一个bookmarkPreLink(InputStream inStream)方法将pdf的bookmark title和
对应的[{"num":78,"gen":0},{"name":"XYZ"},null,224.5144,null]字符串缓存起来以便aspose输出解析结果时作为辅助使用;
备注:
pdf.js还带有一个namedest的hash参数跳转,不知道是不是由于bookmark中文还是,我的测试pdf的bookmark压根不是named,测试使用这个方法的时候没有成功,不过上面的方案已经满足了我的要求;
另外我还扩展了一下pdf.js的工作范围,使用aspose将word转成pdf,一样返回到bookmark位置的链接显示,有个问题是aspose word转pdf如果要带上bookmark的话需要设置,
PdfSaveOptions options = new PdfSaveOptions(); options.getOutlineOptions().setHeadingsOutlineLevels(3);//我这个是到word的3级标题都转转成pdf里的bookmark
可以参考一下https://forum.aspose.com/t/convert-word-bookmark-to-pdf-bookmark/57214+&cd=1&hl=zh-CN&ct=clnk&gl=sg
- 点赞
- 收藏
- 关注作者
评论(0)