pdf.js跳转bookmark链接后端生成

举报
Amrf 发表于 2019/09/27 15:29:30 2019/09/27
【摘要】 使用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%2...

使用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



【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。