- 本文地址: https://www.yangdx.com/2020/03/111.html
- 转载请注明出处
最近有个需求,根据前台用户输入的内容在后台生成宣传图。
我的方法是,把内容渲染成一个 HTML 页面,然后使用 wkhtmltoimage 把网页转换成图片。遇到了一个问题:用户输入的内容带有 Emoji 表情符号,由于 Linux 服务器上没有对应的字体问题,生成图片的时候,Emoji 表情符号全变成了无法识别的框框。
当时我想,要是能把 iOS 系统的 Emoji 表情字体文件 Copy 过来用,那就完美了。在网上搜罗了很久,最终无功而返, iOS 系统的字体文件不能直接在其他系统上使用!
最后,我打算用最笨的方法,就是把 Emoji 表情符号替换成 PNG 图片来显示。即把原来的 Emoji 符号替换成 HTML 的 img 标签。
在 https://www.unicode.org/ 找到了所有 Emoji 表情对应的图片。两个页面,每个页面大概 25MB,打开很慢的,地址分别是:
- https://www.unicode.org/emoji/charts/full-emoji-list.html :所有表情,人物表情只含默认肤色。
- https://www.unicode.org/emoji/charts/full-emoji-modifiers.html :补充人物表情,即剩余的所有肤色。
两个页面的内容格式如下:
这两个页面都列举了每个 Emoji 表情的 Unicode 编码,以及各厂商自己实现的表情效果(图),查看源代码可以发现表情图片是直接嵌入的 base64 数据。我们要把 Code 和 Appl 这两列的数据保存下来,并做一个映射关系。
下面是我整理的 PHP 脚本:
<?php
set_time_limit(0);
$files = [
'full-emoji-list.html' => 'https://www.unicode.org/emoji/charts/full-emoji-list.html',
'full-emoji-modifiers.html' => 'https://www.unicode.org/emoji/charts/full-emoji-modifiers.html'
];
$path = 'images/';
$arr = [];
if (!is_dir($path)) {
mkdir($path);
}
foreach ($files as $file => $url) {
if (!is_file($file)) {
echo "正在下载文件 $file ...\n";
$opts = [
'http' => [
'method' => 'GET',
'timeout' => 1800
],
];
$context = stream_context_create($opts);
$str = @file_get_contents($url, false, $context);
if (stripos($str, '</html>') === false) {
echo "下载失败,建议您手动下载保存到本目录。\n";
exit;
}
file_put_contents($file, $str);
} else {
$str = file_get_contents($file);
}
$pattern = "#<tr><td class='rchars'>(\d+)</td>\n"
. "<td class='code'><a href='(?:.+?)' name='(?:.+?)'>(.+?)</a></td>\n"
. "<td class='chars'>(?:.+?)</td>\n"
. "<td class='andr(?: alt)?(?: miss)?'>(?:—|<img alt='(?:.+?)' class='imga' src='(.+?)'>)</td>#";
preg_match_all($pattern, $str, $m);
foreach ($m[1] as $k => $v) {
$num = $v;
$code = $m[2][$k];
$data = $m[3][$k];
if (!$data) {
continue;
}
// 代码格式转换,如 "U+1F9D1 U+200D U+1F3A8" 转换成 "1F9D1-200D-1F3A8"
$code = str_replace(['U+', ' '], ['', '-'], $code);
$filename = $code . '.png';
$data = str_replace('data:image/png;base64,', '', $data);
$data = base64_decode($data);
file_put_contents($path . $filename, $data);
echo "已保存 $filename\n";
$arr[] = $code;
}
}
usort($arr, function ($a, $b) {
return strlen($b) <=> strlen($a);
});
$result = json_encode($arr, JSON_PRETTY_PRINT);
$data = '<?php' . "\n" . 'return ' . $result . ';';
file_put_contents('emoji.php', $data);
$count = count($arr);
echo "已生成 emoji.php,共 $count 个表情\n";
将以上代码保存为 index.php,然后在命令行模式下执行 php index.php
。执行完毕,会在该目录下生成 emoji.php 文件,表情图片保存在 images 子目录里(3000多个图片)。文件 emoji.php 定义了一个数组,数组元素加上后缀名 .png,就是 images 目录下表情图片的文件名。
文件 emoji.php 的内容大致如下:
<?php
return [
"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468",
"1F469-200D-2764-FE0F-200D-1F48B-200D-1F468",
//...此处省略很多行...
"1F3F4-E0067-E0062-E0077-E006C-E0073-E007F",
//...此处省略很多行...
"1F468-200D-2764-FE0F-200D-1F468",
//...此处省略很多行...
"270C-1F3FE",
//...此处省略很多行...
"264F",
"2651",
"2197"
];
Emoji 表情比较特殊,所占的字节长短不一,有些很长,有些很短。我把最长的放在前面,因为后面我打算用 PHP 的 preg_replace 函数进行批量替换。如果把短的放最前面,替换时会错乱。
替换语法:
// 举例 Emoji 编码 1F9D1-200D-1F3A8 的替换
$pattern = '/\x{1F9D1}\x{200D}\x{1F3A8}/u';
$replace = '<img src="./images/1F9D1-200D-1F3A8.png" />';
$html = preg_replace($pattern, $replace, $html);
写个测试脚本 test.php:
<?php
function emoji2image($html) {
$emojiCodes = require './emoji.php';
$patternArr = [];
$replaceArr = [];
foreach ($emojiCodes as $code) {
$pattern = '/\x{' . str_replace('-', '}\x{', $code) . '}/u';
$imageUrl = '<img class="emoji" src="./images/' . $code . '.png" />';
$patternArr[] = $pattern;
$replaceArr[] = $imageUrl;
}
$html = preg_replace($patternArr, $replaceArr, $html);
return $html;
}
$html = '这是😀🤦🏻♂️🌚什么?';
$html2 = emoji2image($html);
?>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
<style>
p {
font-size: 14px;
line-height: 150%;
}
.p1 {
font-size: 18px;
}
.p1 .emoji {
width: 20px;
height: 20px;
padding: 0 1px 4px;
vertical-align: bottom;
}
.p2 {
font-size: 32px;
}
.p2 .emoji {
width: 40px;
height: 40px;
padding: 0 1px 4px;
vertical-align: bottom;
}
</style>
</head>
<body>
<p>原内容:<?php echo $html; ?></p>
<p class="p1">字号18px:<?php echo $html2; ?></p>
<p class="p2">字号32px:<?php echo $html2; ?></p>
</body>
</html>
页面展示效果如下:
快来评论一下吧!
发表评论