こんにちは、Wonder Soft Coffee の開発者です。
みなさん、cline 利用してますか?弊社では日常使いになりつつあります。
本日は、WeasyPrint を利用した際に味わった苦い開発体験をご紹介したいと思います。
部分的な解決策には至りましたが、果たしてこれで良かったのか、、という点もあり、もしよろしければ最後までご覧いただけますと幸いです。
Background
帳票作成(と聞くと、もう離脱する読者の方もいるかもしれません)を実装する際に、みなさんはどんな方法で実装しますか?
Javaなどの超有名なOSSのPDFBoxや、有償で大手のSVFとかでしょうか。
最近だとNode.jsのhtml2canvasなどもあるし、Pythonにもwkhtmltopdf等等いくつかライブラリがあるのでそういった方法もあるかと思います。
GoにもPDFBoxのように座標を指定してPDF生成する高速に動く良いライブラリがあったと思います。
弊社ではOGPの生成やちょっとしたPNG, PDFの生成でWasyPrintを利用する機会が多く、WasyPrint を利用することもあります。
WeasyPrintって?
WeasyPrint は、HTMLで実装した内容を、 PDFに変換することのできるOSSツールです。
これだけ聞くと、wkhtmltopdf とその特徴はほぼ一緒ですが、WeasyPrint の方が新しいCSS3に比較的対応しており、Pythonからもとても利用しやすいです。
PDFBoxなどの場合は、座標(X,Y)を指定して、それらを結んで表を作成する、という実装になりますが、上記の2つの場合は、HTMLで table タグを作ることで変換ができ、開発者がHTMLを実装できれば表をPDFにすることもできる、というツールとなります。
以下のような感じでPDFファイル生成ができます。
template = Template(template_content)
# JSONデータをテンプレートに適用
html_content = template.render(**json_data)
# 一時ファイルにHTMLを保存(WeasyPrintがファイルパスを必要とする場合のため)
with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as temp_html:
temp_html_path = temp_html.name
temp_html.write(html_content.encode('utf-8'))
try:
# フォント設定を初期化
font_config = FontConfiguration()
# WeasyPrintでPDFを生成
html = HTML(filename=temp_html_path)
# CSSがある場合は適用
stylesheets = []
if css_path and os.path.exists(css_path):
stylesheets.append(CSS(filename=css_path, font_config=font_config))
# PDFの生成(フォント設定を適用)
pdf_content = html.write_pdf(stylesheets=stylesheets, font_config=font_config)
帳票作成の経緯
とある件で、帳票のようなものを作成することになりました。以降は「帳票」と記載します。
(「のようなもの」と記載したのは、広義な意味での帳票は広く、狭義な意味だと商取引・会計系を指すと思いますが、「これが帳票だ!」というシーンがそこまで見当たらず、恐る恐るこの言葉も使っています。)
帳票を欲した人たちは「まぁ比較的簡単な構成だから」と言い、
一旦受け取った人たちも「そうね。比較的簡単ね」と言い、
実装をすることになった人たちも「そこまで複雑ではないしこれなら大丈夫そう」と言う
よくある会話のレーンが発生して設計・開発がスタートします。
(もちろん帳票自体の要求・要件についてはきちんと会話をしています。)
帳票作成の選択肢
前置きは長くなりましたが選択肢です。
①PDFBox等の座標系のライブラリでゴリ押しして実装する
②Excelファイルで定義はされてたのでlibreofficeなどで自動変換する
③HTMLからPDFへ変換するツールを利用する
④有償ツール(AdobeやSVF等等)
生成AIの力を借りて①、というのが非常に魅力的でしたが、すぐに「レイアウト」を生成AIに伝えるコストの方が大きそうなことに気づき却下しました。表をMarkdownとかにして伝えようともいましたがどうしてもうまくいかず。。時間があれば再チャレンジはどこかでしたいところです。また、実装してもらってからcompile --> 出力 --> 確認、という流れが長くなかなか大変そうでした。
②は、ほぼ実装コストがないのでは、と期待に胸を膨らませましたが、エクセル自体のレイアウト調整、という苦行が運用保守後に続くことがチラチラと垣間見えすぎて、かつ、神エクセルが生まれる分岐点にも感じられて却下しました。
残る③については、まず、Node.js系については、完全自動化をしようとした際にheadless browser のインストールが登場し、その container build の運用保守コストの方が帳票自体のレイアウトや中身の保守よりも大きくなるような気配を感じ却下しました。というところで、WeasyPrint としました。
④は有償ツールを利用する規模になれば、と、なりました。
途中まではうまくいっていた
VSCodeを開いて、cline に指示を出します。
今から、HTMLファイルの作成をお願いしていきます。
templates
上記のフォルダに、WeasyPrint で A3 で横向きに出力するための HTML ファイルを作成して欲しいです。
まずは中身は空っぽで良いです。
上記のようにお願いをすると以下のようなものができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A3 横向きテンプレート</title>
<style>
@page {
size: A3 landscape;
margin: 1cm;
}
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: sans-serif;
}
.content {
padding-left: 15mm;
padding-right: 15mm;
padding-top: 0;
padding-bottom: 0;
}
</style>
</head>
</html>
大枠を用意してくれて、A3で出力もできる、という点で期待値が高まります。 さらにお願いをしていきます。
19列29行のテーブルを作ってもらえますか?ヘッダー行は別途作ってください。
それを、全体の2/3程度を占めるくらいの高さに調整して欲しいです。
割と雑にお願いをしていますがきちんと作ってくれました。
この後にだんだんと怪しくなっていきます。
左3列のヘッダーだけ結合してもらえますか?
また、結合したセルについては、ワンダーソフトコーヒー、と記載してください。真ん中でお願いします。
帳票あるあるだと思いますが、列・行を縦横無尽(?)に結合が求められます。 最初の1行はうまくいきましたが、
10列と11列目と12列目の3行目と4行目のセルたち結合してもらえますか?
また、結合したセルについては、ドリップバッグ、と記載してください。真ん中でお願いします。
みたいにしていくと、だんだんと、行と列の理解が生成AI側で怪しくなっていきます。 気づけば、、、以下みたいなことを何度もお願いしていて、だんだん苦い思いをすることが増えていきました。
いや、今度は10列目になってるので、11列目にお願いします。
あと、11列目のヘッダー行の文言は一旦削除してください。「ホットコーヒー」から始まる文言です。
無限ループに遭遇
clineを利用しているときに、もしかしたら遭遇した人たちもいるかもしれませんが、何度指示を出してもずっと同じことを繰り返してしまうことがあります。
今回遭遇しました。ただ、WeasyPrintの問題、というよりは、HTMLのtableの問題だとは思います。
具体的には以下です。
main-table の、
15行目について修正依頼です。
14列から最後の列までを結合してください。そしてそのセルに、「コーヒーの抽出方法」と記載してください。
上記を指示した後に、clineが、HTMLを修正してはくれるのですが、以下のように報告をしてきます。
変更内容:
main-tableの15行目(3行目)の14列目から最後の列までを横方向に結合しました
結合したセルに「コーヒーの抽出方法」というテキストを追加しました、
15行目に書いたよ、というのですが3行目で実施をしてきました。。何かこちらが間違えたのかな、と思いつつ、もう一度お願いをしてみます。
あれ。。。
3行目に修正が入ってますが、違いますよ。
tbodyの中の1行目から数えて15行目でお願いできます?
しかし結果は変わらず、、
ファイルの更新が成功しました。ユーザーの要求に応じて、main-tableの3行目(tbodyの中の1行目から数えて3行目)の14列目から最後の列までを横方向に結合し、結合したセルに「コーヒーの抽出方法」というテキストを表示しました。
こんなやり取りをなん度も繰り返しました。
出来の悪いタイムリープものの小説の中に入り込んだような錯覚を覚えつつ、そんな中、課金だけは進んで行きました。今は円高だし多少は大丈夫、と自身に言い聞かせながらも、一向に治る気配もなく、徐々に嫌な予感を覚えるとき特有のお腹の底が寒くなる感覚に襲われ始めます。。
コーヒーも冷めてしまい、妙に冷静になったタイミングで、行を10行以上跨ぎ、さらに、行や列を途中で結合することは無理なんだな。。と結論づけました。
どう進めたのか
とはいえ、タスクは進める必要がありました。
そこで、帳票のレイアウト自体を複数のテーブルのブロックとして分割をして個別のHTMLとしてclineに指示を出すようにしました。

例えばですが、A〜Fまでの要素にHTML自体をまずはファイル単位で分けてしまい、それぞれをclineで指示を出し、最後に、人間の手で、合体をする、という手段を取りました。
もちろん合体させる部分も Python の Jinja2 などを使えばもっと効率よくできたかもしれませんが、今後の課題としたいと考えています。
コーヒーとともに育む開発文化
ファイルを分割することで解決の兆しが見えたタイミングで、コーヒーを淹れて一息つくことができました。もしかしたもっといい方法があったのかもしれない、と思うので、もし良い方法をご存知の方は教えていただけますと幸いです。(ドリップバッグコーヒーをいくつか贈呈したく思います。)
今後も、ソフトウェア開発やデザインのあれこれについて、コーヒーとともにゆるく投稿していきますので、どうぞお楽しみに ☕✨
🔗 Wonder Soft Coffeeのオンラインストアはこちら:
👉 https://coffee.wonder-soft.com/collections/all