Pdf.js: Não é possível imprimir PDF quando carregado no iFrame

Criado em 11 out. 2014  ·  21Comentários  ·  Fonte: mozilla/pdf.js

Estou tentando usar Javascript para focar e imprimir um arquivo PDF carregado em um iframe que coloquei dinamicamente no DOM. Este problema está ocorrendo especificamente para mim no Firefox.

Meu código se parece com o seguinte:

<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();

Eu recebo o seguinte erro:

Erro: Permissão negada para acessar a propriedade 'print'

Minha pesquisa está me dizendo que isso deve funcionar, leituras adicionais me levaram a acreditar que isso pode ser um bug. Qualquer ajuda seria apreciada.

Editar

Testei a funcionalidade substituindo o arquivo PDF por uma captura de tela no formato PNG da primeira página dentro dele e a funcionalidade de impressão funcionou.

Editar

Teste aprofundado. Adicionei pdfjs à minha instalação do chrome e tentei imprimir com o mesmo código acima. mesmo erro:

SecurityError: Bloqueou um frame com origem " http://domain.com " de acessar um frame de origem cruzada.

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)"

Devo deixar claro que o arquivo PDF está sendo carregado no mesmo domínio.

3-upstream 4-printing

Comentários muito úteis

Acho que sei o motivo disso, pelo menos no Firefox. Em um exemplo simples como o acima, se você usar o Firefox DevTools para examinar document.domain na página principal e no iframe, descobri que document.domain é pdf.js para o renderizador PDF.js, para que as restrições de origem cruzada do navegador sejam ativadas.

Além disso, se eu construísse o PDF.js a partir da fonte e definisse meu iframe como web/viewer.html?file= , chamando o iframe para imprimir (como no OP), funcionaria bem.

Existe uma maneira do PDF.js (pelo menos quando executado no Firefox) aceitar mensagens de outras janelas, talvez por meio de algo como window.postMessage ? (Eu nunca usei, então não sei se é apropriado para este caso de uso ou não.)

Todos 21 comentários

Possivelmente uma duplicata do bug 874200 .

Para quem se deparar com esse problema e gostaria de uma possível solução (considerando que esse problema já existe há mais de um ano sem correção), acabei usando o ImageMagick em PHP junto com o GhostScript para gerar imagens do PDF no servidor , em seguida, serviu esses URLs de imagem de volta em uma resposta json, imprimindo o resultado disso foi trivial.

Conhecendo-me, no entanto, provavelmente há uma maneira melhor e mais fácil de fazer isso ...

em about:config eu defino pdfjs.disabled=true mas o pdf inicia o download, suponho que se usar plugin.disable_full_page_plugin_for_types = application/pdf não pode baixar pdf e ver no visualizador

Acho que sei o motivo disso, pelo menos no Firefox. Em um exemplo simples como o acima, se você usar o Firefox DevTools para examinar document.domain na página principal e no iframe, descobri que document.domain é pdf.js para o renderizador PDF.js, para que as restrições de origem cruzada do navegador sejam ativadas.

Além disso, se eu construísse o PDF.js a partir da fonte e definisse meu iframe como web/viewer.html?file= , chamando o iframe para imprimir (como no OP), funcionaria bem.

Existe uma maneira do PDF.js (pelo menos quando executado no Firefox) aceitar mensagens de outras janelas, talvez por meio de algo como window.postMessage ? (Eu nunca usei, então não sei se é apropriado para este caso de uso ou não.)

Outra observação: eu esperava acionar a impressão por meio do evento onload no iframe , mas o PDF ainda não foi necessariamente renderizado. Não tem certeza se existe uma maneira confiável de verificar isso?

Eu vi o nº 5765, mas parece que isso se refere apenas a eventos nos quais você poderia se conectar se estivesse executando o PDF.js diretamente e não a versão integrada do Firefox?

Estou perplexo que isso seja um problema para fazer algo tão simples quanto acionar um arquivo PDF para imprimir usando javascript. Este problema parece datar de dois ou mais anos (possivelmente mais) agora.

Alguém encontrou uma solução _client side_? Eu vi a solução @Petce , mas essa solução não funcionaria para mim.

Não prenda a respiração @Lynda333 , o suporte para esse problema é notoriamente sem resposta. Sua única opção real, na minha opinião, é encontrar um compromisso adequado na funcionalidade. Se você nos contar mais sobre sua situação, eu e outros poderemos apresentar uma possível solução.

@Petce - não vou quando vi há quanto tempo esse problema começou. Parece ridículo não incluir essa habilidade. Falei com o cliente e vou mudar nossa abordagem para este site. 1) Eles podem baixar o PDF e 2) Estou recriando parte do conteúdo do PDF em HTML e deixando o usuário escolher o que imprimir. Leva mais tempo para criar, mas vai funcionar.

