web-dev-qa-db-fra.com

Imprimer un pdf sans l'ouvrir visuellement

Ce que je veux construire, c’est qu’en cliquant sur un bouton, je veux déclencher l’impression d’un fichier PDF, mais sans l’ouvrir.

+-----------+
| Print PDF |
+-----------+
     ^ Click *---------> printPdf(pdfUrl)

La façon dont j'ai d'abord essayé est d'utiliser un iframe:

var $iframe = null;

// This is supposed to fix the onload bug on IE, but it's not fired
window.printIframeOnLoad = function() {
  if (!$iframe.attr("src")) { return; }
  var PDF = $iframe.get(0);
  PDF.focus();

  try {
    // This doesn't work on IE anyways
    PDF.contentWindow.print();

    // I think on IE we can do something like this:
    // PDF.document.execCommand("print", false, null);
  } catch (e) {
    // If we can't print it, we just open it in the current window
    window.location = url;
  }
};

function printPdf(url) {

  if ($iframe) {
    $iframe.remove();
  }

  $iframe = $('<iframe>', {
    class: "hide",
    id: "idPdf",
    // Supposed to be a fix for IE
    onload: "window.printIframeOnLoad()",
    src: url
  });

  $("body").prepend($iframe);
}

Cela fonctionne sur Safari (desktop & iOS) et Chrome (peut-on généraliser peut-être à webkit?).

Sur Firefox, PDF.contentWindow.print() se termine par une erreur permission denied (Même le pdf est chargé à partir du même domaine).

Sur IE (11), le gestionnaire onload ne fonctionne tout simplement pas.


Ma question est la suivante: existe-t-il un autre meilleur moyen d’imprimer le fichier PDF sans l’ouvrir visuellement à l’utilisateur?

La chose cross-browser est critique ici. Nous devrions supporter autant de navigateurs que possible.

Quel est le meilleur moyen d'y parvenir? Est-ce que mon début est un bon? Comment le compléter?

Nous sommes maintenant en 2016 et je pense que cela reste difficile à mettre en œuvre sur tous les navigateurs.

26
Ionică Bizău

[~ # ~] update [~ # ~] : This link détaille une solution élégante qui implique la modification des propriétés de la page pour la première page et en ajoutant une action sur la page ouverte. Fonctionne sur tous les navigateurs (car les navigateurs exécuteront le code JavaScript placé dans la section actions). Nécessite Adobe Acrobat Pro.


Il semble que 2016 n'apporte aucune nouvelle avancée au problème de l'impression. Avait un problème similaire et pour faire l'impression cross-browser je l'ai résolu en utilisant PDF.JS mais ai dû faire un ajout d'un liner à la source (ils vous demandent de la construire de toute façon).

L'idée:

  • Téléchargez la version stable pré-construite de https://mozilla.github.io/pdf.js/getting_started/#download et ajoutez les dossiers "build" et "web" au projet.
  • Le viewer.html fichier est ce qui rend les PDF avec une interface riche et contient des fonctionnalités d'impression. J'ai ajouté un lien dans ce fichier à mon propre JavaScript qui ne fait que déclencher window.print () après un délai.

Le lien ajouté à la visionneuse:

    <script src="viewer.js"></script>
    <!-- this autoPrint.js was added below viewer.js -->
    <script src="autoPrint.js"></script>
</head>

Le autoPrint.js javascript:

(function () {
    function printWhenReady() {
        if (PDFViewerApplication.initialized) {
            window.print();
        }
        else {
            window.setTimeout(printWhenReady, 3000);
        }
    };

    printWhenReady();
})();
  • Je pourrais alors faire des appels à viewer.html?file= dans le src d'un iframe et le cacher. Doit utiliser la visibilité, pas les styles d'affichage à cause de Firefox:

    <iframe src="web/viewer.html?file=abcde.pdf" style="visibility: hidden">
    

Le résultat: la boîte de dialogue d’impression s’affiche après un bref délai, le PDF étant masqué par l’utilisateur).

Testé sous Chrome, IE, Firefox.

