PythonでPDF作成→Javaで受け取り→Wicket AJAXダウンロード

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";

	// String test1~3 → list
	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");

/* submitボタン click で ↑ を callback */
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

# -*- coding: utf-8 -*-
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

# PDF 作成
def create(template, inputlist):
    # Samplepage インスタンス、テンプレートと入力リストを渡す。
    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
        # 文字フォントセット宣言
        #pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5'))

        # PDF作成実行
        create(argv[1], inlist)
        exit(0)


pagenumCanvas.py

# -*- coding: UTF-8 -*-
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

# -*- coding: utf-8 -*-
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])
        # 入力リスト取得 → global inlist
        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 出力 -----
            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

# -*- coding: utf-8 -*-
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'))