如何使用makepdf将Web worker应用于渲染PDF [英] How to apply web worker to rendering of a PDF using makepdf

查看:170
本文介绍了如何使用makepdf将Web worker应用于渲染PDF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用JavaScript插件(。



我搜索了网络工作者和看到它可以解决这个UI冻结问题。
所以我尝试为它创建一个Web worker:

  $('#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);

console.log()的输出


工作人员说:对象{pageOrientation:portrait,content:Array [7]}
正确记录json结构。


到目前为止一切顺利。
但是在我添加了 pdfmake.min.js 之后 vfs_font.js 给worker,我得到错误 Uncaught TypeError:无法读取属性'createElementNS未定义的



在我开始使用工作人员之前,我收到了错误。



是否可以使用pdfmake插件实现Web worker?

解决方案

简单回答



只需提供一个虚拟构造函数:

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

或者,如果您认为它太脏了,请导入 XML-JS (仅60k)并为 pdfmake

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






说明



pdfmake被称为不兼容与工人一起。



本身,pdfmake 不使用 createElementNS。
但是它的缩小脚本 pdfmake.min.js ,显然是为了创建下载链接。



我们无论如何都不需要下载链接,所以只是给它一个假人来保持它的快乐(现在)。



如果将来它需要一个真正的DOM,
坏消息是文件是不可用
好​​消息是我们有一个纯粹的javascript实现。
下载 XML-JS,
摘录并找到 tinyxmlw3cdom.js
导入它,你可以创建一个功能文档。



除了文档,自 vfs_fonts.js 通过窗口变量获取pdfmake,我们需要引入窗口作为全局的别名。



对我来说,这些步骤使pdfmake在Web worker中起作用。



要保存文件,需要将pdfmake提供的base64数据转换为二进制下载。
有许多脚本可供使用,例如 download.js
在下面的代码中,我使用的是 FileSaver






代码



我的工人是 Builder
如果您在服务器端编写工作程序,
您可以摆脱堆栈并构建函数并直接调用pdfmake,
使两个js代码更加简单。



主要HTML:

 < script src ='FileSaver.min.js'>< ; /脚本> 
< script>
函数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的;
返回新的Blob([buffer],{type:type});
}
///////////////////////////////////////// /////////////////

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

pdfworker.onmessage = function(evt){
// open('data:application / pdf; base64,'+ evt.data.base64); //弹出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:'此段将有更大的字体',fontSize:15});
pdf('gen_pdf'); //完成后触发onmessage

//替代,一个尺寸适合所有用法
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>

工人:

  // 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;

函数集(数据){
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); //追加表
push(template.table.body); //将上下文切换到表体
}

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

function pop(){
if(context_stack.length< = 1)返回console.warn(无法关闭pdf root);
context_stack.length - = 1;
return current = context_stack [ context_stack.length-1];
}

函数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);打破;
case'add':add(data);打破;
case'add_table':add_table(data);打破;
case'close_table':pop();打破;
case'gen_pdf':gen_pdf();打破;
}
};

})();


I successfully created a PDF using a JavaScript plug-in (pdfmake) and it was great. But when I try to render an ~8,000-row inventory/ledger printout, it freeze for over a minute.

This is how I usually declare my 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' }] }

where

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' }];

and

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

generated .

I searched about web workers and see that it can solve this UI freezing problem. So I tried to create a web worker for it:

$('#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);

Output from console.log():

Worker said: Object {pageOrientation: "portrait", content: Array[7]} its logging correctly the json structure.

So far so good. But after I added pdfmake.min.js and vfs_font.js to the worker, I get the error Uncaught TypeError: Cannot read property 'createElementNS' of undefined.

I get the error before I even started using the worker.

Is it possible to implement web workers with the pdfmake plug-in?

解决方案

Simple answer

Just provide a dummy constructor:

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

Alternatively, if you think it is too dirty, import XML-JS (only 60k) and create a virtual document for pdfmake.

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


Explanation

pdfmake is known to be incompatible with worker.

By itself, pdfmake does not use createElementNS. However its minified script pdfmake.min.js do, apparently to create a download link.

We don't need download link anyway, so just give it a dummy to keep it happy (for now).

If in the future it needs a real DOM, bad news is document is unavailable in web worker. Good news is we have a pure javascript implementation. Download XML-JS, extract and find tinyxmlw3cdom.js, import it and you can create a functional document.

In addition to the document, since vfs_fonts.js get pdfmake through the window variable, we need to introduce window as an alias for global.

For me, these steps make pdfmake works in web worker.

To save the file, you need to convert the base64 data provided by pdfmake into a binary download. A number of scripts are available, such as download.js. In the code below I am using FileSaver.


Code

My worker is a Builder that can be cached. If you compose the worker on server side, you can get rid of the stack and build functions and directly call pdfmake, making both js code much simpler.

Main 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>

Worker:

//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;
   }
};

})();

这篇关于如何使用makepdf将Web worker应用于渲染PDF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