こんにちは!
MagentoおよびAdobe Commerce(以下まとめてMagento)で請求書などのPDFを発行するとファイル容量がとても大きいのが気になります。今回はGhostscriptを使ってMagentoが生成するPDFの容量を小さくする方法について解説したいと思います。
MagentoのPDF容量問題について
PDFの容量が大きくなる要因として、フォントをフルセットでPDF内に埋め込んでいることが要因にあります。
PDFへのフォントの埋め込みはPDFを表示する端末にフォントがインストールされていない場合でも正しいフォントで表示するためのものですが、フォントを埋め込むとファイルの容量はその分大きくなり、特に日本語フォントを使用した場合にその影響が顕著です。
Magentoで生成されるPDFは、フォントをフルセットで埋め込んでいるため容量が大きくなっており、これを埋め込みサブセット(必要な文字のみを収録したフォントを埋め込む方式)にすることが出来れば容量が改善出来ると考えられます。
MagentoのPDFライブラリは埋め込みサブセット未サポート
MagentoのPDF生成ライブラリには、Zend_PDFが使用されています。
Zend_PDFについて調査したところ残念ながら「埋め込みサブセット」をサポートしていませんでした。もうかなり古いライブラリで現在はメンテナンスもされていないようなので、今後サポートされるというのも期待が出来ません。
他に埋め込みサブセットをサポートしているPDFライブラリではTCPDF等があります。個人的にはTCPDFは色々と高機能かつ使い勝手が良くこれが使えると結構便利だと思うのですが、Magentoに組み込まれているZend_PdfをTCPDFに入れ替えるのはなかなか大変です。
そこで次のアイデアを思い付きました。
Ghostscriptを使ってみる
生成時に埋め込みサブセットが使えないということであれば生成したPDFを後から変換する方法はどうだろう…、ということで何か使えるツールは無いか探してみたところGhostscriptというソフトウェアを見つけました。
Ghostscriptは、Postscript及びPDFのためのインタープリタです。コマンドラインツールで、Postscriptの操作やPDFの操作などを行うことが出来ます。これを使ってPDFを埋め込みサブセット化することが出来そうです。
インストール
redhat系Linuxでは、yumやdnfでインストールが可能です。
sudo dnf install ghostscript
コマンドでファイル変換
gsコマンドを使ってPDFファイルを埋め込みサブセットに変換できます。オプション次第では画像の解像度を調整して容量を抑えることなども可能です。今回は画質は維持しておきたいので高解像度向け設定を指定しています。
gs -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -o <出力ファイル名> <読み込みファイル名>
オプションの説明
-sDEVICE=pdfwrite | PDFファイルを出力 |
-dPDFSETTINGS=/prepress | 用途別にオプション指定をまとめて指定できるもの /screen = 画面表示向け設定 /ebook = 電子書籍向け設定 /printer = 印刷向け設定 /prepress = 版下向け設定 上から順に低解像度 > 高解像度な用途向け設定です。 |
-o | 出力ファイル名指定 |
-dEmbedAllFonts=true ※ | フォントを埋め込む |
-dSubsetFonts=true ※ | 埋め込みサブセットを使用する |
※-dEmbedAllFonts=true -dSubsetFonts=true というオプションでサブセット埋め込みを指定しますが、どちらもデフォルトであるため省略。
このほかにも様々なオプションがあります。
検証
試しに手動でPDFを変換してみます。
Magentoで請求書を発行したものをローカルに置いて試しました。PDFのフォントには日本語フォントの「IPA P ゴシック」を使用しています。
元のファイルは↓こんな感じ。4.2Mもあります。
-rw-r--r-- 1 ec2-user ec2-user 4.2M 1月 24 21:46 1-befoer.pdf
Ghostscriptをコマンド実行してファイル変換してみます
$ gs -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -o 2-after-pdf 1-befoer.pdf
GPL Ghostscript 9.54.0 (2021-03-30)
Copyright (C) 2021 Artifex Software, Inc. All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
Processing pages 1 through 1.
Page 1
変換後のファイル容量を確認
-rw-r--r-- 1 ec2-user ec2-user 4.2M 1月 24 21:46 1-befoer.pdf
-rw-r--r-- 1 ec2-user ec2-user 71K 1月 24 22:58 2-after-pdf
71Kまで小さくなりました!
いい感じです。このファイル変換をMagentoのPDF生成のところに組み込んでみます。
Magentoへの組み込み
Zend_Pdfクラスのrender()メソッドがPDFの最終的な生成を行っているので(その関数の戻り値がPDFの中身そのものとなる)、この戻り値に対してghostscriptを介してファイル変換するようにしてみます。
MagetnoではZend_Pdfクラスは下記の場所にモジュールとして組み込まれています。
vendor\magento\zend-pdf\library\Zend\Pdf.php
このZend_Pdfクラスを継承した新しいクラスを作成し、render()メソッドを改定してPDFの変換を組み込みます。
処理の内容はこんな感じです。
- 親のrender()メソッドからの戻り値を一旦テンポラリファイルへ保存
- ghostscriptコマンドを実行しファイル変換
- 変換済みのファイルを読み込んで戻り値として返す
サンプルコード
<?php
namespace VendorName\PdfSample\lib;
class Pdf extends \Zend_Pdf
{
public function __construct($source = null, $revision = null, $load = false)
{
parent::__construct($source, $revision, $load);
}
public function render($newSegmentOnly = false, $outputStream = null)
{
$content = parent::render($newSegmentOnly, $outputStream);
if ($content != '') {
$inputFile = tempnam(sys_get_temp_dir(), 'pdf-before');
$outputFile = tempnam(sys_get_temp_dir(), 'pdf-after');
file_put_contents($inputFile, $content);
$command = sprintf('gs -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -o %s %s', escapeshellarg($outputFile), escapeshellarg($inputFile));
exec($command, $output, $returnVar);
if ($returnVar == 0) {
$content = file_get_contents($outputFile);
}
unlink($inputFile);
unlink($outputFile);
}
return $content;
}
}
PDF生成箇所への組み込み
PDFを組み立てている箇所にこのクラスを組み込むには、Zend_Pdfクラスをnewしている箇所を探し上記のクラス名に入れ替えます。
対象ソースコード(請求書PDF)
vendor/magento/module-sales/Model/Order/Pdf/Invoice.phpのgetPdf関数内
public function getPdf($invoices = [])
{
$this->_beforeGetPdf();
$this->_initRenderer('invoice');
$pdf = new \VendorName\PdfSample\lib\Pdf(); //←new \Zend_Pdf()を置き換える
$this->_setPdf($pdf);
$style = new \Zend_Pdf_Style();
$this->_setFontBold($style, 10);
:
※Shipment.php、Creditmemo.php等も必要に応じて対応してみてください。
動作確認
Magento の 管理画面 > Sales で、請求書PDFを発行します
PDFがダウンロードされました。ファイル変換されて容量も小さくなっています。
Adobe Acrobat で文書のプロパティを確認するとフォントが埋め込みサブセットになっていることが確認出来ます。
まとめ
いかがでしたか。
Magentoが生成するPDFのファイル容量が劇的に小さくなりました。
今回使用したghostscriptは単体でツールとして使用できるものなので、単にPDFの容量を小さくしたい場面でも役に立つかもしれません。windows用もありますので使ってみてください。