PHPでエクセルファイルを作成し、PDFとして出力する。(2)

2022/11/30
高橋

こんにちは。株式会社リンクネット、ソリューション事業部の高橋です。
前回の記事PHPでエクセルファイルを作成し、PDFとして出力する。(1)では
Dockerコンテナを起動する度に、sail root-shellでのlibreofficeや日本語フォントのインストールが必要な事が課題として残っておりました。
本記事では、Dockerfileをカスタマイズし、毎回の手動インストールを省略できるようにする方法を共有します。

環境

バージョン情報
$ sail artisan -V
Laravel Framework 8.82.0
$ sail php -v
PHP 8.1.5 (cli) (built: Apr 21 2022 10:15:06) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.5, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.5, Copyright (c), by Zend Technologies
    with Xdebug v3.1.2, Copyright (c) 2002-2021, by Derick Rethans

準備

Dockerコンテナをカスタマイズする際に下記のコマンドを実行します。
Dockerfileをカスタマイズできるようにプロジェクトの管理化に移動します。

Sailカスタマイズ
sail artisan sail:publish

コマンドを実行すると下記のようにdockerディレクトリが生成されます。

追加されたディレクトリ
+  drwxr-xr-x   6 appuser appuser   4096 Nov 22 10:25 docker/

docker-compose.ymlにも下記のように変更されます。
変更内容はDockerfileの参照先が変更されています。

docker-compose.yml
-  context: ./vendor/laravel/sail/runtimes/8.1
+  context: ./docker/8.1
dockerデイレクトリの構造
/home/appuser/project/portal-backend/docker
|--7.4
|  |--Dockerfile
|  |--php.ini
|  |--start-container
|  |--supervisord.conf
|--8.0
|  |--Dockerfile
|  |--php.ini
|  |--start-container
|  |--supervisord.conf
|--8.1
|  |--Dockerfile
|  |--php.ini
|  |--start-container
|  |--supervisord.conf
|--8.2
   |--Dockerfile
   |--php.ini
   |--start-container
   |--supervisord.conf

今回、必要なコンテナは8.1のためdocker-compose.yml参照先の変更、
docker ディレクトリ直下に8.1のファイルを移動します。

docker-compose.yml
-  context: ./docker/8.1
+  context: ./docker/
dockerデイレクトリの構造
./docker
- |--7.4
- |  |--Dockerfile
- |  |--php.ini
- |  |--start-container
- |  |--supervisord.conf
- |--8.0
- |  |--Dockerfile
- |  |--php.ini
- |  |--start-container
- |  |--supervisord.conf
- |--8.1
- |  |--Dockerfile
- |  |--php.ini
- |  |--start-container
- |  |--supervisord.conf
- |--8.2
-    |--Dockerfile
-    |--php.ini
-    |--start-container
-    |--supervisord.conf
+ Dockerfile
+ php.ini
+ start-container
+ supervisord.conf

Dockerfileのカスタマイズ

今回のカスタマイズの目的はsail root-shellで下記のコマンドを手動実行していたインストール作業を
Dockerコンテナ構築時に自動でインストールするようにカスタマイズします。

手動インストール時のコマンド
apt -y install libreoffice libreoffice-l10n-ja libreoffice-dmaths libreoffice-ogltrans libreoffice-writer2xhtml libreoffice-pdfimport libreoffice-help-ja
apt install fonts-ipafont fonts-ipaexfont
fc-cache -fv

上記のコマンドを設定するためDockerfileに下記の内容を追加しました。

Dockerfile
+      && apt -y install libreoffice \
+         libreoffice-l10n-ja \
+         libreoffice-dmaths \
+         libreoffice-ogltrans \
+         libreoffice-writer2xhtml \
+         libreoffice-pdfimport \
+        libreoffice-help-ja \
+      && apt install fonts-ipafont fonts-ipaexfont \
+      && fc-cache -fv \

Dockerコンテナを再構築するため下記のコマンドを実行

Dockerコンテナを再構築する
$sail build --no-cache
Dockerコンテナ再構築のエラー内容
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
E: Unable to locate package  
ERROR: Service 'laravel.test' failed to build: The command  

エラー内容を調査してみるとaptコマンドはCLI向けではないためapt-getコマンドでないとエラーが出るようです。
下記のようにDockerfileを修正し、Dockerコンテナを再構築してみます。

Dockerfile
-      && apt -y install libreoffice \
+      && apt-get -y install libreoffice \
+         libreoffice-l10n-ja \
+         libreoffice-dmaths \
+         libreoffice-ogltrans \
+         libreoffice-writer2xhtml \
+         libreoffice-pdfimport \
+     libreoffice-help-ja \
-      && apt install fonts-ipafont fonts-ipaexfont \
+      && apt-get install fonts-ipafont fonts-ipaexfont \
+      && fc-cache -fv \

