rebbit

WordPress 通过文章标题自动生成特色图片

一些文章没有特色图片感觉很单调不和谐

然后看到果果搞了个文章配图的插件,

https://cn.wordpress.org/plugins/article-with-pictures

可以通过文章标题自动生成纯色背景的特色图片

进行小魔改一番,支持生成 avif 格式和增加压缩率

效果:

测试网址(如果没删就能用):

title可以换成任意文本(大概)

https://www.krjojo.com/resources/auto-image/?title=wordpress-%E9%80%9A%E8%BF%87%E6%96%87%E7%AB%A0%E6%A0%87%E9%A2%98%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E7%89%B9%E8%89%B2%E5%9B%BE%E7%89%87

核心文件

生成图片

新建一个 class-article-with-pictures-api.php

<?php
/**
* 文章配图生成类
*/
class Article_With_Pictures_Api
{
/**
* @var int 图片宽度
*/
private int $width;
/**
* @var int 图片高度
*/
private int $height;
/**
* @var int 文字行高间距
*/
private int $lineHeight = 8;
/**
* @var array 图片背景颜色RGB
*/
private array $backgroundRGB = array(255, 255, 255);
/**
* @var array 文字颜色RGB
*/
private array $textRGB = array(0, 0, 0);
/**
* @var string 文字
*/
private string $text;
/**
* @var float 字体大小
*/
private float $fontSize = 20;
/**
* @var string 字体文件路径
*/
private string $fontFile;
/**
* @var bool 是否开启多行文字。如果文字一行显示不了,则多行显示
*/
private bool $isMultiLine = false;
/**
* @var string 错误信息
*/
private string $error = '';
/**
* @var int 图片最多有多少行,针对于多行文字
*/
private int $maxLineNum = 0;
/**
* @var int 图片每行最多多少个文字
*/
private int $maxLineTextNum = 0;
/**
* @var int 默认每行文字减少文字个数
*/
private int $reduceLineTextNum = 0;
/**
* 构造方法
* @param int $width 图片宽度
* @param int $height 图片高度
*/
public function __construct(int $width, int $height)
{
$this->setWidth($width);
$this->setHeight($height);
}
/**
* 获取图片宽度
* @return int
*/
public function getWidth(): int
{
return $this->width;
}
/**
* 设置图片宽度
* @param int $width
*/
public function setWidth(int $width): void
{
$this->width = $width;
}
/**
* 获取图片高度
* @return int
*/
public function getHeight(): int
{
return $this->height;
}
/**
* 设置图片高度
* @param int $height
*/
public function setHeight(int $height): void
{
$this->height = $height;
}
/**
* 获取背景颜色的RGB
* @return array
*/
public function getBackgroundRGB(): array
{
return $this->backgroundRGB;
}
/**
* 设置背景颜色的RGB
* @param array $backgroundRGB
*/
public function setBackgroundRGB(array $backgroundRGB): void
{
$this->backgroundRGB = $backgroundRGB;
}
/**
* 获取文字颜色的RGB
* @return array
*/
public function getTextRGB(): array
{
return $this->textRGB;
}
/**
* 设置文字颜色的RGB
* @param array $textRGB
*/
public function setTextRGB(array $textRGB): void
{
$this->textRGB = $textRGB;
}
/**
* 获取文字
* @return string
*/
public function getText(): string
{
return $this->text;
}
/**
* 设置文字
* @param string $text
*/
public function setText(string $text): void
{
$this->text = trim($text);
}
/**
* 获取文字大小
* @return float
*/
public function getFontSize(): float
{
return $this->fontSize;
}
/**
* 设置文字大小
* @param float $fontSize
*/
public function setFontSize(float $fontSize): void
{
$this->fontSize = $fontSize;
}
/**
* 获取字体文件
* @return string
*/
public function getFontFile(): string
{
return $this->fontFile;
}
/**
* 设置字体文件
* @param string $fontFile
*/
public function setFontFile(string $fontFile): void
{
$this->fontFile = $fontFile;
}
/**
* 判断是否为多行文字
* @return bool
*/
public function isMultiLine(): bool
{
return $this->isMultiLine;
}
/**
* 设置为多行文字
* @param bool $isMultiLine
*/
public function setIsMultiLine(bool $isMultiLine): void
{
$this->isMultiLine = $isMultiLine;
}
/**
* 获取错误信息
* @return string
*/
public function getError(): string
{
return $this->error;
}
/**
* 设置错误信息
* @param string $error
*/
public function setError(string $error): void
{
$this->error = $error;
}
/**
* 获取最多行数
* @return int
*/
public function getMaxLineNum(): int
{
return $this->maxLineNum;
}
/**
* 设置最多行数
* @param int $maxLineNum
*/
public function setMaxLineNum(int $maxLineNum): void
{
$this->maxLineNum = $maxLineNum;
}
/**
* 获取每行最多文字个数
* @return int
*/
public function getMaxLineTextNum(): int
{
return $this->maxLineTextNum;
}
/**
* 设置每行最多文字个数
* @param int $maxLineTextNum
*/
public function setMaxLineTextNum(int $maxLineTextNum): void
{
$this->maxLineTextNum = $maxLineTextNum;
}
/**
* 获取每行文字减少文字个数
* @return int
*/
public function getReduceLineTextNum(): int
{
return $this->reduceLineTextNum;
}
/**
* 设置每行文字减少文字个数
* @param int $reduceLineTextNum
*/
public function setReduceLineTextNum(int $reduceLineTextNum): void
{
$this->reduceLineTextNum = $reduceLineTextNum;
}
/**
* 设置文字行间距
* @return int
*/
public function getLineHeight(): int
{
return $this->lineHeight;
}
/**
* 获取文字行间距
* @param int $lineHeight
*/
public function setLineHeight(int $lineHeight): void
{
$this->lineHeight = $lineHeight;
}
/**
* 获取颜色的RGB
* @param string $hexColor 颜色
* @return bool|array
*/
public function getRGB(string $hexColor): bool|array
{
if (!preg_match('/^#[0-9a-f]{3,6}$/i', $hexColor)) {
$this->setError('颜色值不符合规则');
return false;
}
$hexColor = substr($hexColor, 1);
$len = strlen($hexColor);
if (3 === $len) {
return array(
hexdec($hexColor[0] . $hexColor[0]),
hexdec($hexColor[1] . $hexColor[1]),
hexdec($hexColor[2] . $hexColor[2])
);
} else if (6 === $len) {
return array(
hexdec($hexColor[0] . $hexColor[1]),
hexdec($hexColor[2] . $hexColor[3]),
hexdec($hexColor[4] . $hexColor[5])
);
} else {
$this->setError('颜色值长度不符合规则');
return false;
}
}
/**
* 获取图片Im资源
* @return false|GdImage|resource
*/
public function getIm(): false|GdImage
{
$im = imagecreatetruecolor($this->width, $this->height);
if (false === $im) {
$this->setError('创建真彩色图像失败');
return false;
}
imagealphablending($im, true);
imageantialias($im, true);
// 使用背景颜色
imagefilledrectangle($im, 0, 0, imagesx($im) - 1, imagesy($im) - 1, imagecolorallocate($im, $this->backgroundRGB[0], $this->backgroundRGB[1], $this->backgroundRGB[2]));
if (!empty($this->text) && !empty($this->fontFile) && file_exists($this->fontFile)) {
// 写文字
// 获取当前字体下的每个文字宽度和高度
$wordBbox = imagettfbbox($this->fontSize, 0, $this->fontFile, '果');
$wordWidth = abs($wordBbox[0] - $wordBbox[4]) + abs($wordBbox[0] * 2);
$wordHeight = abs($wordBbox[1] - $wordBbox[5]) + abs($wordBbox[1] * 2);
$wordHeight += $this->lineHeight;
// 图片上每行最多文字
$lineTextNum = floor($this->width / $wordWidth);
if (!empty($this->maxLineTextNum) && $lineTextNum > $this->maxLineTextNum) {
$lineTextNum = $this->maxLineTextNum;
}
// 图片上减少文字个数
if (!empty($this->reduceLineTextNum) && $lineTextNum > $this->reduceLineTextNum) {
$lineTextNum -= $this->reduceLineTextNum;
}
if ($lineTextNum <= 0) {
$this->setError('图片上无法写入文字');
return false;
}
$text = $this->text;
$textColor = imagecolorallocate($im, $this->textRGB[0], $this->textRGB[1], $this->textRGB[2]);
if ($this->isMultiLine()) {
// 图片上最多文字行数
$lineNum = floor($this->height / $wordHeight);
if (!empty($this->maxLineNum) && $lineNum > $this->maxLineNum) {
$lineNum = $this->maxLineNum;
}
// 多行文字
$line = ceil(mb_strlen($text, 'utf-8') / $lineTextNum);
// 图片文字行数超了,就截取
if ($line > $lineNum) {
$line = $lineNum;
$textLen = $lineTextNum * $line;
$text = mb_substr($text, 0, $textLen, 'utf-8');
}
for ($i = 1; $i <= $line; $i++) {
$t = mb_substr($text, ($i - 1) * $lineTextNum, $lineTextNum, 'utf-8');
$bbox = imagettfbbox($this->fontSize, 0, $this->fontFile, $t);
$x = ($this->width - $bbox[4]) / 2;
$y = ($this->height - $line * $wordHeight - $bbox[1] - $this->lineHeight) / 2;
imagettftext($im, $this->fontSize, 0, (int)$x, (int)($y + $i * $wordHeight), $textColor, $this->fontFile, $t);
}
} else {
// 单行文字
if (mb_strlen($text, 'utf-8') > $lineTextNum) {
$text = mb_substr($text, 0, $lineTextNum, 'utf-8');
}
$bbox = imagettfbbox($this->fontSize, 0, $this->fontFile, $text);
$x = abs($this->width - $bbox[4]) / 2;
$y = abs($this->height - $bbox[5]) / 2;
imagettftext($im, $this->fontSize, 0, (int)$x, (int)$y, $textColor, $this->fontFile, $text);
}
}
return $im;
}
/**
* 保存图片到指定文件路径
* @param string $filename 图片完整路径,包括文件名,文件目录
* @return bool
*/
public function saveImage(string $filename): bool
{
if (empty($filename)) {
$this->setError('图片文件路径不能为空');
return false;
}
$im = $this->getIm();
if (empty($im)) {
return false;
}
$pathinfo = pathinfo($filename);
if (empty($pathinfo)) {
$this->setError('图片文件路径解析失败');
return false;
}
if (!is_dir($pathinfo['dirname'])) {
$result = @mkdir($pathinfo['dirname'], 0777, true);
if (!$result) {
$this->setError('图片文件目录创建失败');
return false;
}
}
if (!empty($pathinfo['extension'])) {
switch (strtolower($pathinfo['extension'])) {
case 'png':
$result = imagepng($im, $filename);
imagedestroy($im);
return $result;
case 'bmp':
if (function_exists('imagebmp')) {
$result = imagebmp($im, $filename);
imagedestroy($im);
return $result;
}
break;
case 'jpeg':
case 'jpg':
$result = imagejpeg($im, $filename, 100);
imagedestroy($im);
return $result;
case 'webp':
if (function_exists('imagewebp')) {
$result = imagewebp($im, $filename, 100);
imagedestroy($im);
return $result;
}
break;
case 'avif':
if (function_exists('imageavif')) {
$result = imageavif($im, $filename, 30);
imagedestroy($im);
return $result;
}
break;
}
}
$this->setError('图片格式不支持');
return false;
}
}

