Pdf.js: Impossible d'imprimer le PDF lorsqu'il est chargé dans iFrame

Créé le 11 oct. 2014  ·  21Commentaires  ·  Source: mozilla/pdf.js

J'essaie d'utiliser Javascript pour mettre au point et imprimer un fichier PDF chargé dans un iframe que j'ai placé dynamiquement dans le DOM. Ce problème se produit spécifiquement pour moi dans Firefox.

Mon code ressemble à ce qui suit :

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

Je reçois l'erreur suivante :

Erreur : Autorisation refusée pour accéder à la propriété "Imprimer"

Mes recherches me disent que cela devrait fonctionner, une lecture plus approfondie m'a amené à croire que cela pourrait être un bogue. Toute aide serait appréciée.

Éditer

J'ai testé la fonctionnalité en remplaçant le fichier PDF par une capture d'écran au format PNG de la première page qu'il contient et la fonctionnalité d'impression a fonctionné.

Éditer

D'autres tests. Ajout de pdfjs à mon installation chrome et tentative d'impression avec le même code ci-dessus. même erreur :

SecurityError : a bloqué l'accès d'un cadre avec l'origine " http://domain.com " à un cadre d'origine croisée.

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

Je dois préciser que le fichier PDF est chargé sur le même domaine.

3-upstream 4-printing

Commentaire le plus utile

Je pense en connaître la raison, du moins dans Firefox. Dans un exemple simple comme celui ci-dessus, si vous utilisez Firefox DevTools pour examiner document.domain à la fois dans la page principale et dans l'iframe, j'ai trouvé que document.domain est pdf.js pour le moteur de rendu PDF.js, de sorte que les restrictions d'origine croisée du navigateur entrent en jeu.

