ผู้เขียน PHP ที่ต้องการสร้างเอกสาร PDF อาจพบเจอปัญหาที่แก้ได้ยากบ่อยครั้งซึ่งเป็นปัญหายอดนิยม นั่นคือเสียงวรรณยุกต์ลอย, หรือสระลอย. ปัญหาดังกล่าวเกิดจากฟอนต์เป็นหลัก เพราะฟอนต์บางตัวก็ทำงานได้ดี แต่บางตัวก็ไม่เป็นเช่นนั้น. การแก้สระลอยในโค้ดต่อไปนี้อาจแก้ได้เฉพาะที่มีเสียงวรรณยุกต์ลอยเท่านั้น (เช่น ไม้เอก, ไม้โท) แต่ไม่สามารถแก้เสียงวรรณยุกต์จม, หรือสระจมได้ เนื่องจากเป็นปัญหาที่ฟอนต์นั้นๆไม่ได้ออกแบบมาให้มีอักขระในส่วนนี้เลย จึงไม่สามารถนำมาใช้ทดแทนแก้ปัญหาได้.
/**
* Fix Thai vowels and characters. (สระลอย, เสียงวรรณยุกต์ลอย)
*
* @link https://gist.github.com/dtinth/716814 Original source code
* @param string $text
* @return string
*/
function fixThaiVowels(string $text): string
{
// เสียงวรรณยุกต์ และทัณฑฆาต (การันต์)
$back = [
"\xE0\xB9\x88" => "\xEF\x9C\x8A", //่(ไม้เอก)
"\xE0\xB9\x89" => "\xEF\x9C\x8B", //(ไม้โท)
"\xE0\xB9\x8A" => "\xEF\x9C\x8C", //(ไม้ตรี)
"\xE0\xB9\x8B" => "\xEF\x9C\x8D", //(ไม้จัตวา)
"\xE0\xB9\x8C" => "\xEF\x9C\x8E" //(ตัวการันต์)
];
$bottomVowels = [
"\xE0\xB8\xB8" => "\xEF\x9C\x98", // สระอุ
"\xE0\xB8\xB9" => "\xEF\x9C\x99", // สระอู
"\xE0\xB8\xBA" => "\xEF\x9C\x9A", // พินธุ
];
$replaces = [];
// ตัวอักษรที่มีความสูงที่จะต้องไม่ให้ชนกับเสียงวรรณยุกต์
$highChars = [
"ิ", // สระอิ
"ี", // สระอี
"ึ", // สระอึ
"ื", // สระอือ
"ั", // ไม้หันอากาศ
"ำ", // สระอำ
"ํ", // สระอำ ที่ไม่มีสระอา
"ป",
"ฝ",
"ฟ",
"ฬ",
];
// ตัวอักษรที่ลากลงด้านล่าง
$lowChars = [
"ฎ",
"ฏ",
"ฤ",
"ฦ",
];
// ตัวอักษรที่มีฐานด้านล่างและจะไปชนกับสระที่อยู่ด้านล่าง ซึ่งจะต้องถูกแทนที่โดยการตัดฐานล่างออกไป
$lowReplaceChars = [
'ญ' => "\xEF\x9C\x8F",
'ฐ' => "\xEF\x9C\x80",
];
// loop กำหนดค่าคืนกลับ (replace) เสียงวรรณยุกต์ที่จะต้องลอยสูง
foreach ($highChars as $p) {
if ($p !== 'ำ' && $p !== 'ํ') {
for ($i = 0x8A; $i <= 0x8E; ++$i) {
$from = $p . "\xEF\x9C" . chr($i);
$to = $p . "\xE0\xB9" . chr($i - 2);
$replaces[$from] = $to;
}// endfor;
unset($i);
} else {
for ($i = 0x8A; $i <= 0x8E; ++$i) {
$from = "\xEF\x9C" . chr($i) . $p;
$to = "\xE0\xB9" . chr($i - 2) . $p;
$replaces[$from] = $to;
}// endfor;
unset($i);
}// endif;
}// endforeach;
unset($highChars, $p);
// loop กำหนดค่าสระด้านล่าง ที่จะต้องเปลี่ยน (replace) เมื่อเจอตัวอักษรที่ลากลงด้านล่าง
foreach ($lowChars as $p) {
foreach ($bottomVowels as $orig => $replace) {
$from = $p . $orig;
$to = $p . $replace;
$replaces[$from] = $to;
}// endforeach;
unset($orig, $replace);
}// endforeach;
unset($lowChars, $p);
// loop กำหนดตัวอักษรที่มีฐานด้านล่าง ที่จะต้องเปลี่ยน (replace) เมื่อมีสระด้านล่างตามมาด้วย
foreach ($lowReplaceChars as $origChar => $replaceChar) {
foreach ($bottomVowels as $orig => $replace) {
$from = $origChar . $orig;
$to = $replaceChar . $orig;
$replaces[$from] = $to;
}// endforeach;
unset($orig, $replace);
}// endforeach;
unset($lowReplaceChars, $origChar, $replaceChar);
$text = strtr($text, $back);
$text = strtr($text, $replaces);
unset($back, $replaces);
return $text;
}// fixThaiVowels
ที่มาของโค้ดด้านบนนี้ แรกเริ่มเดิมทีมาจาก URL https://gist.github.com/dtinth/716814 แต่ได้มีการปรับแต่งเรื่อยมาจนกระทั่งผู้เขียนได้เพิ่มการแก้ปัญหา สระด้านล่างไปชนกับตัวอักษรที่ลากยาวลงมาด้านล่างด้วย เช่น ญ, ฎ เป็นต้น.
ทดสอบ
ในการทดสอบ ผู้เขียนจะใช้โค้ดของ mpdf ซึ่งคุณผู้อ่านจะนำไปใช้กับอะไรอย่างอื่นก็ได้ทั้งหมด. ตัวอย่างโค้ดทดสอบ.
require 'vendor/autoload.php';
function mpdfGetDefaultFonts(): array
{
$defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
$fontDirs = $defaultConfig['fontDir'];
$defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
$fontData = $defaultFontConfig['fontdata'];
// remove not exists font.
unset($fontData['eeyekunicode']);
unset($defaultConfig, $defaultFontConfig);
return [
'fontDirs' => $fontDirs,
'fontData' => $fontData,
];
}
list('fontDirs' => $fontDirs, 'fontData' => $fontData) = mpdfGetDefaultFonts();
$fontDirs = array_merge($fontDirs, [__DIR__ . DIRECTORY_SEPARATOR . 'custom-fonts']);// แก้ไขของคุณ
$fontData = $fontData + [
'sarabunnew' => [
'R' => 'THSarabunNew.ttf',
],
];// แก้ไขของคุณ
$config = [
'default_font' => 'sans-serif',
'default_font_size' => 22,
'fontdata' => $fontData,
'fontDir' => $fontDirs,
];
unset($fontDirs);
$mpdf = new \Mpdf\Mpdf($config);
$fontsToTest = ['garuda', 'zawgyi-one', 'sarabunnew'];
$html = '<!DOCTYPE html><html>' . PHP_EOL;
$html .= '<head><meta charset="utf-8"></head>' . PHP_EOL;
$html .= '<body>' . PHP_EOL;
$html .= '<h1>Fix Thai vowels</h1>' . PHP_EOL;
foreach ($fontsToTest as $font) {
$html .= '<h3>Font: ' . $font . '</h3>' . PHP_EOL;
$html .= '<p style="font-family:' . $font . ';">' . fixThaiVowels('อ่า อิ่ อี่ อึ่ อื่ อ้า อิ้ อี้ อึ้ อื้ อ๊า อิ๊ อี๊ อึ๊ อื๊ อ๋า อิ๋ อี๋ อึ๋ อื๋');
$html .= '</p>' . PHP_EOL;
}
$html .= '</body>' . PHP_EOL;
$html .= '</html>' . PHP_EOL;
$mpdf->WriteHTML($html);
$mpdf->Output();
ผลลัพธ์ควรจะได้คล้ายภาพต่อไปนี้