今回の記事は、以下の記事の続きです。
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フォルダです。