Alguém pode imprimir iframe embutido? Acabei de instalar a última versão da extensão e estou recebendo permissão de erro negada, também não consigo visualizar arquivos pdf no acesso normal ao pdf.

Usando o código desses commits (e do próprio analisador de dados pdfjs), você pode reescrever seu pdf como um blob temporário e adicionar uma instrução de impressão: https://github.com/mozilla/pdf.js/pull/6190/commits
Crie um iframe com o blob como fonte e ele começará a imprimir (observe que não carregará blobs em iframes, além de não seguir a instrução de impressão).
De qualquer forma, isso é bastante complexo se você ainda não estiver executando uma versão personalizada do visualizador.
Alternativamente, você pode adicionar a instrução de impressão no servidor ou em qualquer lugar onde você possa editar os dados do pdf.

O engraçado é que só o firefox precisa... Já faz três anos que esse problema foi reportado e o firefox é o único navegador que reclama de acessar o iframe contentWindow: parece que ninguém se importa com impressão hoje em dia, o que talvez seja uma coisa boa.

Isso parece estar funcionando para mim

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

Não consegui encontrar nenhum evento ou maneira de conectar-se ao visualizador, mas se você acionar o clique mesmo nos botões de impressão (estou usando a versão 1.4.20), parece imprimir. O settimeout permite que o visualizador carregue tempo e tal

Você é capaz de acessar contentWindow no firefox? Você não tem um problema de segurança?

@paolocaminiti Estou carregando todos os arquivos do mesmo domínio. Parece funcionar para mim. Eu usei o IE 10 e 11, IE Edge, Chrome no Windows. E FireFox 38 no Ubuntu 14.04.

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

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

Entendo, você está carregando o pdf novamente do controle remoto e resulta no mesmo domínio.
No chrome/opera/safari estou usando isso:

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

Basta usar os dados locais para criar um blob e depois chamar print, o setTimeout 0 permite que o iframe seja anexado.

Infelizmente, o firefox não permite a contentWindow com o blob, mas um blob com uma instrução de impressão será impresso por conta própria.

Por exemplo, nenhuma solução usando dados locais, mas talvez ter um proxy em um servidor proprietário para rotear dados de pdfs faria para permitir que todos fossem do mesmo domínio e aplicassem seu código.

Olá,

O que significa "res"? E PDFViewerApplication vem de PDF.js?

Obrigado,

Sim PDFViewerApplication é fornecido pela compilação PDFViewer do pdfjs, a fonte está no diretório /web, você deve modificar lá e fazer uma nova compilação para que seja mais fácil mesclar o repositório mestre à medida que for atualizado ...

res é apenas o argumento passado por getData, deve ser o byte rappresentation do arquivo pdf, aqui é usado para construir um url de blob local para carregar sem mais solicitações de rede.

Se você está indo para algo como o código acima, você pode querer limpá-lo um pouco:

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

No final, optei por não descartar o iframe, pois provou ser complicado entender quando fazê-lo, estou apenas deixando-o lá e reutilizando-o toda vez que o usuário quiser imprimir, supondo que o pdf não possa mudar no meio.

Espero que ajude.

@thenewguy Apenas para esclarecer este tópico para outros leitores, parece que estamos fazendo coisas bem diferentes:
Estou tentando usar o visualizador de pdf do navegador nativo para imprimir com qualidade total (não encontrei nenhuma maneira de acessar o contentWindow no ie e na borda não importa usando o mesmo domínio).
Você está carregando o visualizador de PDFjs em um iframe, possivelmente visível, e controlando sua impressão a partir do aplicativo host.

É engraçado porque eu estava tentando conseguir @paolocaminiti inserindo um script no meu iframe e chamando-o como uma função, mas agora recebo "offsetParent não está definido - não pode rolar" 😢. Vou usar a impressão do quiosque cromado e esqueci isso.

Até agora, nenhuma solução foi proposta

Ainda preciso ter uma solução do lado do servidor ou há algum progresso nesse assunto? É possível usar o PDFPrintService do pdf-js? Isso seria uma solução funcional? Se sim, como é possível usar este serviço sem ter que usar todo o visualizador de pdf?

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

kleins05 picture kleins05  ·  3Comentários

jigskpatel picture jigskpatel  ·  3Comentários

BrennanDuffey picture BrennanDuffey  ·  3Comentários

xingxiaoyiyio picture xingxiaoyiyio  ·  3Comentários

timvandermeij picture timvandermeij  ·  4Comentários