0
1

【make it easy】Javetを利用して、pdf-libとpdfmakeをefwに使ってみる

Last updated at Posted at 2023-10-14

今回の記事は、以下の記事の続きです。

Javetについて、先日の記事をみてください。

loadWithGlobalPool関数の改造

パラメータにengineとreturnVarを追加します。以下はサンプルです。

var pdfDataUri=loadWithGlobalPool({
    name:"pdf-lib-javet",
    max:10,
    initializer:initscript,
    script:runscript,
    context:{bytesIPAexgothic:bytesIPAexgothic,createdate:new Date().format("mm:ss SSS")},
    engine:"javet",
    returnVar:"pdfDataUri",
});

前回は、Nashornエンジンの複数コンテキスト対応の仕組みを利用して、複数コンテキストでプールを作って、同時複数リクエストを対応できるようにしました。今回は、Javetを利用してみたいです。そして、NashornとJavetの切り替えが必要ですから、engineのパラメータを設けます。

また、JavetのawaitはES2017の標準仕様で、async関数内しか利用できなくて、TOPレベルで呼び出せません。そして、前回のNashornの拡張await関数のようにTOPレベルで実行して、Promiseの実行結果を待つことができません。Promiseの実行結果をとるため、実行完了後どの変数が結果になるか指定する必要です。これはreturnVarを設ける経緯です。

pdf-libのの試験ソース

  • global.js
var global={};
var bytesIPAexgothic=null;
global.fire=function(){
	bytesIPAexgothic=file.readAllBytes("font/ipaexg00401.ttf"); 
	efw.register("bytesIPAexgothic");
}
  • testJavet.js
var testJavet={};
testJavet.paramsFormat={
	pdf:null
};
testJavet.fire=function(params){
	var initscript=`
		const PDFLib = require(_eventfolder+"/pdf-lib.min.js");
		const fontkit = require(_eventfolder+"/fontkit.umd.min.js");
		function byteToUint8Array(byteArray) {
			var uint8Array = new Uint8Array(byteArray.length);
			uint8Array.set(Array.from(byteArray));
			return uint8Array;
		}
		var embedderIPAexgothic;
		(async function(){
			embedderIPAexgothic=await PDFLib.CustomFontSubsetEmbedder.for(
				fontkit, 
				byteToUint8Array(bytesIPAexgothic), 
				"IPAexゴシック", 
				{}
			);
		})();
	`;
	var runscript=`
		var pdfDataUri;
		(async function(){
			var pdfDoc=await PDFLib.PDFDocument.create();
			var myfont=PDFLib.PDFFont.of(pdfDoc.context.nextRef(), pdfDoc, embedderIPAexgothic);
			pdfDoc.fonts.push(myfont);
			var page = pdfDoc.addPage([500, 400]);
			page.setFont(myfont);
			page.moveTo(10, 200);
			page.drawText('Hello World!こんにちは東京'+createdate);
			pdfDataUri=await pdfDoc.saveAsBase64({ dataUri: true });
		})();
	`;
	var pdfDataUri=loadWithGlobalPool({
		name:"pdf-lib-javet",
		max:10,
		initializer:initscript,
		script:runscript,
		context:{bytesIPAexgothic:bytesIPAexgothic,createdate:new Date().format("mm:ss SSS")},
		engine:"javet",
		returnVar:"pdfDataUri",
	});
	return new Result().eval("$('"+params.pdf+"')[0].src='" +pdfDataUri +"'");
}

ソースの書き方は、前回のNashorn版と結構似ていますが、await関連の部分の書き方は違っています。これは標準ES2017の書き方です。どうも前回Nashorn拡張のawait関数のほうが分かり易い気がします。比較のため、Nashorn拡張の書き方をここに貼り付けます。

  • testNashorn.js
var testNashorn={};
testNashorn.paramsFormat={
	pdf:null
};
testNashorn.fire=function(params){
	var initscript=`
		load(_eventfolder+"/pdf-lib.min.js");
		load(_eventfolder+"/fontkit.umd.min.js");
		function byteToUint8Array(byteArray) {
			var uint8Array = new Uint8Array(byteArray.length);
			uint8Array.set(Java.from(byteArray));
			return uint8Array;
		}
		var embedderIPAexgothic=await(PDFLib.CustomFontSubsetEmbedder.for(
				fontkit, 
				byteToUint8Array(bytesIPAexgothic), 
				"IPAexゴシック", 
				{}
			)
		);
	`;
	var runscript=`
		var pdfDataUri;
		var pdfDoc=await(PDFLib.PDFDocument.create());
		var myfont=PDFLib.PDFFont.of(pdfDoc.context.nextRef(), pdfDoc, embedderIPAexgothic);
		pdfDoc.fonts.push(myfont);
		var page = pdfDoc.addPage([500, 400]);
		page.setFont(myfont);
		page.moveTo(10, 200);
		page.drawText('Hello World!こんにちは東京'+createdate);
		pdfDataUri=await(pdfDoc.saveAsBase64({ dataUri: true }));
		pdfDataUri;
	`;
	var pdfDataUri=loadWithGlobalPool({
		name:"pdf-lib-nashorn",
		max:10,
		initializer:initscript,
		script:runscript,
		context:{bytesIPAexgothic:bytesIPAexgothic,createdate:new Date().format("mm:ss SSS")},
		engine:"nashorn",
		returnVar:"pdfDataUri",
	});
	return new Result().eval("$('"+params.pdf+"')[0].src='" +pdfDataUri +"'");
}

jspは、前回とほぼ同じですから、説明を略します。

pdfmakeの試験ソース

  • global.js
