บทความนี้เกิดจากคำถามในกลุ่ม Laravel Thailand ซึ่งพบบ่อยพอสมควรว่าจะสร้าง PDF ภาษาไทยยังไง ก็เลยเขียนขึ้นมาซะเลย โดยในบทความนี้จะใช้ laravel-dompdf เป็นหลัก
วิธีการเพิ่ม font ภาษาไทยลง laravel-dompdf
เตรียมโปรเจคสำหรับทดสอบ
ส่วนนี้จะสอนลง laravel-dompdf กับสร้าง PDF แบบพังๆขึ้นมา 1 ตัวเพื่อใช้ทดสอบ ถ้าทำเป็นแล้วข้ามไปได้เลยครับ
- ก่อนอื่นก็ต้องลง Laravel แบบปกติก่อน (ไม่สอนนะ น่าจะทำกันเองเป็น) ในเวอร์ชันนี้ผมใช้ Laravel 5.4 แต่คิดว่าตระกูล 5.x สามารถใช้ได้เหมือนกันหมด
- สร้างโฟลเดอร์ storage/fonts และแก้ไข permission ให้สามารถเขียนได้ (โดยทั่วไปคือ 0777)
- ติดตั้ง laravel-dompdf โดยมีขั้นตอนดังนี้
- สั่ง
composer require barryvdh/laravel-dompdf
- แก้ไขไฟล์ config/app.php โดยเพิ่ม
Barryvdh\DomPDF\ServiceProvider::class,
ลงไปในส่วนของ providers
และเพิ่ม
'PDF' => Barryvdh\DomPDF\Facade::class,
ลงในส่วนของ aliases
- สร้างหน้าทดลองสำหรับใช้ PDF (ในขั้นนี้ต้องประยุกต์เอาเองตามความต้องการ หากมีอยู่แล้วข้ามไปข้อ 4 ได้เลย)
- สร้าง view สำหรับทดสอบ โดยในตัวอย่างของผมคือ invoice.blade.php ใน resources/views/pdf โดยมีเนื้อหาดังนี้
<html>
<head>
</head>
<body>
<h1>ใบแจ้งหนี้สำหรับ คุณ{{ $name }}</h1>
ขอขอบคุณในการสั่งซื้อ
</body>
</html>
- สร้าง route หรือ controller ในการสร้าง pdf (เนื่องจากเป็นตัวอย่าง เลยทำเป็น route ง่ายๆ)
<?php
Route::get('/pdf', function () {
$data = [
'name'=>'อะไรสักอย่าง ไม่รู้นามสกุลอะไร'
];
$pdf = PDF::loadView('pdf.invoice', $data);
return @$pdf->stream();
});
ในบรรทัดที่ 8 จำเป็นต้องใส่ @ นำหน้า เพื่อปิด error บางตัว ที่อาจจะเกิดจากการหา fonts ไม่เจอ
- หลังจากที่ทำตามข้างบนแล้วจะพบว่าเมื่อเข้าไปภาษาไทยยังเป็นเครื่องหมายตกใจอยู่ เพราะข้างบนนั้นยังไม่ได้ทำให้รองรับภาษาไทย
ปรับ laravel dompdf ให้สามารถใช้งาน font ภาษาไทยได้
ในขั้นนี้จะเป็นขั้นตอนจริงๆ เพื่อทำให้ laravel-dompdf รองรับภาษาไทยในการ render ต้องทำตามขั้นตอนต่อไปนี้เพื่อให้รองรับภาษาไทย โดยขั้นตอนนี้เป็นแบบง่าย ซึ่งในการสร้าง pdf ครั้งแรกสุดอาจจะทำให้ช้าได้ โดย font ไทยในครั้งนี้ผมเลือกใช้ THSarabunNew ครับ (ถ้าอยากดาวโหลดมาทำการทดสอบ สามารถดาวโหลดได้จากที่นี่)
- ให้สร้างโฟลเดอร์ชื่อ fonts ใน public และนำ font ทั้งหมดไปใส่ไว้ ดังรูป
- แก้ไขในส่วนของ 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>
- เพิ่ม 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 ไหน
โดยเมื่อเราทำตามขั้นตอนด้านบนแล้วจะได้ดังภาพ
ปัญหาที่พบบ่อย
- เจอ Error ประมาณว่า Failed to open stream ดังภาพนี้ ให้กลับสร้าง folder ชื่อ fonts ใน storage ก่อนนะครับ ผมเขียนไว้แล้ว ข้อ 2 ด้านบน
- ถ้าทำตามด้านบนแล้วยังไม่ได้ ให้ลองเปลี่ยนจาก public_path ไปใช้เป็น asset แทนครับ (แต่แบบ asset จะใช้กับ php artisan serve ไม่ได้)
- จากข้อก่อนหน้าจะเห็นได้ว่า เราได้ว่ามีบางส่วนมาเป็นภาษาไทยแล้ว แต่บางส่วนยังเป็นเครื่องหมายตกใจอยู่ สาเหตุเป็นเพราะตอนประกาศ 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 ใหม่จะเห็นว่า สามารถใช้ภาษาไทยได้สมบูรณ์แล้ว
ข้อควรระวัง
ในการสร้าง pdf ครั้งแรกมันจะสร้างได้ช้าพอสมควรเนื่องจากมันต้องไป download font มา (จากเครื่องตัวเองนั่นแหละ) จากนั้นมาแปลง fonts เป็น format ของมัน และ cache ลงใน storage/fonts ทำให้ครั้งแรกช้ากว่าปกติพอสมควร แต่หลังจากนั้นจะความเร็วปกติ (เพราะ cache ไว้แล้ว)
ข้อแนะนำอื่นๆ
- หากนำไปใช้จริง ตัวเซิฟเวอร์ควรจะลง OPCache ไว้ด้วย เพื่อความรวดเร็วในการสร้าง PDF