De plus, si je construisais PDF.js à partir de la source et que je définissais mon iframe sur web/viewer.html?file= , puis que j'appelais l'iframe à imprimer (comme dans l'OP), cela fonctionnait bien.

Existe-t-il un moyen pour PDF.js (au moins lors de l'exécution dans Firefox) d'accepter les messages d'autres fenêtres, peut-être via quelque chose comme window.postMessage ? (Je ne l'ai jamais utilisé, donc je ne sais pas si c'est approprié pour ce cas d'utilisation ou non.)

Tous les 21 commentaires

Peut-être un doublon du bogue 874200 .

Pour tous ceux qui tombent sur ce problème et aimeraient un éventuel contournement (étant donné que ce problème existe depuis plus d'un an maintenant sans solution), j'ai fini par utiliser ImageMagick en PHP avec GhostScript pour générer des images du PDF sur le serveur , puis a renvoyé ces URL d'image dans une réponse json, l'impression du résultat était triviale.

Me connaissant cependant, il y a probablement un moyen meilleur et plus facile de le faire...

dans about:config j'ai mis pdfjs.disabled=true mais pdf démarre le téléchargement je suppose que s'il utilise plugin.disable_full_page_plugin_for_types = application/pdf il ne peut pas télécharger le pdf et voir dans la visionneuse

Je pense en connaître la raison, du moins dans Firefox. Dans un exemple simple comme celui ci-dessus, si vous utilisez Firefox DevTools pour examiner document.domain à la fois dans la page principale et dans l'iframe, j'ai trouvé que document.domain est pdf.js pour le moteur de rendu PDF.js, de sorte que les restrictions d'origine croisée du navigateur entrent en jeu.

De plus, si je construisais PDF.js à partir de la source et que je définissais mon iframe sur web/viewer.html?file= , puis que j'appelais l'iframe à imprimer (comme dans l'OP), cela fonctionnait bien.

Existe-t-il un moyen pour PDF.js (au moins lors de l'exécution dans Firefox) d'accepter les messages d'autres fenêtres, peut-être via quelque chose comme window.postMessage ? (Je ne l'ai jamais utilisé, donc je ne sais pas si c'est approprié pour ce cas d'utilisation ou non.)

Autre constat : j'espérais déclencher l'impression via l'événement onload sur le iframe , mais le PDF n'a pas encore forcément été rendu. Vous ne savez pas s'il existe un moyen fiable de vérifier cela ?

J'ai vu #5765, mais il semble que cela ne fasse référence qu'aux événements auxquels vous pourriez vous connecter si vous exécutiez directement PDF.js et non la version intégrée de Firefox ?

Je suis déconcerté que ce soit un tel problème pour faire quelque chose d'aussi simple que de déclencher un fichier PDF à imprimer en utilisant javascript. Ce problème semble remonter à environ deux ans (peut-être plus) maintenant.

Quelqu'un a-t-il trouvé une solution _côté client_ ? J'ai vu la solution @Petce mais cette solution ne fonctionnerait pas pour moi.

Ne retenez pas votre souffle @ Lynda333 , le support pour ce problème est notoirement insensible. Votre véritable seule option à mon avis est de trouver un compromis approprié dans la fonctionnalité. Si vous nous en dites plus sur votre situation, moi-même et d'autres pourraient être en mesure de présenter une éventuelle solution de contournement.

@Petce - Je ne le ferai pas quand j'ai vu depuis combien de temps ce problème a commencé. Il semble ridicule de ne pas inclure cette capacité. J'ai parlé avec le client et je vais changer notre approche pour ce site. 1) Ils peuvent télécharger le PDF et 2) Je recrée une partie du contenu PDF en HTML et laisse l'utilisateur choisir ce qu'il souhaite imprimer. Prend plus de temps à créer mais cela fonctionnera.

Est-ce que quelqu'un peut imprimer une iframe intégrée ? Je viens d'installer la dernière version de l'extension et je reçois l'autorisation d'erreur refusée, je ne peux pas non plus afficher les fichiers pdf sur un accès pdf normal.

En utilisant le code de ces commits (et l'analyseur de données pdfjs lui-même), vous pouvez réécrire votre pdf en tant que blob temporaire et ajouter une instruction d'impression : https://github.com/mozilla/pdf.js/pull/6190/commits
Créez un iframe avec le blob comme source et il commencera à imprimer (notez que c'est-à-dire qu'il ne chargera pas du tout les blobs dans les iframes, à part ne pas suivre l'instruction d'impression).
Quoi qu'il en soit, c'est assez complexe si vous n'utilisez pas déjà une version personnalisée de la visionneuse.
Alternativement, vous pouvez ajouter l'instruction d'impression sur le serveur ou n'importe où vous pouvez modifier les données du pdf.

La partie amusante est que seul Firefox en a besoin... Cela fait trois ans que ce problème a été signalé et Firefox est le seul navigateur à se plaindre d'accéder à l'iframe contentWindow : on dirait que personne ne se soucie vraiment de l'impression de nos jours, ce qui est peut-être une bonne chose.

Cela semble fonctionner pour moi

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

Je n'ai trouvé aucun événement ou moyen de me connecter à la visionneuse, mais si vous déclenchez le clic même sur ses boutons d'impression (j'utilise la version 1.4.20), il semble imprimer. Le settimeout laisse au spectateur le temps de charger et tel

Pouvez-vous accéder à contentWindow sur firefox ? Vous n'avez pas un problème de sécurité ?

@paolocaminiti Je charge tous les fichiers du même domaine. Il semble fonctionner pour moi. J'ai utilisé IE 10 et 11, IE Edge, Chrome sous Windows. Et FireFox 38 sur 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));
    }

Je vois, vous chargez à nouveau le pdf à distance et il en résulte le même domaine.
Sur chrome/opera/safari j'utilise ceci :

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

Utilise simplement les données locales pour créer un blob puis appelle print, le setTimeout 0 permet d'attacher l'iframe.

Malheureusement, firefox interdit le contentWindow avec le blob, mais un blob avec une instruction d'impression s'imprimera tout seul.

Par exemple, aucune solution utilisant des données locales, mais peut-être que le fait d'avoir un proxy sur son propre serveur pour acheminer les données pdf permettrait à tous d'être dans le même domaine et d'appliquer votre code.

Bonjour,

Qu'est-ce que cela signifie "rés" ? Et PDFViewerApplication vient de PDF.js ?

Merci,

Oui PDFViewerApplication est fourni par la version PDFViewer de pdfjs, la source se trouve sous le répertoire /web, vous devez y modifier et créer une nouvelle version afin qu'il soit plus facile de fusionner le dépôt principal au fur et à mesure de sa mise à jour...

res est juste l'argument renvoyé par getData, devrait être la représentation en octets du fichier pdf, ici est utilisé pour créer une URL de blob locale à charger sans autres requêtes réseau.

Si vous optez pour quelque chose comme le code ci-dessus, vous voudrez peut-être le nettoyer un peu :

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

En fin de compte, j'ai choisi de ne pas supprimer l'iframe car il s'est avéré compliqué de comprendre quand le faire, je le laisse simplement là et le réutilise chaque fois que l'utilisateur veut imprimer, en supposant que le pdf ne peut pas changer entre les deux.

J'espère que ça aide.

@thenewguy Juste pour clarifier ce fil pour les autres lecteurs, il semble que nous fassions des choses assez différentes :
J'essaie d'utiliser la visionneuse pdf du navigateur natif pour imprimer en pleine qualité (je n'ai trouvé aucun moyen d'accéder à contentWindow à la fois sur ie et edge, peu importe en utilisant le même domaine).
Vous chargez le visualiseur PDFjs dans un iframe, éventuellement visible, et contrôlez son impression à partir de l'application hôte.

C'est drôle parce que j'essayais de réaliser @paolocaminiti en insérant un script dans mon iframe et en l'appelant comme une fonction mais maintenant j'obtiens "offsetParent n'est pas défini -- ne peut pas faire défiler" 😢. J'utiliserai l'impression de kiosque chromé et j'ai oublié cela.

A ce jour, aucune solution n'a été proposée

Dois-je encore disposer d'une solution côté serveur ou y a-t-il des progrès à ce sujet ? Est-il possible d'utiliser le PDFPrintService de pdf-js ? Serait-ce une solution de contournement fonctionnelle? Si oui, comment est-il possible d'utiliser ce service sans avoir à utiliser tout le visualiseur pdf ?

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

THausherr picture THausherr  ·  3Commentaires

zerr0s picture zerr0s  ·  3Commentaires

dmisdm picture dmisdm  ·  3Commentaires

jigskpatel picture jigskpatel  ·  3Commentaires

liuzhen2008 picture liuzhen2008  ·  4Commentaires