WebアプリをJava で構築していて、Javaでダウンロードすファイルを作るなら一時ファイル
(ディスクに一旦書き出すこと)
を作らずにダウンロードするものを作るのは容易ですが、言語、実行環境が異なる処理に作らせて
一時ファイルを生成することなくダウンロードするのはそれなりの処理が必要です。
Javaからプロセスで実行する Python が作成する PDF をPythpnの処理で標準出力で出力して
Javaがその出力を受け取ったら HTTPのレスポンスとして出力ストリーム(OutputStream) に出力します。
(まるで、横流し。。。)
Java Wicket Web ページの方からサンプルを紹介します。
Javaからプロセス実行は、
yipuran-core ver4.6 からの ScriptExecutor の
runStream(Supplier<String>, Supplier<Collection<String>>
, Consumer<InputStream>, BiConsumer<String, Throwable>)
メソッドを使って
Script_exec · yipuran/yipuran-core Wiki · GitHub
行います。
AJAX のダウンロード処理として、InputStream でプロセスが出力する標準出力を受け取って
そのまま OutputStream に流してます。
String script = "python /pdf/sample_pdf.py /etc/template.pdf";
が実行する Python の処理プロセスです。
List<String> list = new ArrayList<>(); は、処理プロセスの標準入力に渡す(流す)ものです。
final AJAXDownload download = AJAXDownload.of(out->{
String script = "python /pdf/sample_pdf.py /etc/template.pdf";
List<String> list = new ArrayList<>();
list.add(SJutil.toUnicode(text1));
list.add("\n");
list.add(SJutil.toUnicode(text2));
list.add("\n");
list.add(SJutil.toUnicode(text3));
list.add("\n");
list.add("\n");
int sts = ScriptExecutor.runStream(()->script, ()->list
, inst->{
try{
byte[] b = new byte[1024];
int len;
while((len=inst.read(b, 0, b.length)) >= 0){
out.write(b, 0, len);
}
out.flush();
out.close();
}catch(Exception ex){
throw new RuntimeException(ex);
}
}, (e, x)->{
logger.error(e);
logger.error(x.getMessage(), x);
});
logger.debug("status = " + sts);
}, ()->"application/pdf", ()->"test.pdf");
queue(new Button("submit")
.add(AjaxFormSubmitBehavior.onSubmit("click",
SerialThrowableConsumer.of(t->{
download.callBackDownload(t);
}, (u, x)->{
logger.error(x.getMessage(), x);
}))).add(download));
実行する Python の処理 → sample_pdf.py
reportlab、pdfrw だけでなく、PyPDF3 を利用して作成した PdfFileReader でメモリ上に読み込んで
PdfFileWriter で、PDF出力するオブジェクトとして取込み、sys.stdout.buffer.write で
バイナリ標準出力します。print ではだめです。
ファイルとしては生成しないメモリ上での生成として、io.BytesIO() を使用するのが重要です
以下、折角なのでページ番号書き出しと、フォント指定などページ全体を共通に使い回すために
pagenumCanvas.py → from pagenumCanvas import PageNumCanvas
ページコンテンツを書きだす samplepage_pdf.py は、page_pdf.py のクラスを継承します
sample_pdf.py
import sys
import io
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from PyPDF3 import PdfFileWriter, PdfFileReader
from pagenumCanvas import PageNumCanvas
from samplepage_pdf import SamplePages
import webbrowser
import os
def create(template, inputlist):
sample = SamplePages(template, inputlist)
packet = io.BytesIO()
doc = SimpleDocTemplate(packet)
doc.build([Paragraph("", getSampleStyleSheet()['Normal'])]
, onFirstPage=sample.page
, onLaterPages=sample.page
, canvasmaker=PageNumCanvas)
new_pdf = PdfFileReader(packet)
output = PdfFileWriter()
for i in range(new_pdf.getNumPages()):
output.addPage(new_pdf.getPage(i))
po = io.BytesIO()
output.write(po)
sys.stdout.buffer.write(po.getvalue())
if __name__ == '__main__':
argv = sys.argv
argvlen = len(argv)
if argvlen != 2:
sys.stderr.write('Error: argument [1]=template file required!')
exit(1)
else:
inlist = []
try:
while True:
inp = input('')
if inp == '': break
inlist.append(inp)
except EOFError:
pass
create(argv[1], inlist)
exit(0)
pagenumCanvas.py
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
class PageNumCanvas(canvas.Canvas):
def __init__(self, *args, **kwargs):
canvas.Canvas.__init__(self, *args, **kwargs)
self.pages = []
def showPage(self):
self.pages.append(dict(self.__dict__))
self._startPage()
def save(self):
page_count = len(self.pages)
for page in self.pages:
self.__dict__.update(page)
self.draw_page_number(page_count)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page_number(self, page_count):
page = "Page %s of %s" % (self._pageNumber, page_count)
self.setFont("Helvetica", 10)
self.drawRightString(195 * mm, 272 * mm, page)
samplepage_pdf.py
from page_pdf import Pdfpage
from pdfrw import PdfReader
from pdfrw.buildxobj import pagexobj
from pdfrw.toreportlab import makerl
import reportlab.rl_config
class SamplePages(Pdfpage):
global page_template
global inlist
center_w = reportlab.rl_config.defaultPageSize[0] / 2.0
center_h = reportlab.rl_config.defaultPageSize[1] / 2.0
def __init__(self, template, inputlist):
super().__init__()
temp_page = PdfReader(template, decompress=False).pages
SamplePages.page_template = pagexobj(temp_page[0])
SamplePages.inlist = inputlist
def page(self, canvas, doc):
for i in range(4):
if i > 0: canvas.showPage()
canvas.doForm(makerl(canvas, SamplePages.page_template))
canvas.setFont(super().HeiseiKakugoW5, 9)
canvas.saveState()
canvas.drawCentredString(SamplePages.center_w, SamplePages.center_h, "あいう テスト")
h = 400
for item in SamplePages.inlist:
canvas.drawCentredString(SamplePages.center_w, h, item.encode().decode('unicode-escape') )
h -= 20
canvas.restoreState()
page_pdf.py
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.pdfbase import pdfmetrics
class Pdfpage():
global HeiseiKakugoW5
def __init__(self):
Pdfpage.HeiseiKakugoW5 = 'HeiseiKakuGo-W5'
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5'))