2015-09-21 1 views
3

J'ai réussi à créer un PDF en utilisant un plug-in JavaScript (pdfmake) et c'était génial. Mais lorsque j'essaie d'afficher un inventaire/registre de 8 000 lignes, il gèle pendant plus d'une minute.Comment appliquer le Web worker au rendu d'un PDF en utilisant makepdf

Voilà comment je déclare habituellement mon docDefinition

var docDefinition = { 
pageOrientation: orientation, 
footer: function(currentPage, pageCount) { return {text: currentPage.toString() + '/' + pageCount, fontSize:8, alignment:'center'}; }, 
content:[ 
    printHeader, 
    { fontSize: 8, alignment: 'right', style: 'tableExample', 
    table: { 
     widths: width, 
     headerRows: 1, body: arr }, 
    layout: 'lightHorizontalLines' }] } 

var printHeader = [ { text: 'COMPANY NAME',alignment:'center' }, 
{ text: 'Address 1',alignment:'center' }, 
{ text: 'Address 2',alignment:'center' }, 
{ text: 'Additional Details,alignment:'center' }, 
{ text: 'document title',alignment:'center' }]; 

et

var arr = [[{"text":"","alignment":"left"},"text":"Date","alignment":"left"}, 
{"text":"Trans #","alignment":"left"},{"text":"Description","alignment":"left"}, 
{"text":"Ref #","alignment":"left"},{"text":"Debit","alignment":"left"}, 
{"text":"Credit","alignment":"left"},{"text":"Amount","alignment":"left"}, 
{"text":"Balance","alignment":"left"}],[{"text":"ACCOUNT : Merchandise Inventory","alignment":"left","colSpan":8},"","","","","","","", 
{"text":"1,646,101.06"}],["","10/13/2015","ST#0094",{"text":"","alignment":"left"},{"text":"","alignment":"left"},"546.94","0.00","546.94","1,646,648.00"],[{"text":"Total","alignment":"left","bold":true},"","","","", 
{"text":"546.94","alignment":"right","bold":true}, 
{"text":"0.00","alignment":"right","bold":true}, 
{"text":"","alignment":"right","bold":true}, 
{"text":"546.94","alignment":"right","bold":true}],[{"text":"ACCOUNT : Accounts Payable-Main","alignment":"left","colSpan":8},"","","","","","","", 
{"text":"-1,741,953.62"}],["","10/13/2015","ST#0094", 
{"text":"","alignment":"left"}, 
{"text":"","alignment":"left"},"0.00","546.94","-546.94","-1,742,500.56"], 
[{"text":"Total","alignment":"left","bold":true},"","","","", 
{"text":"0.00","alignment":"right","bold":true}, 
{"text":"546.94","alignment":"right","bold":true}, 
{"text":"","alignment":"right","bold":true}, 
{"text":"-546.94","alignment":"right","bold":true}] 

généré file.

J'ai cherché sur les travailleurs Web et voir qu'il peut résoudre ce problème de gel de l'interface utilisateur. J'ai donc essayé de créer un travailleur web pour lui:

$('#makepdf').click(function(){ 
    var worker = new Worker("<?php echo URL::to('/'); ?>/js/worker.js"); 
    worker.addEventListener('message',function(e){ 
    console.log('Worker said: ',e.data); 
},false); 

worker.postMessage(docDefinition); 

//worker.js

self.addEventListener('message', function(e) { 
    self.postMessage(e.data); 
}, false); 

Sortie de console.log():

travailleur a dit: Object {PageOrientation: « portrait ", contenu: Array [7]} sa journalisation correctement la structure json.

Jusqu'ici tout va bien. Mais après avoir ajouté pdfmake.min.js et vfs_font.js à l'agent, j'obtiens l'erreur Uncaught TypeError: Cannot read property 'createElementNS' of undefined.

Je reçois l'erreur avant même que j'ai commencé à utiliser le travailleur.

Est-il possible d'implémenter des travailleurs Web avec le plugin pdfmake?

+0

ressemble à cet outil a besoin du DOM/document, que les travailleurs n'ont pas. vous pourriez être en mesure d'utiliser une dom dom virtuelle pour le noeud que vous naviguez, cela pourrait être cool ... – dandavis

+0

Merci beaucoup pour l'attention monsieur .. est-il une chance que vous pouvez me suggérer quelques liens sur par où commencer. cela m'aiderait beaucoup .. merci encore – melvnberd

Répondre

7

réponse simple

Il suffit de fournir un constructeur fictif:

var document = { 'createElementNS': function(){ return {} } }; 
var window = this; 
importScripts('pdfmake.min.js', 'vfs_fonts.js'); 

Sinon, si vous pensez qu'il est trop sale, importer XML-JS (seulement 60k) et créer un document virtuel pour pdfmake.

importScripts('tinyxmlw3cdom.js'); 
var window = this; 
var document = new DOMDocument(new DOMImplementation()); 
importScripts('pdfmake.min.js', 'vfs_fonts.js'); 

Explication

pdfmake est connu pour être incompatible avec un travailleur.

En soi, pdfmake does not use createElementNS. Cependant, son script minifié pdfmake.min.js fait, apparemment pour créer un lien de téléchargement.

Nous n'avons pas besoin de lien de téléchargement de toute façon, alors donnez-lui simplement un mannequin pour le rendre heureux (pour le moment).

Si à l'avenir, il a besoin d'un vrai DOM, mauvaises nouvelles est le document est unavailable dans le travailleur Web. Bonne nouvelle, nous avons une implémentation javascript pure. Download XML-JS, extrait et trouve tinyxmlw3cdom.js, l'importer et vous pouvez créer un document fonctionnel.

En plus du document, puisque vfs_fonts.js obtenir pdfmake à travers la variable window, nous devons introduire la fenêtre comme un alias pour global.

Pour moi, ces étapes font que pdfmake fonctionne dans le Web worker.

Pour enregistrer le fichier, vous devez convertir les données base64 fournies par pdfmake en un téléchargement binaire. Un certain nombre de scripts sont disponibles, tels que download.js. Dans le code ci-dessous, j'utilise FileSaver.


code

Mon travailleur est un Builder qui peut être mis en mémoire cache. Si vous composez le serveur côté serveur, vous pouvez vous débarrasser des fonctions de pile et de construction et appeler directement pdfmake, rendant le code js beaucoup plus simple.

principal HTML:

<script src='FileSaver.min.js'></script> 
<script> 
function base64ToBlob(base64, type) { 
    var bytes = atob(base64), len = bytes.length; 
    var buffer = new ArrayBuffer(len), view = new Uint8Array(buffer); 
    for (var i=0 ; i < len ; i++) 
     view[i] = bytes.charCodeAt(i) & 0xff; 
    return new Blob([ buffer ], { type: type }); 
} 
////////////////////////////////////////////////////////// 

var pdfworker = new Worker('worker.js'); 

pdfworker.onmessage = function(evt) { 
    // open('data:application/pdf;base64,' + evt.data.base64); // Popup PDF 
    saveAs(base64ToBlob(evt.data.base64, 'application/pdf'), 'General Ledger.pdf'); 
}; 

function pdf(action, data) { 
    pdfworker.postMessage({ action: action, data: data }); 
} 

pdf('add', 'Hello WebWorker'); 
pdf('add_table', { headerRows: 1 }); 
pdf('add', [ 'First', 'Second', 'Third', 'The last one' ]); 
pdf('add', [ { text: 'Bold value', bold: true }, 'Val 2', 'Val 3', 'Val 4' ]); 
pdf('close_table'); 
pdf('add', { text: 'This paragraph will have a bigger font', fontSize: 15 }); 
pdf('gen_pdf'); // Triggers onmessage when it is done 

// Alternative, one-size-fit-all usage 
pdf('set', { pageOrientation: 'landscape', footer: { text: 'copyright 2015', fontSize: 8, alignment:'center'}, content:[ "header", { fontSize: 8, alignment: 'right', table: { headerRows: 1, body: [[1,2,3],[4,5,6]] } }] }); 
pdf('gen_pdf'); 

</script> 

Travailleur:

//importScripts('tinyxmlw3cdom.js'); 
//var document = new DOMDocument(new DOMImplementation()); 
var document = { 'createElementNS': function(){ return {} } }; 
var window = this; 
importScripts('pdfmake.min.js', 'vfs_fonts.js'); 

(function() { 'use strict'; 

var doc, current, context_stack; 

function set (data) { 
    doc = data; 
    if (! doc.content) doc.content = []; 
    current = doc.content; 
    context_stack = [ current ]; 
} 
set({}); 

function add (data) { 
    current.push(data); 
} 

function add_table (template) { 
    if (! template) template = {}; 
    if (! template.table) template = { table: template }; 
    if (! template.table.body) template.table.body = []; 
    current.push(template); // Append table 
    push(template.table.body); // Switch context to table body 
} 

function push (data) { 
    context_stack.push(current); 
    return current = data; 
} 

function pop() { 
    if (context_stack.length <= 1) return console.warn("Cannot close pdf root"); 
    context_stack.length -= 1; 
    return current = context_stack[ context_stack.length-1 ]; 
} 

function gen_pdf() { 
    pdfMake.createPdf(doc).getBase64(function(base64) { 
     postMessage({ action: 'gen_pdf', base64: base64 }); 
    }); 
} 

onmessage = function(evt) { 
    var action = evt.data.action, data = evt.data.data; 
    switch (action) { 
     case 'set': set(data); break; 
     case 'add': add(data); break; 
     case 'add_table' : add_table(data); break; 
     case 'close_table': pop(); break; 
     case 'gen_pdf': gen_pdf(); break; 
    } 
}; 

})(); 
+0

Merci beaucoup pour votre aide monsieur @ Sheepy ..! Votre code fonctionne vraiment .. maintenant je suis juste en train de l'ajuster pour s'adapter à ma mise en page .. hmm est là pour envoyer la docDefinition tout de suite au lieu du tableau manuel? et aussi la fonction ouverte (gen_pdf) ne fonctionne pas quand il s'agit de grande table, je pense que c'est le problème du navigateur, donc ce serait beaucoup mieux si c'est une fonctionnalité de téléchargement à la place. Merci beaucoup pour l'aide monsieur! \ – melvnberd

+0

J'ai essayé de remplacer 'open ('données: application/pdf; base64,' + evt.data.base64);' à télécharger ('General Ledger From: à . pdf '); 'mais cela me donne un' téléchargement 'erreur non définie. – melvnberd

+1

@melvnberd Actuellement, les navigateurs n'ont pas d'API de téléchargement cohérente, donc je recommanderais des bibliothèques tierces. Il y en a quelques unes, et j'ai mis à jour l'explication et le code de la réponse avec une explication qui est relativement multi-plateforme. – Sheepy