【问题标题】:How to apply web worker to rendering of a PDF using makepdf如何使用 makepdf 将 Web Worker 应用于 PDF 的呈现
【发布时间】:2015-09-21 07:49:11
【问题描述】:

我使用 JavaScript 插件 (pdfmake) 成功创建了一个 PDF,它很棒。 但是,当我尝试渲染约 8,000 行的库存/分类帐打印输出时,它会冻结超过一分钟。

这就是我通常声明我的 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' }];

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

生成

我搜索了 web workers 发现它可以解决这个 UI 冻结问题。 所以我尝试为它创建一个网络工作者:

$('#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() 的输出:

Worker 说:Object {pageOrientation: "portrait", content: Array[7]} 它正确记录了 json 结构。

到目前为止一切顺利。 但是在我将pdfmake.min.jsvfs_font.js 添加到worker 之后,我得到了错误Uncaught TypeError: Cannot read property 'createElementNS' of undefined

我什至在开始使用 worker 之前就收到了错误。

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

【问题讨论】:

  • 看起来该工具需要 DOM/文档,而工作人员没有。您可能能够为您浏览器化的节点使用虚拟 dom 库,这可能很酷......
  • 非常感谢您的关注,先生..您有没有机会提示我一些关于从哪里开始的链接..它真的对我有很大帮助..再次感谢

标签: javascript pdf web-worker pdfmake


【解决方案1】:

简单回答

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

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 被称为 incompatible with worker。

pdfmake does not use createElementNS 本身。 但是它的缩小脚本pdfmake.min.js 确实是为了创建一个下载链接。

无论如何我们都不需要下载链接,所以只要给它一个假人让它开心(现在)。

如果将来它需要一个真正的 DOM, 坏消息是 web worker 中的文档是 unavailable。 好消息是我们有一个纯 JavaScript 实现。 Download XML-JS, 提取并找到tinyxmlw3cdom.js, 导入它,您就可以创建一个功能文档。

除了文档,由于vfs_fonts.js是通过window变量获取pdfmake的,所以需要引入window作为global的别名。

对我来说,这些步骤使 pdfmake 在 web worker 中工作。

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


代码

我的工人是可以缓存的Builder。 如果您在服务器端组成工作人员, 您可以摆脱堆栈并构建函数并直接调用pdfmake, 让两个 js 代码更简单。

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

工人:

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

})();

【讨论】:

  • 非常感谢您的帮助先生@Sheepy..!您的代码确实有效.. 现在我只是调整它以适合我的布局.. 嗯,是否可以立即发送 docDefinition 而不是手动数组?而且在处理大表时打开功能(gen_pdf)也不起作用,我认为它是浏览器的问题,所以如果它是一个下载功能会更好。非常感谢先生的帮助!这将解决我项目未来的问题。\
  • 我尝试将open( 'data:application/pdf;base64,' + evt.data.base64 ); 替换为download('General Ledger From : &lt;?php echo $startDate; ?&gt; to &lt;?php echo $endDate; ?&gt;.pdf');,但它给了我一个“下载”未定义错误。
  • @melvnberd 目前浏览器没有一致的下载 API,所以我推荐 3rd 方库。有几个,我用一个相对跨平台的更新了答案的解释和代码。
  • 非常感谢 @sheepy 先生真正帮助我解决这个问题。我真的没有使用任何网络工作者的经验。但在这种情况下,我真的特别需要它,我正在生成 10mb> 个文件.. 你的回答也可以帮助很多人:) 因为关于 web 工作者的文档对于 makePDF 来说是非常有限的
  • @melvnberd 啊。你看,函数不能传递给 webworker。变通办法太先进了,由于其复杂性,可能超出范围。我已经尽力添加了一个 set 命令——它允许你定义整个 pdf,只要它们都是可克隆的对象。 (对象、数组、数字、字符串等。详情请搜索“结构化克隆算法”。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-13
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-04
相关资源
最近更新 更多