做了个pdf插件

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){}

 

上一篇:WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据


下一篇:jsPDF 添加 中文字体 分页打印 移除空白页