警告のWARNINGは消えましたが、エラーについては発生したままです。

Dockerコンテナ再構築のエラー内容
E: Unable to locate package  
ERROR: Service 'laravel.test' failed to build: The command  

確認しましたところ日本語フォント(IPAフォント)のインストール時に
応答が必要なようですので下記のように-yオプションを追加しDockerコンテナを再構築します。

Dockerfile
+      && apt-get -y install libreoffice \
+         libreoffice-l10n-ja \
+         libreoffice-dmaths \
+         libreoffice-ogltrans \
+         libreoffice-writer2xhtml \
+         libreoffice-pdfimport \
+     libreoffice-help-ja \
-      && apt-get install fonts-ipafont fonts-ipaexfont \
+      && apt-get -y install fonts-ipafont fonts-ipaexfont \
+      && fc-cache -fv \

エラーがなくDockerコンテナの再構築は完了しましたので、
libreofficeがインストールされたかsail root-sehllでコンテナに接続し、
/usr/bin/sofficeへのPATHが存在するかで確認します。

libreofficeの確認
$sail root-sehll
root@834dad2d51be:/var/www/html# cd /usr/bin/
root@834dad2d51be:/usr/bin# pwd
/usr/bin
root@834dad2d51be:/usr/bin# ll | grep soffice
lrwxrwxrwx 1 root root         34 Oct 27 10:34 libreoffice -> ../lib/libreoffice/program/soffice*
lrwxrwxrwx 1 root root         34 Oct 27 10:34 soffice -> ../lib/libreoffice/program/soffice*

libreofficeは問題なくインストールされているようです。
続けて日本語フォント(IPAフォント)が登録されているかも併せて確認します。
fc-match :lang=jaでデフォルトのフォントを確認します。

日本語フォント(IPAフォント)の確認
root@834dad2d51be:/var/www/html#
root@834dad2d51be:/var/www/html# fc-match :lang=ja
fonts-japanese-gothic.ttf: "IPAexGothic" "Regular"

日本語フォント(IPAフォント)も問題なく登録できているようです。
次にlibreofficeを使用してエクセルファイルをPDFに変換します。

/usr/bin/soffice --headless --convert-to pdf --outdir /var/www/html/storage/app/sample /var/www/html/public/common/sample/sample.xlsx
convert /var/www/html/public/common/sample/sample.xlsx -> /var/www/html/storage/app/sample/sample.pdf using filter : calc_pdf_Export

変換前と変換後の画像はそれぞれ下記となります。 変換自体は正常に終了し日本語フォントは文字化けせずに表示されています。 今回はPhpSpreadsheetで印刷範囲をしていないため、変換後のPDFは2ページになりました。

alt 変換前のエクセルファイル ・変換前のエクセルファイル

alt 変換後のPDFファイル ・変換後のPDFファイル

前回と同様にPhpSpreadsheetを用いてPHPでエクセルファイルをPDFに変換します。
変換に使用したPHPのソースコードの抜粋と変換後のPDFは下記となります。

PHPでファイル変換の一部を抜粋
        $this -> info('export start');
        $tmp_path = storage_path('app/sample');
        if(!file_exists($tmp_path)){
            mkdir($tmp_path, 0777, true);
        }

        $format = public_path('common/sample/sample.xlsx');
        $tmp_xlsx_path = $tmp_path.'/tmp.xlsx';
        $tmp_pdf_path = $tmp_path.'/sample.pdf';

        $xr = new XReader();
        $xr -> setReadDataOnly(false); //これをfalseにしないと複写できない
        $spread = $xr -> load($format);
        $sheet = $spread -> getActivesheet();
        $sheet -> getPageSetup() -> setPrintArea('A1:AR46');
        $sheet -> getPageSetup() -> setScale(90);
        $sheet -> getPageSetup() -> setPaperSize(PageSetup::PAPERSIZE_A4); //A4サイズ
        //xlsxファイルを出力
        $writer = new XlsxWriter($spread);
        $writer -> save($tmp_xlsx_path);

        $command = '/usr/bin/soffice --headless --convert-to pdf --outdir '.$tmp_path.' '.$tmp_xlsx_path;
        if(exec($command)){
            unlink($tmp_xlsx_path);
        }
        return Command::SUCCESS;

alt PHPで変換したPDFファイル ・変換後のPDFファイル

まとめ

今回は、PDF変換のために必要なlibreofficeや日本語フォントをDockerコンテナ構築時にインストールさせる方法をまとめました。
今後、Dockerコンテナに別のフォントやアプリケーションが必要になった際、
本記事と同様の手法を用いることで様々な案件で応用が可能です。

前の記事次の記事