当前位置 博文首页 > mataodehtml的博客:PDF.js实现行定位功能(通过外部点击跳转到

    mataodehtml的博客:PDF.js实现行定位功能(通过外部点击跳转到

    作者:[db:作者] 时间:2021-08-10 22:14

    我们公司之前的需要实现的功能? pdf上传、定位、浏览功能,这块主要我负责,就像上一个博客写的一样,遇到很多很多的问题,主要用到的js就是pdf.js,实现对上传pdf的浏览,上一个博客记录的是跨域问题(跨域问题是很重要的一个,因为一般文件服务器和项目服务器是分开的),下面记录一下如果实现的行定位功能。

    ?????? 本来我是不打算用行定位的,因为之前考虑了一下是完全没有头绪的,因为pdf.js主要就是点击缩略图定位到对应的页码实现页码跳转的。因为用户点击不可能点击的是pdf.js内部的按钮,需要在外部添加按钮(相当于锚点功能)。主要功能点:锚点设置 和 锚点定位。

    ?????? 下面我就只讲行定位的方法:

    ??????? 思路:pdf.js有个目录功能,点击目录中的标题就实现了行定位,我就是受这个启发才去思考的实现行定位。首页通过页面F12去查看目录定位的连接,发现是一串编码(不知道啥格式),我就复制出来去网页中转码过来,发现格式如下:

    #[{"num":23,"gen":0},{"name":"XYZ"},74,769,0]
    #[{"num":46,"gen":0},{"name":"XYZ"},74,442,0]

    这是其中二个标题对应的编码转换后的代码,我当时发现就二个地方是不同的,一个就是 num的值,另一个就是后面倒数第二个的数字,接下来就要去找源码了,一个一个的console,终于在viewer.js的9285行找到了端倪(由于每个版本的行数不一样,下面是我复制的那个js方法,因为加密电脑不能截图,只能给代码了):

    key: '_updateLocation',
    value: function _updateLocation(firstPage) {
        var currentScale = this._currentScale;
        var currentScaleValue = this._currentScaleValue;
        var normalizedScaleValue = parseFloat(currentScaleValue)
        ...........

    这个方法console方法当你滚动页面的时候就会走这个方法,这个方法中的后面有几个非常重要的参数

     this._location = {
         pageNumber: pageNumber,
         scale: normalizedScaleValue,
         top: intTop,
         left: intLeft,
         rotation: this._pagesRotation,
         pdfOpenParams: pdfOpenParams
    };

    通过打印能够发现其中的pageNumber就是当前页码,intTop 就是发现那个编码中的变化数字中的倒数第二个参数,解释为高度(哈哈,现在才知道不是记录的行,而是记录的当前页码的高度实现的“行定位”),现在第一个参数有了,主要目标就是那个num的值了,跟我们在页面看到的值完全不一样的,我就猜想一定是一个json或者数组啥的通过key value对应到的页码,然后我就通过这个方法顺藤摸瓜,找到了大本营:

    {
        key: 'cachePageRef',
        value: function cachePageRef(pageNum, pageRef) {
         if (!pageRef) {
              return;
          }
        var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
         this._pagesRefCache[refStr] = pageNum;
         
     }
      }, {
        key: '_cachedPageNumber',
        value: function _cachedPageNumber(pageRef) {
        var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
        return this._pagesRefCache && this._pagesRefCache[refStr] || null;
    }
      },

    我这边大概在7139行(viewer.js),这二个方法狠狠重要,一个是页码(下面),一个是创建的key-value格式的编码(上面一个),通过console得知,当你加载pdf文件的时候就会去调用上面这个方法,将页码和编码对应(对应格式是“27 0 R”-“4”,4是value就是页码,前面是key就是编码)。

    ?????? 到目前为止获取就有了(拨开云雾见青天)的感觉,下面就是获取了,上面说过9千多行那个滚动页面就会走的那个方法,那几个参数,其中的intTop就是我们需要的高度的值,页码需要通过那个key-value获取,所以我开始修改了源码。

    ?????? 我们需要实时的获取当前页的数据

     this._location = {
         pageNumber: pageNumber,
         scale: normalizedScaleValue,
         top: intTop,
         left: intLeft,
         rotation: this._pagesRotation,
         pdfOpenParams: pdfOpenParams
    };
    //下方是我添加的代码(这里你需要在viewer.html页面中加入二个隐藏的input,id为page_top和page_cache)
        var page_top = document.getElementById("page_top");
        page_top.value = this._location.top;
        var page_cache = document.getElementById("page_cache");
        page_cache.value = this.getPageCache(this._location.pageNumber);

    最后一行那个getPageCache方法是我修改的源码:

     this._location = {
         pageNumber: pageNumber,
         scale: normalizedScaleValue,
         top: intTop,
         left: intLeft,
         rotation: this._pagesRotation,
         pdfOpenParams: pdfOpenParams
    };
    //下方是我添加的代码(这里你需要在viewer.html页面中加入二个隐藏的input,id为page_top和page_cache)
        var page_top = document.getElementById("page_top");
        page_top.value = this._location.top;
        var page_cache = document.getElementById("page_cache");
        page_cache.value = this.getPageCache(this._location.pageNumber);
     }
      },{//下面是我添加的方法
    //这个方法用来获取对应num的页码cache
        key:'getPageCache',
        value:function getPageCache(pageNum){
        //循环结果(这里的page_cache是一个数组,下面细说)
        var str = JSON.stringify(page_cache);
        //	console.log("格式为:"+typeof(JSON.parse(str)))
    //	console.log("json的数据:"+str)
        for(var key in JSON.parse(str)){
            if(JSON.parse(str)[key]==pageNum){
                    return key;
    	}
    				}
    	}

    我们上面有说那个key-value格式的数据,因为不是一个代码块中,所在我在js的最上方加上了一个全局变量page_cache

    //定义全局变量   去接受页码对应编码值
    var page_cache = [];

    然后在页面加载pdf的时候也给这个数组赋值一下:

    {
        key: 'cachePageRef',
        value: function cachePageRef(pageNum, pageRef) {
         if (!pageRef) {
              return;
          }
        var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
         this._pagesRefCache[refStr] = pageNum;
    
           //当赋值完毕之后,给全局赋值,这样我就获取到这个编码了
           page_cache =  this._pagesRefCache;
    
         
     }
      }, {
        key: '_cachedPageNumber',
        value: function _cachedPageNumber(pageRef) {
        var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
        return this._pagesRefCache && this._pagesRefCache[refStr] || null;
    }
      },

    这样就如上面,通过这个全局去获取对应的编码值。(整个获取就到此结束了(设置锚点))

    补充一下:有的人可能不会获取iframe下的文件,发一下我的这个:

    //这个是子页面
    <iframe id="iframe01" name='iframe01' style="width:100%;height:100%;" 
    src='http://服务器/PDF测试/js/PDF.js/web/viewer.html?file=http://服务器/PDF测试/src/demo.pdf&page=1&top=0&zoom=1'></iframe>
    
    //js
    top = $("#iframe01").contents().find("#page_top").val();
    cache = $("#iframe01").contents().find("#page_cache").val().split(" ")[0];
    console.log("父页面获取的top:"+$("#iframe01").contents().find("#page_top").val());
    console.log("父页面获取的页码编码:"+$("#iframe01").contents().find("#page_cache").val());

    这样就获取了那个编码中的num和那个高度的值了。

    ?

    下面就是实现定位了(锚点点击定位):直接上代码

    //html
    <button class='set1'>点击定位1</button>
    <button hidden="hidden" class='set2'>点击定位2</button>
    
    
    //jq
    
    //这个才是定位
    $(".set1").click(function(){
        $(".set2").click();
        //始终在页面只有一个是模拟添加的a标签
        if($("#iframe01").contents().find(".addLink").length!=0){
            $("#iframe01").contents().find(".addLink").remove();
        }
        //创建一个模拟目录中的标题a标签
        str = "<a hidden:'hidden'  href='"
            + '#[{"num":'+cache+',"gen":0},{"name":"XYZ"},74,'+top+',0]'
            + "'class='internalLink addLink' id='p37318'><p></p></a>";
        //添加到子页面中
        //先判断是否有   没有再添加   有就不添加
        if($("#iframe01").contents().find("#p37318").length==0){
            $("#iframe01").contents().find("body").append(str);
        }
        //模拟目录a标签的点击事件
        $("#iframe01").contents().find("#p37318").find("p")[0].click();
    })
    
    //这个是假的
    $(".set2").click(function(){
        if($("#iframe01").contents().find(".addLink").length!=0){
            $("#iframe01").contents().find(".addLink").remove();
        }
        str = "<a  href='"
            + '#[{"num":17,"gen":0},{"name":"XYZ"},74,192,0]'
            + "'class='internalLink addLink' id='p17192'><p>GG</p></a>";
        if($("#iframe01").contents().find("#p17192").length==0){
            $("#iframe01").contents().find("body").append(str);
        }
        $("#iframe01").contents().find("#p17192").
        find("p")[0].click();
    })

    思路:发现目录的a标签的事件和class,后台传的数据有那二个参数,创建一个类似的a标签,将参数放进去,然后放到子页面中,点击的时候去找到我们新增的a标签,触发点击,实现跳转,这里的set1是跳转的。因为在我的本地一个点击当你滚动的时候,再次点击就不行了,失效。所以又加了一个隐藏的点击,每次点击先触发一下隐藏的点击。发现可用。

    ?

    有很多人都遇到这种问题了。也不用去找我了,通过需要的内容设置定位点以及跳转到定位点我在github写了一个小demo,有兴趣的可以下载试试https://github.com/Ma-Tao007/pdfdemo2

    ?

    整个模拟行定位实现完毕。用了我一整天的时间,但是最后的结果还是让我极其欣慰的。由于发不了项目源码,所以只能在github上传了一个简单的demo,有兴趣的可以下载看看。有问题的可以留言。

    cs