var global={};
var ipaexg00401_ttf=null;
global.fire=function(){
	ipaexg00401_ttf=encodeBytestoBase64forPdfMake(file.readAllBytes("font/ipaexg00401.ttf"));//IPAexゴシック 
	efw.register("ipaexg00401_ttf");
}

function encodeBytestoBase64forPdfMake(aBytes) {
	//Byte配列をUint8Arrayに変換する
	var temp = new Uint8Array(aBytes.length);
	temp.set(Java.from(aBytes));
	aBytes=temp;
	//base64の文字列を作成する
	var nMod3 = 2, sB64Enc = '';
	for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
		nMod3 = nIdx % 3;
		if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += '\r\n'; }
			nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
		if (nMod3 === 2 || aBytes.length - nIdx === 1) {
			sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
			nUint24 = 0;
		}
	}
	return sB64Enc.substr(0, sB64Enc.length - 2 + nMod3) + (nMod3 === 2 ? '' : nMod3 === 1 ? '=' : '==');
	//------------
	function uint6ToB64 (nUint6) {
		return nUint6 < 26 ?
		  nUint6 + 65
		: nUint6 < 52 ?
		  nUint6 + 71
		: nUint6 < 62 ?
		  nUint6 - 4
		: nUint6 === 62 ?
		  43
		: nUint6 === 63 ?
		  47
		:
		  65;
	}
}
  • testJavet.js
var testJavet={};
testJavet.paramsFormat={
	pdf:null
};
testJavet.fire=function(params){
	var initscript=`
		const pdfMake = require(_eventfolder+"/pdfmake.min.js");
		//require(_eventfolder+"/vfs_fonts.min.js");
		pdfMake.vfs={};
		pdfMake.vfs["ipaexg00401.ttf"]=ipaexg00401_ttf;
		var fonts={
			"IPAexゴシック":{
				normal:"ipaexg00401.ttf",
				bold:"ipaexg00401.ttf",
				italics:"ipaexg00401.ttf",
				bolditalics:"ipaexg00401.ttf",
			},
		}
	
	`;
	var runscript=`
		var docDefinition = {
			content: [
				{ text:"hello world!こんにちは東京", fontSize: 20 },
			],
			defaultStyle: {
				font:"IPAexゴシック"
			}
		};
		var doc=pdfMake.createPdf(docDefinition,null,fonts);
		var pdfDataUri;
		(async function(){
			pdfDataUri=await new Promise(function(resolve,reject){
					doc.getDataUrl(function(data){
						resolve(data);
					});
				});
		})();
		
		pdfDataUri="aaaa";
	`;
	var pdfDataUri=loadWithGlobalPool({
		name:"pdfMake-javet",
		max:10,
		initializer:initscript,
		script:runscript,
		context:{ipaexg00401_ttf:ipaexg00401_ttf},
		engine:"javet",
		returnVar:"pdfDataUri",
	});
	return new Result().eval("$('"+params.pdf+"')[0].src='" +pdfDataUri +"'");
}
  • testNashorn.js
var testNashorn={};
testNashorn.paramsFormat={
	"pdf":null
};
testNashorn.fire=function(params){
	var initscript=`
		load(_eventfolder+"/pdfmake.min.js");
		//load(_eventfolder+"/vfs_fonts.min.js");
		pdfMake.vfs={};
		pdfMake.vfs["ipaexg00401.ttf"]=ipaexg00401_ttf;
		var fonts={
			"IPAexゴシック":{
				normal:"ipaexg00401.ttf",
				bold:"ipaexg00401.ttf",
				italics:"ipaexg00401.ttf",
				bolditalics:"ipaexg00401.ttf",
			},
		}
	`;
	var runscript=`
		var docDefinition = {
			content: [
				{ text:"hello world!こんにちは東京", fontSize: 20 },
			],
			defaultStyle: {
				font:"IPAexゴシック"
			}
		};
		var doc=pdfMake.createPdf(docDefinition,null,fonts);
		var pdfDataUri=await(
			new Promise(function(resolve,reject){
				doc.getDataUrl(function(data){
					resolve(data);
				});
			})
		);
		pdfDataUri;
	`;
	var pdfDataUri=loadWithGlobalPool({
		name:"pdfMake-nashorn",
		max:2,
		initializer:initscript,
		script:runscript,
		context:{ipaexg00401_ttf:ipaexg00401_ttf},
		engine:"nashorn",
		returnVar:"pdfDataUri",
	});
	return new Result().eval("$('"+params.pdf+"')[0].src='" +pdfDataUri +"'");
}

10回同時PDF作成の試験結果

pdf-lib

Nashorn初回33秒、次回5秒です。
Javet初回10秒、次回1秒です。

pdfmake

Nashorn初回61秒、次回19秒です。
Javet初回7秒、次回2秒です。

結論

さすがのV8です。とても速いです。Efw環境に、ES2015以上の特徴を大量に利用しなければならない場合、loadWithGlobalPool + Javetでよい選択になるかもしれません。

展望

Efw本体のJavascriptエンジンをJavetに切り替えする可能性があるかどうか。

環境

JDKは15以上、tomcatは7,8,9のいずれ、jarは以下です。

<dependency>
    <groupId>io.github.efwgrp</groupId>
    <artifactId>efw</artifactId>
    <version>4.07.009</version>
</dependency>
<dependency>
    <groupId>org.openjdk.nashorn</groupId>
    <artifactId>nashorn-core</artifactId>
    <version>15.4</version>
</dependency>
<dependency>
    <groupId>com.caoccao.javet</groupId>
    <artifactId>javet</artifactId>
    <version>3.0.0</version>
</dependency>

サンプルソース

以下のリポジトリのhello-pdf-lib3とhello-pdfmake3フォルダです。

0
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1