Laravelのdusk、tcpdf使用メモ

## はじめに
larvelでテストツール(dusk)を使用し画像ショット作成。
その画像が一つに収まらない時、複数ページを取り合えす取得。
それをtcpdfで１つのpdfにしている。
chromedriverが必要とかSelenumが必要とか忘れた。
すぐに忘れるので、まだ多少動くものがあるうちにメモだけでも残す。

## 大まかな手順
前提:dockerを使用している
以下の環境を元に作成

https://qiita.com/kenji123/items/0c3e0a8c331b2847123e


Laravel DuskをDocker環境でインストールするには、いくつかのステップを踏む必要があります。以下は、そのプロセスです。

1. **Laravel Duskのインストール**: まず、Laravelプロジェクトのルートにあるコンテナ内でDuskをインストールします。

   ```bash
   docker-compose exec app composer require --dev laravel/dusk
   ```

2. **Duskのセットアップ**: Duskをセットアップします。これには、Duskのサービスプロバイダーを登録するために、`AppServiceProvider`にコードを追加したり、Dusk専用の`.env.dusk.local`環境設定ファイルを作成したりすることが含まれます。

   ```bash
   docker-compose exec app php artisan dusk:install
   ```

   `dusk:install` コマンドは、Duskのテストを書くためのテンプレートファイルを生成し、必要な設定ファイルをセットアップします。

3. **Dockerfileの修正**: DuskはデフォルトでGoogle Chromeを使用します。そのため、Dockerコンテナ内にChromeとChromeDriverをインストールする必要があります。`Dockerfile`に以下のようなコマンドを追加して、これらの依存関係をインストールします。

   ```Dockerfile
   # Chromeのインストール
   RUN apt-get update && apt-get install -y libnss3-dev libxss1 libasound2 libxrandr2 libatk-bridge2.0-0 libcups2 libgtk-3-0 libgbm1
   
   # ChromeDriverのダウンロードとインストール
   RUN LATEST_CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
       curl -sS -o /tmp/chromedriver_linux64.zip https://chromedriver.storage.googleapis.com/$LATEST_CHROMEDRIVER_VERSION/chromedriver_linux64.zip && \
       unzip /tmp/chromedriver_linux64.zip -d /usr/local/bin/ && \
       chmod +x /usr/local/bin/chromedriver
   ```

   Dockerfileに変更を加えたら、Dockerイメージを再ビルドする必要があります。

   ```bash
   docker-compose up -d --build
   ```

4. **Duskの実行**: 環境が整ったら、Duskテストを実行してみましょう。

   ```bash
   docker-compose exec app php artisan dusk
   ```

Duskのテスト実行時には、ブラウザがGUIモードで起動しようとするので、ヘッドレスモードで動作するように`.env.dusk.local`環境ファイルを設定することを忘れないでください。たとえば、以下のような設定を行います。
   

### Seleniumサービスの確認

1. **`docker-compose.yml`の確認**: Seleniumサーバーを`docker-compose.yml`に追加しているか確認してください。もし追加していない場合は、以下のようにサービスを追加する必要があります。

```yaml
version: '3'
services:
  # 既存のサービス（app, nginx, db, ...）

  selenium:
    image: selenium/standalone-chrome:latest
    ports:
      - "4444:4444"
    networks:
      - laravel
    # その他の必要なオプションがあれば追加する

networks:
  laravel:
    driver: bridge
```

2. **ネットワークの接続確認**: Seleniumサーバーが`laravel`ネットワークに接続していることを確認します。`docker-compose.yml`内で他のサービスと同じネットワークに所属している必要があります。

3. **コンテナの再起動**: 変更を適用するために、Dockerコンテナを再起動します。

```bash
docker-compose down
docker-compose up -d
```

### Duskの設定の確認

1. **`.env.dusk.local`の確認**: `.env.dusk.local` ファイルがプロジェクトのルートにあるか確認し、適切な設定がされているか確認してください。

```env
APP_URL=http://localhost:8000
DUSK_DRIVER_URL=http://selenium:4444/wd/hub
```

Laravel Duskでテストを作成するには、artisanコマンドを使ってテストファイルを生成します。以下のコマンドをコンテナ内で実行して、新しいDuskテストファイルを作成します。

```bash
docker-compose exec app php artisan dusk:make NewExampleTest
```

このコマンドは、`NewExampleTest` という名前で新しいDuskテストクラスを作成します。このテストクラスは `tests/Browser` ディレクトリ内に配置されます。

