Pdf.js: 在 iFrame 中加载时无法打印 PDF

创建于 2014-10-11  ·  21评论  ·  资料来源: mozilla/pdf.js

我正在尝试使用 Javascript 来聚焦和打印在我动态放置到 DOM 中的 iframe 中加载的 PDF 文件。 这个问题在 Firefox 中特别出现在我身上。

我的代码类似于以下内容:

<iframe name="printer_frame" id="printer_frame" src="http://domain.com/media/eparcel_label_1413020567.pdf"></iframe>
window.frames['printer_frame'].window.focus();
window.frames['printer_frame'].window.print();

我收到以下错误:

错误:访问属性“打印”的权限被拒绝

我的研究告诉我这应该可行,进一步阅读使我相信这可能是一个错误。 任何帮助,将不胜感激。

编辑

我通过将 PDF 文件替换为其中第一页的 PNG 格式的屏幕截图来测试该功能,并且打印功能正常工作。

编辑

进一步测试。 将 pdfjs 添加到我的 chrome 安装中,并尝试使用上面相同的代码进行打印。 同样的错误:

SecurityError: 阻止具有源“ http://domain.com ”的框架访问跨域框架。

code: 18
message: "Blocked a frame with origin "http://domain.com" from accessing a cross-origin frame."
name: "SecurityError"
stack:
"Error: Blocked a frame with origin "http://domain.com" from accessing a cross-origin frame.
    at Error (native)
    at <anonymous>:2:34
    at Object.InjectedScript._evaluateOn (<anonymous>:730:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:669:52)
    at Object.InjectedScript.evaluate (<anonymous>:581:21)"

我应该明确表示 PDF 文件是在同一个域上加载的。

3-upstream 4-printing

最有用的评论

我想我知道其中的原因,至少在 Firefox 中是这样。 在上面的一个简单示例中,如果您使用 Firefox DevTools 检查主页和 iframe 中的document.domain ,我发现document.domainpdf.js PDF.js 渲染器——因此浏览器的跨域限制正在发挥作用。

此外,如果我从源代码构建 PDF.js,并将 iframe 设置为web/viewer.html?file= ,然后调用告诉 iframe 进行打印(如在 OP 中),它工作正常。

PDF.js(至少在 Firefox 中运行时)有没有办法接受来自其他窗口的消息,可能是通过类似window.postMessage的方式? (我从未使用过它,所以我不知道它是否适合这个用例。)

所有21条评论

可能是错误 874200的副本。

对于任何偶然发现此问题并希望解决此问题的人(考虑到此问题已经存在一年多没有修复),我最终在 PHP 中使用 ImageMagick 和 GhostScript 在服务器上生成 PDF 的图像,然后在 json 响应中返回这些图像 URL,打印结果是微不足道的。

但是,了解我,可能有更好,更简单的方法来做到这一点......

about:config我设置 pdfjs.disabled=true 但 pdf 开始下载我想如果它使用 plugin.disable_full_page_plugin_for_types = application/pdf 它无法下载 pdf 并在查看器中看到

我想我知道其中的原因,至少在 Firefox 中是这样。 在上面的一个简单示例中,如果您使用 Firefox DevTools 检查主页和 iframe 中的document.domain ,我发现document.domainpdf.js PDF.js 渲染器——因此浏览器的跨域限制正在发挥作用。

此外,如果我从源代码构建 PDF.js,并将 iframe 设置为web/viewer.html?file= ,然后调用告诉 iframe 进行打印(如在 OP 中),它工作正常。

PDF.js(至少在 Firefox 中运行时)有没有办法接受来自其他窗口的消息,可能是通过类似window.postMessage的方式? (我从未使用过它,所以我不知道它是否适合这个用例。)

另一个观察结果:我希望通过 $ iframe上的onload事件触发打印,但 PDF 还不一定被渲染。 不确定是否有可靠的方法来检查?

我看到了#5765,但它似乎只指如果您直接运行 PDF.js 而不是内置的 Firefox 版本时可以挂钩的事件?

我很困惑,这是一个像使用javascript触发PDF文件打印这样简单的问题。 这个问题现在似乎可以追溯到两年左右(可能更长)。

有没有人找到_client side_解决方案? 我看到了@Petce解决方案,但该解决方案对我不起作用。

不要屏住呼吸@Lynda333 ,对这个问题的支持是出了名的反应迟钝。 在我看来,您真正唯一的选择是在功能上找到合适的折衷方案。 如果您告诉我们更多关于您自己和其他人的情况的信息,可能会提出一种可能的解决方法。

@Petce - 当我看到这个问题多久以前开始时,我不会。 不包括这种能力似乎很荒谬。 我与客户进行了交谈,并打算改变我们对该网站的处理方式。 1) 他们可以下载 PDF 和 2) 我正在用 HTML 重新创建一些 PDF 内容并让用户选择要打印的内容。 创建需要更多时间,但它会起作用。

有人可以打印嵌入式 iframe 吗? 我只是安装了最后一个扩展版本,我得到错误权限被拒绝,我也无法在正常的 pdf 访问中查看 pdf 文件。

