pdfjs 渲染页面上下颠倒和内容尺寸异常

背景

在项目中使用pdfjs渲染页面,出现缩放和dpr异常,表现为文档内容等比缩放到页面左上部分,且上下颠倒。

问题排查

问题出现之前,使用的渲染方式是getDocument完成后,逐页面循环调用page.render,直到所有页面都加载完成。为了节省资源,引入 Intersection Observer以后,发现此问题出现。

查找资料得知,出现此问题的原因99%都是因为并行操作canvas导致的。

现在逻辑是每次收到Observer回调,就调一次渲染单页(先设置canvas宽高,再调用page.render)。Intersection Observer有时会反复针对某一页面进行回调,虽然我们已经使用了一个set进行标记加锁防止重复渲染,但是只保护了page.render的调用,漏掉了设置canvas尺寸的逻辑,这就导致了在page.render过程中依然可以设置了canvas宽高的情况,也就出现了开头这种异常情况。将canvas尺寸修改也加到保护范围后,问题解决。

解决方法

想办法确保不要同时操纵canvas,包括page.render和修改canvas height/width等。此处使用的方案是一个set,渲染中将pageNum放到set中,每次请求渲染前检查page是否在set中。注意,这只是个简单的实现,极端情况下依然可能存在竞争的情况。

伪代码如下:

const busyPageSet = new Set();
const renderPage = async (pageNum) => {
  // 关键代码,判断当前页面是否被标记为正在渲染状态
  if (busyPageSet.has(pageNum)) {
    return;
  }
  busyPageSet.add(pageNum); // 标记当前页面为渲染状态
  const page = await pdfDoc.getPage(pageNum);
  const viewport = page.getViewport(); // 并处理scale和dpr等缩放逻辑
  const canvas = getCanvasRef(pageNum); // 获取canvas
  canvas.width = ...; // 设置canvas高度
  await page.render({context: ..., viewport: ...}).promise; // 调用pdfjs渲染canvas
  busyPageSet.delete(pageNum); // 解除标记当前页面的渲染状态
}

补充

当重复调用page.render时,pdfjs会在控制台输出告警。但是若渲染时进行了canvas的尺寸修改、直接调用draw命令,pdfjs不会发出告警。

本次问题主要原因是加锁保护时,设置的保护范围不全面,漏掉了其他canvas操作,而这又不会触发告警,导致排查的时间有点长,值得吸取经验。

参考资料

https://stackoverflow.com/questions/44885973/pdf-js-document-is-presented-upside-down-randomly
https://github.com/mozilla/pdf.js/issues/11277#issuecomment-546498526

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容