```php
<?php

namespace Tests\Browser;

use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class NewExampleTest extends DuskTestCase
{
    /**
     * A Dusk test example.
     */
    public function testExample(): void
    {
        // $this->browse(function (Browser $browser) {
        //     $browser->visit('https://www.yahoo.co.jp')
        //         ->assertSee('Yahoo');

        //     // JavaScriptを実行してページの最下部へスクロール
        //     $browser->script('window.scrollTo(0, document.body.scrollHeight);');

        //     // 少し待機してからスクリーンショットを撮影
        //     $browser->pause(1000) // 1000ミリ秒（1秒）待機
        //         ->screenshot('yahoo_homepage'); // スクリーンショットを撮影
        // });
        // $this->browse(function (Browser $browser) {
        //     $browser->visit('https://www.yahoo.co.jp')
        //         ->maximize()
        //         ->screenshot('yahoo_homepage'); // スクリーンショットを撮影

        // });
        $this->browse(function (Browser $browser) {
            // Yahoo Japanのページを開く
            $browser->visit('https://www.yahoo.co.jp');

            // スクロールする回数を決定する
            $scrolls = 3; // 例として3回スクロールする
            $scrollHeight = 1000; // 各スクロールで移動する高さ

            for ($i = 0; $i < $scrolls; $i++) {
                // ページをスクロールする
                $browser->script("window.scrollTo(0, $scrollHeight * $i);");

                // スクロール後に描画が完了するまで少し待機
                $browser->pause(1000); // 1秒間待機

                // スクリーンショットを撮影
                $browser->screenshot("page_part_{$i}");
            }
        });
    }
}


```




2. **Duskの実行**: 上記の設定を行った後、Duskテストを再度実行します。

```bash
docker-compose exec app php artisan dusk
```


![スクリーンショット 2023-11-20 22.03.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/1678b463-bb2e-61d6-8d07-624ab555333d.png)


上記のpngを一つにする
```php 
PdfController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use TCPDF;
use Illuminate\Support\Facades\File; // これを追加します


class PdfController extends Controller
{
    public function createPdfFromImages()
    {

        // 新しいPDFドキュメントを作成
        $pdf = new TCPDF();

        // ヘッダーとフッターを削除
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);

        // 画像ファイルの配列
        // $images = [
        //     public_path('screenshots/page_part_0.png'),
        //     public_path('screenshots/page_part_1.png'),
        //     public_path('screenshots/page_part_2.png'),
        //     public_path('screenshots/yahoo_homepage.png'),
        // ];

        $images = [
            base_path('tests/Browser/screenshots/page_part_0.png'),
            base_path('tests/Browser/screenshots/page_part_1.png'),
            base_path('tests/Browser/screenshots/page_part_2.png'),
            base_path('tests/Browser/screenshots/yahoo_homepage.png'),
            base_path('tests/Browser/screenshots/peach-1.jpg'),

        ];

        // 各画像をPDFに追加
        // foreach ($images as $image) {
        //     $pdf->AddPage();

        //     // 画像をページに挿入
        //     $pdf->Image($image, 15, 25, 180, 0, 'PNG');
        // }
        foreach ($images as $image) {
            $pdf->AddPage();

            // ページ全体の背景色を変える場合
            $pdf->SetFillColor(0, 0, 255); // RGBで青色
            // ページ全体に背景色を適用
            $pdf->Rect(0, 0, $pdf->GetPageWidth(), $pdf->GetPageHeight(), 'F');

            // ファイルの拡張子に基づいて形式を設定
            $imageType = strtolower(pathinfo($image, PATHINFO_EXTENSION));
            if ($imageType == 'jpg' || $imageType == 'jpeg') {
                $imageType = 'JPG';
            } elseif ($imageType == 'png') {
                $imageType = 'PNG';
            } // 他の画像形式も同様に追加することができます

            // 画像をページに挿入
            $pdf->Image($image, 15, 25, 180, 0, $imageType);
        }

        // PDFをファイルとして出力
        $pdfFileName = public_path('screenshots/combined_images.pdf');
        // ディレクトリが存在しない場合は作成する
        if (!File::isDirectory(dirname($pdfFileName))) {
            File::makeDirectory(dirname($pdfFileName), 0755, true, true);
        }

        $pdf->Output($pdfFileName, 'F');

        return response()->download($pdfFileName);
    }
}


```

web.php
```
use App\Http\Controllers\PdfController;

Route::get('/create-pdf', [PdfController::class, 'createPdfFromImages']);
```

以下のような感じでダウンロードできる
![スクリーンショット 2023-11-20 23.03.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/39cdd275-a978-b5e2-f9e2-1e9fb58bd076.png)

PDFのページ例
![スクリーンショット 2023-11-20 23.06.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/62da3fba-48f1-f27b-052d-e0379038da6c.png)

追記
１ページに収めたい場合
ある特定の要素までスクロールさせて高さを取得。

```php
            $browser->visit('あるアドレス')
                ->waitFor('特定の要素', 30)
                ->scrollTo('特定の要素');


            $height = $browser->driver->executeScript("return document.body.scrollHeight");
            echo "Height: " . $height . "\n";

            // ページのトップまでスクロールし直す
            $browser->driver->executeScript('window.scrollTo(0, 0);');
            sleep(1); // スクロール後にレンダリングを安定させるために少し待つ

            $browser->resize(1920, $height)
                ->screenshot('yahoo_screenshot');
```