使用此提交中的代码(以及 pdfjs 数据解析器本身),您可以将 pdf 重写为临时 blob 并添加打印指令: https ://github.com/mozilla/pdf.js/pull/6190/commits
创建一个以 blob 作为源的 iframe,它将开始打印(请注意,除了不遵循打印指令之外,ie 根本不会将 blob 加载到 iframe 中)。
无论如何,如果您还没有运行自定义版本的查看器,这将非常复杂。
或者,您可以在服务器或任何可以编辑 pdf 数据的地方添加打印指令。

有趣的是只有firefox需要它......这个问题已经报告了三年,firefox是唯一一个抱怨访问iframe contentWindow的浏览器:看起来现在没有人真正关心打印,这可能是一件好事。

这似乎对我有用

        setTimeout(function(){
            this.printIframeRef.contentWindow.document.getElementById('secondaryPrint').click()
        }.bind(this), 3000)

我找不到任何事件或方法来连接查看器,但如果您触发点击,即使它是打印按钮(我使用的是 1.4.20 版),那么它似乎可以打印。 settimeout 允许查看器有时间加载等

您可以在 Firefox 上访问 contentWindow 吗? 你不会遇到安全问题吗?

@paolocaminiti我正在加载来自同一域的所有文件。 它似乎对我有用。 我在 Windows 上使用过 IE 10 和 11、IE Edge、Chrome。 和 Ubuntu 14.04 上的 FireFox 38。

    handlePrintClick() {
        blurComponentRef(this.printButtonRef)
        this.printIframeRef.contentWindow.document.getElementById('secondaryPrint').click()
    }

    handlePrintLoad() {
        this.printIframeRef.contentWindow.addEventListener("documentload", function(){
            this.setState({canPrint: true})
        }.bind(this));
    }

我明白了,您正在从远程再次加载 pdf,它会产生相同的域。
在 chrome/opera/safari 我使用这个:

function directPrint () {
  PDFViewerApplication.pdfDocument.getData().then(function(res) {
    var b = URL.createObjectURL(new Blob([res], { type: 'application/pdf' }))
    var printFrame = document.getElementById('print-frame')
    if (!printFrame) {
      printFrame = document.createElement('iframe')
      printFrame.id = 'print-frame'
      printFrame.src = b
      document.body.appendChild(printFrame)
    }
    setTimeout(function () {
      printFrame.contentWindow.print()
      // ...dispose iframe and blob.
    }, 0)
  })
}

只需使用本地数据创建一个 blob,然后调用 print,setTimeout 0 允许附加 iframe。

不幸的是,Firefox 不允许带有 blob 的 contentWindow,但是带有打印指令的 blob 将自行打印。

对于即没有使用本地数据的解决方案,但也许在一个拥有的服务器上拥有一个代理来路由 pdfs 数据将允许所有人成为同一个域并应用您的代码。

你好,

“res”是什么意思? 而 PDFViewerApplication 来自 PDF.js?

谢谢,

是的 PDFViewerApplication 是由 pdfjs 的 PDFViewer 构建提供的,源代码位于 /web 目录下,您应该在那里修改并创建一个新构建,以便在主存储库更新时更容易合并...

res 只是 getData 传回的参数,应该是 pdf 文件的字节表示,这里用来构建一个本地 blob url 以在没有进一步网络请求的情况下加载。

如果您想要类似上面的代码,您可能需要稍微清理一下:

function directPrint () {
  var printFrame = document.getElementById('print-frame')
  if (printFrame) {
    printFrame.contentWindow.print()
  } else {
    PDFViewerApplication.pdfDocument.getData().then(function(res) {
      var src = URL.createObjectURL(new Blob([res], { type: 'application/pdf' }))
      printFrame = document.createElement('iframe')
      printFrame.id = 'print-frame'
      printFrame.style.display = 'none'
      printFrame.src = src
      document.body.appendChild(printFrame)
      setTimeout(function () {
        printFrame.contentWindow.print()
      }, 0)
    })
  }
}

最后,我选择不处理 iframe,因为事实证明它很难理解何时执行此操作,我只是将它留在那里并在每次用户想要打印时重复使用它,假设 pdf 不能在两者之间更改。

希望能帮助到你。

@thenewguy只是为了向其他读者澄清这个线程,我们似乎在做完全不同的事情:
我正在尝试使用本机浏览器 pdf 查看器以全质量打印(我发现无法访问 ie 和 edge 上的 contentWindow,无论使用相同的域)。
您正在 iframe 中加载 PDFjs 查看器,可能是可见的,并控制它从主机应用程序打印。

这很有趣,因为我试图实现@paolocaminiti将脚本插入到我的 iframe 并将其作为函数调用,但现在我得到“offsetParent 未设置 - 无法滚动”😢。 我将使用 chrome kiosk 打印并忘记了这一点。

到目前为止,还没有提出解决方案

我是否还需要服务器端解决方案,或者在这个主题上有什么进展? 是否可以使用 pdf-js 中的PDFPrintService ? 这会是一个功能性的解决方法吗? 如果是,如何在不必使用整个 pdf-viewer 的情况下使用此服务?

此页面是否有帮助?
0 / 5 - 0 等级