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

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

https://qiita.com/changkejun/items/bbb3c094c4c2e4a7a120

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

https://qiita.com/changkejun/items/f08b1959fc7fd3f24a8f

# loadWithGlobalPool関数の改造
パラメータにengineとreturnVarを追加します。以下はサンプルです。
```js
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
```js
var global={};
var bytesIPAexgothic=null;
global.fire=function(){
	bytesIPAexgothic=file.readAllBytes("font/ipaexg00401.ttf"); 
	efw.register("bytesIPAexgothic");
}
```
- testJavet.js
```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
```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
```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
```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
```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は以下です。
```xml
<dependency>
    <groupId>io.github.efwgrp</groupId>
    <artifactId>efw</artifactId>
    <version>4.07.024</version>
</dependency>
```
```xml
<dependency>
    <groupId>org.openjdk.nashorn</groupId>
    <artifactId>nashorn-core</artifactId>
    <version>15.6</version>
</dependency>
```
```xml
<dependency>
    <groupId>com.caoccao.javet</groupId>
    <artifactId>javet</artifactId>
    <version>3.1.8</version>
</dependency>
```

# サンプルソース
以下のリポジトリのhello-pdf-lib3とhello-pdfmake3フォルダです。

https://github.com/efwGrp/qittaSamples