新建一个 index.php

在同级目录下用 index.php 作为入口 (用其他名字也行)

如果把注释解除,将会在同级目录下的 cache 文件夹里缓存已经生成过的图片

文字和背景要用对比色

30压缩率的 avif 在400*400分辨率下,生成的特色图片基本在1KB-4KB,文字越多越大

<?php
$title = $_GET['title'] ?? "";
$id = $_GET['id'] ?? 0;
if (!is_numeric($id) || $id < 1 || $title == "") {
return;
}
// 背景色 字体色
$HEX = [
['#5B8982', '#FFFFFF'],
['#45545F', '#CEC6B6'],
['#D47655', '#E1F8E1'],
['#223C5F', '#E3927F'],
['#825855', '#F9ECDF'],
['#FFBFA5', '#6A7BA4'],
['#331B40', '#ADC7B5'],
['#4F586B', '#DDE3F1'],
['#023440', '#F0EDCC'],
['#42586E', '#E3D4AF'],
['#AE9AAB', '#F2EBE7'],
['#417CA9', '#EDEBE4'],
['#7379B0', '#C6EDEC'],
['#4E2236', '#FFFFFF'],
];
$index = array_rand($HEX);
// 读取缓存
// $content = file_get_contents(__DIR__ . '/cache/' . ($id) . '.avif');
// if ($content) {
//     header('Content-Type: image/webp');
//     echo $content;
//     return;
// }
// 绘画图片
include_once __DIR__ . '/class-article-with-pictures-api.php';
$api = new Article_With_Pictures_Api('400', '400');
// 设置图片默认背景颜色
$api->setBackgroundRGB($api->getRGB($HEX[$index][0]));
$api->setTextRGB($api->getRGB($HEX[$index][1]));
$api->setFontFile(__DIR__ . '/AlimamaDongFangDaKai-Regular.ttf');
$api->setText($title);
$api->setIsMultiLine(true);
$api->setFontSize(25);
// $api->saveImage(__DIR__ . '/cache/' . ($id) . '.avif');
$im = $api->getIm();
header('Content-Type: image/webp');
imagewebp($im);
// 释放内存
imagedestroy($im);

