วิธีใช้ภาษาไทยบน Laravel ด้วย laravel-dompdf โดยการเพิ่ม font ภาษาไทยลงไป

บทความนี้เกิดจากคำถามในกลุ่ม Laravel Thailand ซึ่งพบบ่อยพอสมควรว่าจะสร้าง PDF ภาษาไทยยังไง ก็เลยเขียนขึ้นมาซะเลย โดยในบทความนี้จะใช้ laravel-dompdf เป็นหลัก

วิธีการเพิ่ม font ภาษาไทยลง laravel-dompdf

เตรียมโปรเจคสำหรับทดสอบ

ส่วนนี้จะสอนลง laravel-dompdf กับสร้าง PDF แบบพังๆขึ้นมา 1 ตัวเพื่อใช้ทดสอบ ถ้าทำเป็นแล้วข้ามไปได้เลยครับ

  1. ก่อนอื่นก็ต้องลง Laravel แบบปกติก่อน (ไม่สอนนะ น่าจะทำกันเองเป็น) ในเวอร์ชันนี้ผมใช้ Laravel 5.4 แต่คิดว่าตระกูล 5.x สามารถใช้ได้เหมือนกันหมด
  2. สร้างโฟลเดอร์ storage/fonts และแก้ไข permission ให้สามารถเขียนได้ (โดยทั่วไปคือ 0777)
  3. ติดตั้ง laravel-dompdf โดยมีขั้นตอนดังนี้
    1. สั่ง
      composer require barryvdh/laravel-dompdf
    2. แก้ไขไฟล์ config/app.php โดยเพิ่ม
      Barryvdh\DomPDF\ServiceProvider::class,

      ลงไปในส่วนของ providers

      และเพิ่ม

      'PDF' => Barryvdh\DomPDF\Facade::class,
      

      ลงในส่วนของ aliases

  4. สร้างหน้าทดลองสำหรับใช้ PDF (ในขั้นนี้ต้องประยุกต์เอาเองตามความต้องการ หากมีอยู่แล้วข้ามไปข้อ 4 ได้เลย)
    1. สร้าง view สำหรับทดสอบ โดยในตัวอย่างของผมคือ invoice.blade.php ใน resources/views/pdf โดยมีเนื้อหาดังนี้
      <html>
      <head>
      </head>
      <body>
      <h1>ใบแจ้งหนี้สำหรับ คุณ{{ $name }}</h1>
      ขอขอบคุณในการสั่งซื้อ
      </body>
      </html>
    2. สร้าง route หรือ controller ในการสร้าง pdf (เนื่องจากเป็นตัวอย่าง เลยทำเป็น route ง่ายๆ)
      <?php
      
      Route::get('/pdf', function () {
          $data = [
              'name'=>'อะไรสักอย่าง ไม่รู้นามสกุลอะไร'
          ];
          $pdf = PDF::loadView('pdf.invoice', $data);
          return @$pdf->stream();
      });
      

      ในบรรทัดที่ 8 จำเป็นต้องใส่ @ นำหน้า เพื่อปิด error บางตัว ที่อาจจะเกิดจากการหา fonts ไม่เจอ

    3. หลังจากที่ทำตามข้างบนแล้วจะพบว่าเมื่อเข้าไปภาษาไทยยังเป็นเครื่องหมายตกใจอยู่ เพราะข้างบนนั้นยังไม่ได้ทำให้รองรับภาษาไทย
      laravel-dompdf ยังไม่รองรับภาษาไทย

ปรับ laravel dompdf ให้สามารถใช้งาน font ภาษาไทยได้