15
Balah

Après avoir passé les dernières heures à essayer de comprendre celui-ci et à effectuer de nombreuses recherches, voici ce que j'ai déterminé ...

Le spécification de l'API Web HTML5 pour l'impression indique que l'un des printing steps doit déclencher beforeprint , un événement simple (événement non annulable) sur l’objet window du document en cours d’impression (ainsi que sur tout contexte de navigation imbriqué, il s’agit des iframes). permettre des modifications au document avant l'impression. Cette étape est interne au navigateur et n’est pas quelque chose que vous pourrez ajuster. Au cours de ce processus, la boîte de dialogue Imprimer du navigateur affiche parfois un aperçu du fichier (c'est ce que Chrome fait) ... donc, si votre objectif est de ne jamais afficher le fichier à la visionneuse, vous pourriez être bloqué.

Je suis venu le plus près de cet objectif en créant un index.html fichier contenant un bouton contenant des attributs data- * fournissant un contexte. Changer le chemin/nom de fichier.ext dans le data-print-resource-uri attribut à un fichier local de votre choix.

<!DOCTYPE html>
<html>
    <head>
        <title>Express</title>
        <link rel="stylesheet" href="/stylesheets/style.css">
    </head>
    <body>
        <h1>Express</h1>
        <p>Welcome to Express</p>
        <button name="printFile" id="printFile" data-print-resource-uri="/binary/paycheckStub.pdf" data-print-resource-type="application/pdf">Print File</button>
        <iframe name="printf" id="printf" frameborder="0"></iframe>
        <script src="/javascripts/print.js"></script>
    </body>
</html>

Puis dans le print.js fichier, j’ai essayé quelques trucs, mais je n’ai jamais vraiment réussi à le faire (en laissant dans les commentaires des choses avec lesquelles j’avais joué).

// Reference vars
var printButton = document.getElementById('printFile');
var printFrame = document.getElementById('printf');

// onClick handler
printButton.onclick = function(evt) {
    console.log('evt: ', evt);
    printBlob('printf', printButton.getAttribute('data-print-resource-uri'), printButton.getAttribute('data-print-resource-type'));
}

// Fetch the file from the server
function getFile( fileUri, fileType, callback ) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', fileUri);
    xhr.responseType = 'blob';
    xhr.onload = function(e) {
        // Success
        if( 200 === this.status ) {
            // Store as a Blob
            var blob = new Blob([this.response], {type: fileType});
            // Hang a URL to it
            blob = URL.createObjectURL(blob);
            callback(blob);
        } else {
            console.log('Error Status: ', this.status);
        }
    };
    xhr.send();
}

function printBlob(printFrame, fileUri, fileType) {
    // Debugging
    console.log('inside of printBlob');
    console.log('file URI: ', fileUri);
    console.log('file TYPE: ', fileType);

    // Get the file
    getFile( fileUri, fileType, function(data) {
        loadAndPrint(printFrame, data, fileType);
    });
}

function loadAndPrint(printFrame, file, type) {
    // Debugging
    console.log('printFrame: ', printFrame);
    console.log('file: ', file);

    window.frames[printFrame].src = file;
    window.frames[printFrame].print();

    /*
    // Setup the print window content
    var windowContent = '<!DOCTYPE html>';
    windowContent += '<html>'
    windowContent += '<head><title>Print canvas</title></head>';
    windowContent += '<body>'
    windowContent += '<embed src="' + file + '" type="' + type + '">';
    windowContent += '</body>';
    windowContent += '</html>';

    // Setup the print window
    var printWin = window.open('','','width=340,height=260');
    printWin.document.open();
    printWin.document.write(windowContent);
    printWin.document.close();
    printWin.focus();
    printWin.print();
    printWin.close();
    */
}

Je pense que si vous pouvez le faire fonctionner correctement à l’aide de Blob, celui-ci fonctionnera au mieux avec la méthode multi-navigateurs que vous souhaitiez.

J'ai trouvé quelques références sur ce sujet qui pourraient être utiles:

6
Benjamin Dean