上面入口文件的访问规则是

?id=1&title=标题内容

id是文章的id,通过id保存图片和寻找图片

也可以用标题作为文件名,但是要确保标题不会有奇怪的特殊符号,那些特殊符号对路径和文件名不一定合法

下载字体文件

可以选用阿里妈妈东方大楷,主要够粗

https://www.iconfont.cn/fonts/detail?spm=a313x.fonts_index.i1.d9df05512.51573a815TItTY&cnid=IhcTcFymWeyf

也可以在:https://www.iconfont.cn/ 找自己喜欢的字体

也是放到同级目录

测试

此时已经可以访问了,网址填写刚才的目录

例如

https://www.krjojo.com/resources/auto-image/?id=1&title=%E9%BA%A6%E5%BD%93%E5%8A%B3%C3%97%E5%A2%A8%E8%BF%B9%E5%A4%A9%E6%B0%94%20%E9%98%B3%E5%85%89%E2%80%9C%E8%8C%B6%E2%80%9D%E6%89%BE%E8%AE%A1%E5%88%92%20%E5%85%8D%E8%B4%B9%E9%A2%86%E9%98%B3%E5%85%89%E6%9F%A0%E6%AA%AC%E7%BA%A2%E8%8C%B6%E3%80%904%E6%9C%8822%E6%97%A5-5%E6%9C%8821%E6%97%A5%E3%80%91

id为 1

标题为 麦当劳×墨迹天气%20阳光“茶”找计划%20免费领阳光柠檬红茶【4月22日-5月21日】

修改wordpress特色图片过滤器

修改 has_post_thumbnail ,让判断是否有特色图片永远返回true

add_filter('has_post_thumbnail', '__return_true');

修改 post_thumbnail_html,自己写入特色图片的链接

标题一定要进行url转义

add_filter('post_thumbnail_html', function ($html) {
if (!$html) {
return '<img width="400" height="400" src="https://www.krjojo.com/resources/auto-image/?id=' . get_the_ID() . '&title=' . rawurlencode(get_the_title()) . '" class="attachment-thumbnail size-thumbnail wp-post-image" alt="" itemprop="image" decoding="async">';
}
return $html;
});

完工

© 版权声明
分享是一种美德,转载请保留原链接
THE END

文章不错?点个赞呗
点赞 0 分享

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部