ในขั้นนี้จะเป็นขั้นตอนจริงๆ เพื่อทำให้ laravel-dompdf รองรับภาษาไทยในการ render ต้องทำตามขั้นตอนต่อไปนี้เพื่อให้รองรับภาษาไทย โดยขั้นตอนนี้เป็นแบบง่าย ซึ่งในการสร้าง pdf ครั้งแรกสุดอาจจะทำให้ช้าได้ โดย font ไทยในครั้งนี้ผมเลือกใช้ THSarabunNew ครับ (ถ้าอยากดาวโหลดมาทำการทดสอบ สามารถดาวโหลดได้จากที่นี่)

  1. ให้สร้างโฟลเดอร์ชื่อ fonts ใน public และนำ font ทั้งหมดไปใส่ไว้ ดังรูป
  2. แก้ไขในส่วนของ views โดยเพิ่ม tag meta ลงไปใน head ของ invoice.blade.php จะได้เป็น
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
    <h1>ใบแจ้งหนี้สำหรับ {{ $name }}</h1>
    ขอขอบคุณในการสั่งซื้อ
    
    </body>
    </html>
  3. เพิ่ม tag style เพื่อประกาศ fonts แบบ runtime และบอกให้ทั้งเอกสารใช้ fonts นั้น วิธีการคือให้แก้ไข  invoice.blade.php ให้เป็นดังนี้
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style>
            @font-face {
                font-family: 'THSarabunNew';
                font-style: normal;
                font-weight: normal;
                src: url("{{ public_path('fonts/THSarabunNew.ttf') }}") format('truetype');
            }
    
            body {
                font-family: "THSarabunNew";
            }
        </style>
    </head>
    <body>
    <h1>ใบแจ้งหนี้สำหรับ {{ $name }}</h1>
    ขอขอบคุณในการสั่งซื้อ
    
    </body>
    </html>

    โดยส่วนของการประกาศ fonts คือบรรทัดที่ 6 – 10 ส่วนสำคัญคือบรรทัดที่ 10 ที่เป็นการบอกว่า fonts อยู่ที่ไหน

    และส่วนที่บอกว่า ให้ใช้ fonts ทั้งเอกสารคือบรรทัดที่ 12 – 14 โดยเราสามารถแก้ไขได้เหมือน css ปกติเลยว่าส่วนไหนใช้ fonts ไหน

    โดยเมื่อเราทำตามขั้นตอนด้านบนแล้วจะได้ดังภาพ ใส่ font ภาษาไทยให้ laravel-dompdf แล้ว สำเร็จบางส่วน

    ปัญหาที่พบบ่อย

    1. เจอ Error ประมาณว่า Failed to open stream ดังภาพนี้ ให้กลับสร้าง folder ชื่อ fonts ใน storage ก่อนนะครับ ผมเขียนไว้แล้ว ข้อ 2 ด้านบน
      หากพบ error failed to open stream ให้ไปสร้าง folder ชื่อ fonts ใน storage ก่อนนะครับ
    2. ถ้าทำตามด้านบนแล้วยังไม่ได้ ให้ลองเปลี่ยนจาก public_path ไปใช้เป็น asset แทนครับ (แต่แบบ asset จะใช้กับ php artisan serve ไม่ได้)
  4. จากข้อก่อนหน้าจะเห็นได้ว่า เราได้ว่ามีบางส่วนมาเป็นภาษาไทยแล้ว แต่บางส่วนยังเป็นเครื่องหมายตกใจอยู่ สาเหตุเป็นเพราะตอนประกาศ font ในข้อก่อนหน้านั้น เราประกาศว่าใช้เฉพาะกับข้อความปกติเท่านั้น (บรรทัด 7-8 ในข้อก่อนหน้า) แต่ h1 นั้นจะให้ข้อความที่เป็นตัวหนา (font-weight: bold;) ดังนั้นเราจึงต้องประกาศเพิ่มให้ครบครับ จึงต้องแก้ invoice.blade.php เป็นดังนี้
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style>
            @font-face {
                font-family: 'THSarabunNew';
                font-style: normal;
                font-weight: normal;
                src: url("{{ public_path('fonts/THSarabunNew.ttf') }}") format('truetype');
            }
            @font-face {
                font-family: 'THSarabunNew';
                font-style: normal;
                font-weight: bold;
                src: url("{{ public_path('fonts/THSarabunNew Bold.ttf') }}") format('truetype');
            }
            @font-face {
                font-family: 'THSarabunNew';
                font-style: italic;
                font-weight: normal;
                src: url("{{ public_path('fonts/THSarabunNew Italic.ttf') }}") format('truetype');
            }
            @font-face {
                font-family: 'THSarabunNew';
                font-style: italic;
                font-weight: bold;
                src: url("{{ public_path('fonts/THSarabunNew BoldItalic.ttf') }}") format('truetype');
            }
    
            body {
                font-family: "THSarabunNew";
            }
        </style>
    </head>
    <body>
    <h1>ใบแจ้งหนี้สำหรับ {{ $name }}</h1>
    ขอขอบคุณในการสั่งซื้อ
    
    </body>
    </html>

    ทีนี้พอเราสร้าง pdf ใหม่จะเห็นว่า สามารถใช้ภาษาไทยได้สมบูรณ์แล้ว laravel-dompdf รองรับภาษาไทยสมบูรณ์แล้ว

ข้อควรระวัง

ในการสร้าง pdf ครั้งแรกมันจะสร้างได้ช้าพอสมควรเนื่องจากมันต้องไป download font มา (จากเครื่องตัวเองนั่นแหละ) จากนั้นมาแปลง fonts เป็น format ของมัน และ cache ลงใน storage/fonts ทำให้ครั้งแรกช้ากว่าปกติพอสมควร แต่หลังจากนั้นจะความเร็วปกติ (เพราะ cache ไว้แล้ว)

ข้อแนะนำอื่นๆ

  1. หากนำไปใช้จริง ตัวเซิฟเวอร์ควรจะลง OPCache ไว้ด้วย เพื่อความรวดเร็วในการสร้าง PDF