Pdf这玩意是不太想碰的,Acrobat javascript更是不想碰。但是有个需求,似乎用js做比较合理,所以只好查资料,花了不少时间总算做好了。
需求是:打开pdf阅读时,将文件名和路径记录到某个文件(姑且称为索引文件)。这样,读了一半,以后继续阅读时,只要打开那个文件,点击某个链接,就打开读了一半的pdf,并且跳到上次停下时的位置。
主要是因为硬盘上的电子书太多,一个目录下就有几千本书,好容易找到一本想看的书,关闭后,下次要看又要找一次。虽然输入关键词,用windows search也不难。但对于我这样的懒人来说,这样都觉得麻烦(当然也和穷人用破机器,windows search比较慢有关)。uvz格式的(即超星pdg文件的zip包)电子书,只要打开任意一个uvz文件,然后利用其中的书签功能,可以从一个文件中打开其他的uvz文件并跳到相应的位置。而pdf文件就没有这样的功能。uvz文件书签功能的设计思路给了我一点启发。
固然,用很多语言都可以实现。但是,希望打开pdf时,“一键”实现,似乎用Acrobat javascript做插件最为方便,因为不需要打开其他程序(这我也觉得麻烦)。
设计思路就是点击插件时,自动打开索引文件(也做成pdf),先检查是否记录已存在,若没有,则添加到文件末尾。
打开文件的功能(就是前面说的“点击某个链接”),先是考虑用Link实现。在Acrobat的Javascript Debugger Console下调试可以,不料做好的Link,点击死活都没有反应。官方的API文档也确实没有在Link里用app.openDoc的例子。只好试着换成Button,没想到就可以了。Link不可以,Button可以,好像没什么道理。
接下来的问题是如何添加到文件末尾。Doc对象有个numFields属性,开始以为this.getNthFieldName(this.numFields-1)就可以了,没想到Field不是按添加先后排序的,而是按name的字母顺序排列。只好遍历页面上的所有Field,取Y坐标值最小的那个作为最后一个添加的Field。
调试时又发现一个问题。如果pdf尚未打开,那么在openDoc之后马上取Field和Annotation,有可能有的尚未加载,取不到。没有好的方法,只好先检测app.activeDocs里是否有索引文件,若没有,则一边打开,一边app.alert,提示一下等文件打开后重试。
接下来,考虑如何实现跳到上次阅读位置的功能。发现无法利用Bookmark,只能先在pdf里手工做个destination(其实也相当于书签的功能),然后用openDoc({cPath: "foo.pdf", cDest: "bar"})的方法来实现。
还有个问题,代码里的索引文件路径是硬编码写死的,虽然改代码不难,但总归不好(从设计的角度来说的,从使用的角度说其实没什么问题,因为只是我自己用)。用js读文本文件很麻烦,先要建个Field,然后将文本文件作为附件读进来,然后再删除Field,等于是要修改当前pdf,感觉不好。最后用了个pdf作为配置文件,加了个文本框以供输入索引文件的路径,然后把它放在Acrobat的安装目录下(因为可以用app.getPath("app")来取到,而不用硬编码路径)。这样差不多就大功告成了。
对Adobe素无好感,现在感觉依旧。做了这个插件,对于Acroabat javascript API和pdf,多少总加深了一点理解吧。
主要代码如下(图标是一串“乱码”,大概是base64之类的编码,这里就省略了)
if((app.viewerType == "Reader") || (app.viewerVersion < 7)) { app.alert("The Register Pdf Position Tool Requires Adobe Acrobat Professional/Standard version 6.0 or Later"); throw "Invalid Viewer"; } //<JSCodeSnippet name="ImageData7"> var icNewDocSm = "(乱码)"; var oIconNewDocument = {count: 0, width: 16, height: 16, read: function(nBytes){return icNewDocSm.slice(this.count, this.count += nBytes);}}; var DoCmdRegisterPdfPosition = app.trustedFunction(function() { app.beginPriv(); //read setting var settingDoc = app.openDoc(app.getPath("app") + "registerPdfPositionSetting.pdf"); var spath = settingDoc.getField("txtPdfPath").value; spath = "/" + spath.replace(/\\/g, "/").replace(":",""); //open doc var myDoc = null; var isOpen = false; for (var i = 0; i < app.activeDocs.length; i++) { if (app.activeDocs[i].path == spath) { isOpen = true; myDoc = app.activeDocs[i]; break; } } if (isOpen == false) { try { myDoc = app.openDoc(spath); } catch (e) { myDoc = app.newDoc(); try { myDoc.saveAs({cPath: spath, bPromptToOverwrite: true}); } catch (e) {}; } app.alert('Please wait until the index pdf is fully loaded, and try again.'); return; } //check if index exists var btn = myDoc.getField(this.documentFileName); if (btn != null) return; var b = myDoc.getPageBox("Crop", myDoc.numPages - 1); var pageWidth = b[2]-b[0]; var pageHeight = b[1]-b[3]; var upperLeftX = 20; var upperLeftY = pageHeight - 40; var lowerRightX = pageWidth - 45; var lowerRightY = pageHeight - 55; //get last field var annots = myDoc.getAnnots({nPage:myDoc.numPages-1}); if (annots && annots.length > 0) { var minY = pageHeight; for (var i = 0; i < annots.length; i++) { if (annots[i].rect[3] < minY) { minY = annots[i].rect[3]; } } if (minY - 60 < 0) {//margin 40, height 15, space 5 myDoc.newPage(myDoc.numPages, pageWidth, pageHeight); } else { upperLeftY = minY - 5; lowerRightY = upperLeftY - 15; } } var f1 = myDoc.addField(this.documentFileName + '_label', "text", myDoc.numPages-1, [upperLeftX,upperLeftY, lowerRightX, lowerRightY]); f1.value = this.documentFileName; f1.textColor = color.black; f1.textSize = 0; // Now construct a button field with a right arrow from ZapfDingbats 这一段是从api文档里抄的 var f = myDoc.addField(this.documentFileName, "button", myDoc.numPages-1, [lowerRightX+5,upperLeftY - 2, lowerRightX+25, lowerRightY + 2]); f.setAction("MouseUp", "app.openInPlace=true;app.openDoc({cPath:'" + this.path + "',cDest:'here'});"); f.delay = true; f.borderStyle = border.s; f.highlight = "push"; f.textSize = 0; // Auto-sized f.textColor = color.blue; f.fillColor = color.ltGray; f.textFont = font.ZapfD; f.buttonSetCaption("\341") // A right arrow f.delay = false; var annot = myDoc.addAnnot({type: "Line", page: myDoc.numPages-1, points:[[upperLeftX, lowerRightY -2],[lowerRightX+25, lowerRightY - 2]]}); annot.strokeColor = color.gray; myDoc.saveAs({cPath: spath, bPromptToOverwrite: false}); app.alert('done'); app.endPriv(); }); var oRegisterPdfPosition = { cName: "RegisterPdfPosition", cExec: "DoCmdRegisterPdfPosition()", cEnable: "event.rc = true", cMarked: "event.rc = false", cTooltext: "Register Pdf Position", cLabel: "Register Pdf Position", oIcon: oIconNewDocument }; try{app.removeToolButton("RegisterPdfPosition");}catch(e){} try{app.addToolButton(oRegisterPdfPosition);}catch(e){}