0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

(続)SVG帳票

Last updated at Posted at 2024-01-23

前回(SVG帳票)、SVGで帳票を作れるようにしたが問題がいくつかある。印刷ダイアログで調整が必要になる、ページ繰越しをどうするかなどだ。

印刷ダイアログではそのままだと(自分の環境では?)なぜか縮小されてしまうので200%などにしないとあわない。ダイアログは最後の状態を記憶しているので毎回設定する必要はないが別の帳票などで変更したりすると再度設定の必要があり使い勝手が悪い。いろいろ試したがうまくいかないので諦めて次の課題、ページ繰越しに移ることにした。

ページ繰越しのロジックを作ってそのまま複数ページにすることもできるがページ毎にダイアログがでる訳でそんなアホなことはしたくない。また、SVGにはページの概念がないので複数ページをくっつけて一つにしてもうまくいかないだろう。さて、どうするか。PDFに変換してまとめればよいのではないか。ツールを探すとlibrsvgでできそうだ。これを誰がやるか。クライアントサイドでやるにはユーザーにダウンロードさせたりしなくてはならず面倒そう。サーバーに投げて結果を返してもらえばよい。サーバーサイドはFastAPIなのでそれにやらせることにした。

UPLOAD_DIR = "./files"
def get_pdf(db: Session, request: schemas.File):
    # SVGを保存
    i = 0
    for svg in request.file:
        f = open(os.path.join(UPLOAD_DIR, str(i).zfill(3) + "-" + request.name + ".svg"), 'w')
        f.write(svg)
        f.close
        i += 1
    # PDFに変換
    subprocess.run("rsvg-convert -f pdf -o " + os.path.join(UPLOAD_DIR, request.name + ".pdf") + " " + os.path.join(UPLOAD_DIR, "*" + request.name + ".svg"), shell=True)
    response = FileResponse(
        path = os.path.join(UPLOAD_DIR, request.name + ".pdf"),
        filename = request.name + ".pdf"
    )
    subprocess.run("rm -f " + os.path.join(UPLOAD_DIR, "*" + request.name + ".svg"), shell=True)
    return response

こんな感じ。
Vue側ではSVGのArrayをAxiosで投げる

getPdf: function(name) {
    setTimeout(() => {                   
        const data = {
            fid: this.$store.state.facilityId,
            name: name, 
            file: this.uploadSvg,                 
        };
        const url = this.$store.state.apiUrl + "/files/"
        axios
            .post(url, data, {
                withCredentials: true,
                responseType: 'blob', // これ重要 blob or arraybuffer
            })
            .then((res) => {                           
                const blob = new Blob([res.data], { type: "application/pdf" })
                const url = URL.createObjectURL(blob)
                this.$router.go(-1) // 戻る
                const winPrint = window.open(url)
                winPrint.print()                         
            })
            .catch((error) => {
                console.log(error);   
            })
            .finally()
    }, this.wait)
},

こんな感じ。responseTypeはblobでもarraybufferでもよいみたいだがこれがないと真っ白なページが返ってきて1日悩むことになるので注意。

SVGで200%拡大しないといけない問題も同時に解決した。しかし、試してみると1ページなら問題ないが複数ページだとPDFのウインドウが開いたり開かなかったりする。調べるとFastAPI側でPDFはちゃんと作られている。あれこれ悩んだが最終的にブラウザにポップアップをブロックされていることが原因だった。これはブラウザで許可してやるしかない。

あと、SVGでは文字数が多いとsvg-paperが横に圧縮していい感じに収めてくれていたがPDFではフォントそのそのまなので枠からはみ出る。これは事前に文字数をカウントしてsetAttributeでフォントサイズを小さくすることで対応した。

ということで帳票はなんとかなりそうだ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?