<?php
// ===== 源码查看保护 =====
// 本文件同时用于服务器运行(cron)和在线查看源码。
// 通过 Web 直接访问时,仅以纯文本输出自身源码,不执行任何业务逻辑。
// 注意:gk_auto.php 若被 Web 执行会真的触发发文,这层保护是必需的。
if (PHP_SAPI !== 'cli' && basename($_SERVER['SCRIPT_FILENAME'] ?? '') === basename(__FILE__)) {
header('Content-Type: text/plain; charset=utf-8');
echo file_get_contents(__FILE__);
exit;
}
/**
* media.php — 文章配图模块(百度图片方案 + 多维质量打分)
*
* 为什么需要打分:百度图片结果质量参差(水印/字幕/教科书翻拍/厂商广告),
* 盲取第一张必然出问题。百度返回了 clarityScore、原始尺寸、来源页标题、
* 来源站点等信号,据此评分择优。
*
* 过滤链:
* 1. 关键词质量 —— 只提核心名词,剔除"我们/为什么/但你会看吗"这类碎片
* 2. 结果预筛 —— clarityScore、尺寸、长宽比、来源站点黑名单、来源标题相关性
* 3. 图像校验 —— 魔数 + 可解码 + 分辨率 + 色彩丰富度(识别翻拍/截图)
* 4. 综合打分 —— 多个维度加权,取最高分
*/
/** 图片尺寸下限 */
const GK_IMG_MIN_W = 700;
const GK_IMG_MIN_H = 380;
/** 目标最大宽度 */
const GK_IMG_MAX_W = 1200;
/** JPEG 质量 */
const GK_IMG_QUALITY = 82;
/** 最低接受分数 */
const GK_IMG_MIN_SCORE = 40;
/** 低质来源站点(论坛/问答/素材站/游戏站,多为截图水印图) */
const GK_BAD_HOSTS = [
'zhidao.baidu.com', 'wenku.baidu.com', 'baike.baidu.com', 'jingyan.baidu.com',
'tieba.baidu.com', 'bbs.', 'forum.', 'download.', 'zol.com.cn', 'pcauto.com.cn',
'taobao.com', 'tmall.com', 'jd.com', '1688.com', 'made-in-china.com',
'docin.com', 'doc88.com', 'book118.com', 'yxlady.com', 'duitang.com',
'bilibili.com', 'douyin.com', 'kuaishou.com', '3dmgame.com', 'ali213.net',
];
/**
* 提取核心关键词(规则版,作为 AI 失败时的兜底)
* 目标:拿到能搜到"主题照片"的名词,而不是句子碎片
*/
function gk_keywords_rule($title, $content = '') {
$pool = [];
if ($content !== '' && preg_match_all('#<h2[^>]*>(.*?)</h2>#is', $content, $hm)) {
foreach ($hm[1] as $h) $pool[] = trim(strip_tags($h));
}
$t = preg_replace('/[((].*?[))]/u', ' ', $title);
$parts = preg_split('/[,,。.、;;::||—]/u', $t);
if ($parts) $pool[] = trim($parts[0]);
$stop = ['我们','你们','他们','这个','那个','这些','那些','一个','一种','可以','已经','还是','不是',
'没有','就是','因为','所以','但是','而且','如果','虽然','这样','那样','自己','时候','东西',
'事情','问题','地方','真的','其实','开始','需要','不会','能','会','要','就','都','也','还',
'被','把','让','给','对','从','到','在','和','与','或','的','了','着','过','吗','呢','吧','啊',
'为什么','怎么样','如何','怎么','什么','是不是','能不能','会不会','该不该','要不要','有没有',
'别急','别再','千万','一定','到底','究竟','明明','原来','凭什','谁说','你会','你不','教你',
'浅谈','浅析','关于','那些','这些'];
$words = [];
foreach ($pool as $seg) {
// 只取完整语义块(2-5 字),不做人工截断
if (preg_match_all('/[\x{4e00}-\x{9fa5}]{2,5}/u', $seg, $m)) {
foreach ($m[0] as $w) {
if (in_array($w, $stop, true)) continue;
if (preg_match('/^(的|了|着|过|与|和|在|从)/u', $w)) continue;
if (preg_match('/(的|了|着|过)$/u', $w)) continue;
if (preg_match('/^[0-9一二三四五六七八九十百千万]+(年|月|日|天|分|秒|小时)?$/u', $w)) continue;
$words[] = $w;
}
}
}
$words = array_values(array_unique($words));
usort($words, function ($a, $b) {
return mb_strlen($b, 'UTF-8') - mb_strlen($a, 'UTF-8');
});
return array_slice($words, 0, 2);
}
/**
* 用 AI 生成精准搜图关键词
* 规则切词会切出「与低压」「种典型」这类无意义碎片,
* 交给模型判断语义最可靠。
*
* @return array ['cn'=>['中文词'], 'en'=>['英文词']] 或 null
*/
function gk_keywords_ai($title, $content, $cfg) {
// 本模块可能被独立脚本引用(如一次性发布脚本),此时主程序的
// ai_chat / extract_json 不存在,直接退回规则关键词,不要崩。
if (!function_exists('ai_chat') || !function_exists('extract_json')) {
return null;
}
$plain = trim(mb_substr(strip_tags($content), 0, 700));
$prompt = "文章标题:{$title}\n\n文章开头:{$plain}\n\n"
. "请为这篇文章选配一张**真实摄影照片**,需要给出图片搜索关键词。\n"
. "要求:\n"
. "1. 关键词必须是**具体可拍摄的实物或场景**,2-4 个汉字,不要抽象概念。\n"
. " 好例:电动汽车、充电桩、单反相机、城市夜景、电影院\n"
. " 坏例:产业发展、时间结构、现代改写、与低压、种典型\n"
. "2. 给 3 组中文关键词,从最贴切到最宽泛;再给对应英文关键词 2-3 个。\n"
. "3. 如果是影评,用影片类型对应的场景(如战争片→战场、科幻片→太空站),不要用片名(片名会搜到无关东西)。\n"
. "只输出 JSON:{\"cn\":[\"词1\",\"词2\",\"词3\"],\"en\":[\"word1\",\"word2\"]}";
$raw = ai_chat($prompt, '你是图片编辑,负责为文章配图挑搜索关键词。只输出 JSON。', $cfg);
if (strpos($raw, 'AI_CALL_FAILED::') === 0) return null;
$j = extract_json($raw);
if (!is_array($j)) return null;
$cn = array_values(array_filter(array_map(function ($w) {
return trim((string)$w);
}, (array)($j['cn'] ?? []))));
$en = array_values(array_filter(array_map(function ($w) {
return trim((string)$w);
}, (array)($j['en'] ?? []))));
if (!$cn && !$en) return null;
return ['cn' => array_slice($cn, 0, 3), 'en' => array_slice($en, 0, 3)];
}
/** 统一入口:优先 AI,失败退回规则 */
function gk_keywords($title, $content = '', $cfg = null) {
if (is_array($cfg)) {
$ai = gk_keywords_ai($title, $content, $cfg);
if ($ai && !empty($ai['cn'])) {
return $ai['cn'];
}
}
return gk_keywords_rule($title, $content);
}
/**
* 中文 → 英文关键词(内置词表,供参考显示;百度走中文)
*/
function gk_translate_kw($cnWords) {
$map = [
'摄影'=>'photography','相机'=>'camera','镜头'=>'lens','光圈'=>'aperture','虚化'=>'bokeh',
'曝光'=>'exposure','色彩'=>'color','人像'=>'portrait','风景'=>'landscape','夜景'=>'night',
'航拍'=>'drone','视频'=>'video','电池'=>'battery','充电'=>'charging','电动'=>'electric',
'汽车'=>'car','手机'=>'smartphone','芯片'=>'chip','人工智能'=>'ai','算法'=>'algorithm',
'电影'=>'film','影评'=>'cinema','导演'=>'director','纪录片'=>'documentary','动画'=>'animation',
'太空'=>'space','航天'=>'aerospace','科学'=>'science','历史'=>'history','文学'=>'literature',
'旅行'=>'travel','美食'=>'food','健康'=>'health','建筑'=>'architecture','城市'=>'city',
];
$out = [];
foreach ($cnWords as $w) {
foreach ($map as $k => $v) {
if (mb_strpos($w, $k, 0, 'UTF-8') !== false) { $out[] = $v; break; }
}
}
return array_slice(array_values(array_unique($out)), 0, 3);
}
/* =========================================================
* HTTP
* =======================================================*/
function gk_http_get($url, $timeout = 15, $maxBytes = 6 * 1024 * 1024) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => 8,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_ENCODING => '',
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120',
CURLOPT_HTTPHEADER => ['Accept: image/*,*/*;q=0.8', 'Referer: https://image.baidu.com/'],
CURLOPT_NOPROGRESS => false,
CURLOPT_PROGRESSFUNCTION => function ($r, $dl) use ($maxBytes) {
return ($dl > $maxBytes) ? 1 : 0;
},
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code !== 200 || !is_string($body) || $body === '') return '';
return $body;
}
/* =========================================================
* 图源:百度图片(含预筛)
* =======================================================*/
function gk_search_baidu($kwWords, $want = 20) {
if (!$kwWords) return [];
$word = urlencode(implode(' ', $kwWords));
$url = "https://image.baidu.com/search/acjson?tn=resultjson_com&logid=1&ipn=rj"
. "&ct=201326592&fp=result&word={$word}&pn=0&rn={$want}&gsm=1e";
$raw = gk_http_get($url, 15);
if ($raw === '') return [];
$j = json_decode($raw, true);
if (!is_array($j) || empty($j['data'])) {
$j = json_decode(str_replace('\\/', '/', $raw), true);
}
if (!is_array($j) || empty($j['data'])) return [];
$cands = [];
foreach ($j['data'] as $item) {
if (!is_array($item)) continue;
$u = $item['hoverURL'] ?? ($item['middleURL'] ?? ($item['thumbURL'] ?? ''));
if (!is_string($u) || $u === '') continue;
$u = preg_replace('#^http://#i', 'https://', $u);
$host = strtolower((string)($item['fromURLHost'] ?? ''));
$srcTitle = (string)($item['fromPageTitleEnc'] ?? '');
$cw = (int)($item['width'] ?? 0);
$chh = (int)($item['height'] ?? 0);
$clarity = (int)($item['clarityScore'] ?? 0);
// 黑名单站点直接丢
$bad = false;
foreach (GK_BAD_HOSTS as $b) {
if ($host !== '' && strpos($host, $b) !== false) { $bad = true; break; }
}
if ($bad) continue;
// 原始尺寸太小 / 长宽比异常 直接丢(放宽:百度 width/height 是原图尺寸,
// 部分结果缺失或不准确,过严会把可用图全部筛掉)
if ($cw > 0 && $chh > 0) {
if ($cw < 640 || $chh < 360) continue;
$r = $cw / $chh;
if ($r < 0.85 || $r > 3.4) continue; // 允许略竖的图,但仍排除长篇截图
}
// 清晰度太低的丢(放宽到 35)
if ($clarity > 0 && $clarity < 35) continue;
// 来源标题里出现明显不相关的词(游戏/攻略/下载/小说)直接丢
if (preg_match('/(游戏|攻略|下载|小说|漫画|壁纸|头像|表情包|视频教程|破解|注册码)/u', $srcTitle)) {
continue;
}
// 旅游/消费/促销类来源降权标记(这类图多半是打卡照、广告图,与严肃内容不搭)
$softPenalty = 0;
if (preg_match('/(体验|打卡|探店|游艇|度假|旅游攻略|价格|优惠|促销|优惠券|多少钱|测评|开箱)/u', $srcTitle)) {
$softPenalty = 18;
}
// 软件界面/截图类降权(不是摄影照片,配文章显得廉价)
if (preg_match('/(软件|界面|截图|客户端|app|工具下载|安装包|系统设置|后台|教程步骤)/iu', $srcTitle)) {
$softPenalty += 30;
}
$cands[] = [
'url' => $u,
'host' => $host,
'srcText' => $srcTitle,
'w' => $cw,
'h' => $chh,
'clarity' => $clarity,
'penalty' => $softPenalty,
];
}
return $cands;
}
/**
* 图像质量评估(识别翻拍纸张 / 截图 / 纯色图)
* @return array ['score'=>0-100, 'reasons'=>[]]
*/
function gk_image_quality($im) {
$w = imagesx($im); $h = imagesy($im);
$score = 50;
$reasons = [];
// 采样网格
$grid = 40;
$satSum = 0; $lumSum = 0; $n = 0;
$lumMin = 255; $lumMax = 0;
$edgeWhite = 0; // 边缘近白像素(翻拍纸张特征)
for ($gy = 0; $gy < $grid; $gy++) {
for ($gx = 0; $gx < $grid; $gx++) {
$px = (int)($gx * $w / $grid);
$py = (int)($gy * $h / $grid);
if ($px >= $w) $px = $w - 1;
if ($py >= $h) $py = $h - 1;
$c = imagecolorat($im, $px, $py);
$r = ($c >> 16) & 0xFF; $g = ($c >> 8) & 0xFF; $b = $c & 0xFF;
$mx = max($r, $g, $b); $mn = min($r, $g, $b);
$sat = $mx == 0 ? 0 : ($mx - $mn) / $mx; // 0..1
$lum = 0.299*$r + 0.587*$g + 0.114*$b; // 0..255
$satSum += $sat; $lumSum += $lum; $n++;
if ($lum < $lumMin) $lumMin = $lum;
if ($lum > $lumMax) $lumMax = $lum;
// 边缘区域白像素统计
if ($gx < 4 || $gx >= $grid - 4 || $gy < 4 || $gy >= $grid - 4) {
if ($lum > 215 && $sat < 0.12) $edgeWhite++;
}
}
}
$avgSat = $n ? $satSum / $n : 0;
$avgLum = $n ? $lumSum / $n : 0;
$range = $lumMax - $lumMin;
// ① 色彩丰富度:照片通常有明显饱和;翻拍文档/截图偏灰白
if ($avgSat >= 0.28) { $score += 22; $reasons[] = '色彩丰富'; }
elseif ($avgSat >= 0.18) { $score += 12; }
elseif ($avgSat < 0.08) { $score -= 25; $reasons[] = '几乎无色彩(疑文档/截图)'; }
else { $score -= 6; }
// ② 亮度分布:过曝/过暗都差
if ($avgLum < 35) { $score -= 20; $reasons[] = '过暗'; }
elseif ($avgLum > 225) { $score -= 22; $reasons[] = '过亮(疑白底文档)'; }
elseif ($avgLum >= 60 && $avgLum <= 185) { $score += 10; }
// ③ 动态范围:照片有层次,纯色/低对比图差
if ($range < 60) { $score -= 18; $reasons[] = '对比度极低(疑纯色/截图)'; }
elseif ($range > 150) { $score += 8; }
// ④ 边缘大面积为白 → 典型"翻拍纸张/文档截图"
$edgeRatio = $n ? $edgeWhite / ($n * 0.36) : 0; // 边缘约占 36%
if ($edgeRatio > 0.55) { $score -= 30; $reasons[] = '边缘大面积白色(疑翻拍文档)'; }
elseif ($edgeRatio > 0.35) { $score -= 12; }
// ⑤ 分辨率加分
if ($w >= 1000 && $h >= 500) { $score += 6; }
// ⑥ 角落水印粗检:水印多为某角落一小块高对比、高亮度的区域,
// 与周边形成突变。检查四角 18% 区域内是否存在这种"异常块"。
$warnCorner = false;
$cw = (int)($w * 0.18); $ch = (int)($h * 0.18);
$corners = [[0,0], [$w-$cw,0], [0,$h-$ch], [$w-$cw,$h-$ch]];
foreach ($corners as $ci => $corner) {
$bright = 0; $tot = 0; $contrast = 0;
for ($y = $corner[1]; $y < $corner[1] + $ch; $y += 3) {
for ($x = $corner[0]; $x < $corner[0] + $cw; $x += 3) {
if ($x < 0 || $y < 0 || $x >= $w || $y >= $h) continue;
$c = imagecolorat($im, $x, $y);
$r = ($c >> 16) & 0xFF; $g = ($c >> 8) & 0xFF; $b = $c & 0xFF;
$lum = 0.299*$r + 0.587*$g + 0.114*$b;
if ($lum > 235) $bright++;
$tot++;
}
}
// 角落出现大量近白像素(白字水印常见形态)
if ($tot > 50 && ($bright / $tot) > 0.10) { $warnCorner = true; break; }
}
if ($warnCorner) { $score -= 10; $reasons[] = '角落疑有水印'; }
return ['score' => max(0, min(100, $score)), 'reasons' => $reasons,
'sat' => round($avgSat, 2), 'lum' => round($avgLum), 'range' => $range];
}
/**
* 下载 + 校验 + 规整一张图
* @return array|null ['data','w','h','ext','quality']
*/
function gk_load_image($url) {
$bin = gk_http_get($url, 20);
if ($bin === '' || strlen($bin) < 4000) return null;
$isJpg = (substr($bin, 0, 2) === "\xFF\xD8");
$isPng = (substr($bin, 0, 4) === "\x89PNG");
$isWebp = (substr($bin, 0, 4) === 'RIFF' && substr($bin, 8, 4) === 'WEBP');
if (!$isJpg && !$isPng && !$isWebp) return null;
$im = @imagecreatefromstring($bin);
if (!$im) return null;
$w = imagesx($im); $h = imagesy($im);
if ($w < GK_IMG_MIN_W || $h < GK_IMG_MIN_H) { imagedestroy($im); return null; }
$q = gk_image_quality($im);
// 缩放
if ($w > GK_IMG_MAX_W) {
$nw = GK_IMG_MAX_W;
$nh = (int)round($h * ($nw / $w));
$dst = imagecreatetruecolor($nw, $nh);
imagecopyresampled($dst, $im, 0, 0, 0, 0, $nw, $nh, $w, $h);
imagedestroy($im);
$im = $dst; $w = $nw; $h = $nh;
}
ob_start();
imagejpeg($im, null, GK_IMG_QUALITY);
$out = ob_get_clean();
imagedestroy($im);
if (!is_string($out) || strlen($out) < 4000) return null;
return ['data' => $out, 'w' => $w, 'h' => $h, 'ext' => 'jpg', 'quality' => $q];
}
/**
* 按主题取一张配图(主入口)
* @param array $cfg 传入配置时启用 AI 关键词;不传则用规则兜底
* @return array|null ['data','w','h','ext','src','url','score','quality']
*/
function gk_fetch_topic_image($title, $content = '', $cfg = null, $log = null) {
// 关键词:AI 优先,规则兜底;同时拿到多组候选词轮试
$kwSets = [];
if (is_array($cfg)) {
$ai = gk_keywords_ai($title, $content, $cfg);
if ($ai && !empty($ai['cn'])) {
foreach ($ai['cn'] as $w) { $kwSets[] = [$w]; }
// 两两组合也试一次(更具体)
if (count($ai['cn']) >= 2) { $kwSets[] = [$ai['cn'][0], $ai['cn'][1]]; }
if ($log) $log(" AI关键词: " . implode(' / ', $ai['cn']));
}
}
if (!$kwSets) {
$rule = gk_keywords_rule($title, $content);
if ($rule) {
$kwSets[] = $rule;
foreach ($rule as $w) { $kwSets[] = [$w]; }
if ($log) $log(" 规则关键词: " . implode(' / ', $rule));
}
}
if (!$kwSets) return null;
$best = null;
foreach ($kwSets as $kw) {
$cands = gk_search_baidu($kw, 20);
if ($log) $log(" 搜「" . implode(' ', $kw) . "」→ " . count($cands) . " 张候选");
if (!$cands) continue;
usort($cands, function ($a, $b) { return $b['clarity'] <=> $a['clarity']; });
$tried = 0;
foreach ($cands as $c) {
if ($tried >= 6) break;
$tried++;
$img = gk_load_image($c['url']);
if (!$img) continue;
$total = $img['quality']['score'];
$total += (int)round($c['clarity'] * 0.25);
if ($c['w'] >= 1600) $total += 6;
$total -= (int)($c['penalty'] ?? 0); // 旅游/消费类降权
if ($log) {
$log(sprintf(" 试%d %dx%d 清晰%d 质量%d 合计%d %s",
$tried, $img['w'], $img['h'], $c['clarity'],
$img['quality']['score'], $total,
$img['quality']['reasons'] ? '(' . implode('/', $img['quality']['reasons']) . ')' : ''));
}
if (!$best || $total > $best['score']) {
$best = $img;
$best['score'] = $total;
$best['src'] = 'Baidu';
$best['url'] = $c['url'];
$best['srcText'] = $c['srcText'];
$best['kw'] = $kw;
}
if ($total >= 88) break 2; // 足够好,整体停止
}
}
if (!$best) return null;
if ($best['score'] < GK_IMG_MIN_SCORE) {
if ($log) $log(" ✗ 最高分 {$best['score']} 低于阈值 " . GK_IMG_MIN_SCORE . ",放弃配图");
return null;
}
return $best;
}
/**
* 把图片写入 WP 媒体库
*/
function gk_save_media($conn, $prefix, $siteDir, $siteUrl, $img, $title) {
$ym = date('Y/m');
$uploadDir = rtrim($siteDir, '/') . '/wp-content/uploads/' . $ym;
if (!is_dir($uploadDir)) @mkdir($uploadDir, 0755, true);
if (!is_dir($uploadDir) || !is_writable($uploadDir)) return null;
$base = preg_replace('/[^\p{Han}a-zA-Z0-9]+/u', '-', mb_substr($title, 0, 30));
$base = trim($base, '-');
if ($base === '') $base = 'post';
$name = $base . '-' . substr(md5(uniqid('', true)), 0, 6) . '.jpg';
$path = $uploadDir . '/' . $name;
if (file_put_contents($path, $img['data']) === false) return null;
@chmod($path, 0644);
@chown($path, 'www');
@chgrp($path, 'www');
$relFile = $ym . '/' . $name;
$url = rtrim($siteUrl, '/') . '/wp-content/uploads/' . $relFile;
$now = date('Y-m-d H:i:s');
$nowG = gmdate('Y-m-d H:i:s');
$titleEsc = $conn->real_escape_string($title);
$guid = $conn->real_escape_string($url);
$sql = "INSERT INTO {$prefix}posts
(post_author, post_date, post_date_gmt, post_content, post_title, post_excerpt,
post_status, comment_status, ping_status, post_password, post_name,
to_ping, pinged, post_modified, post_modified_gmt, post_content_filtered,
post_parent, guid, menu_order, post_type, post_mime_type, comment_count)
VALUES (1, '{$now}', '{$nowG}', '', '{$titleEsc}', '',
'inherit', 'open', 'closed', '', '',
'', '', '{$now}', '{$nowG}', '', 0, '{$guid}', 0,
'attachment', 'image/jpeg', 0)";
if (!$conn->query($sql)) { @unlink($path); return null; }
$aid = $conn->insert_id;
$relEsc = $conn->real_escape_string($relFile);
$meta = serialize([
'width' => $img['w'], 'height' => $img['h'],
'file' => $relFile, 'sizes' => [],
'image_meta' => [
'aperture' => '0', 'credit' => '', 'camera' => '', 'caption' => '',
'created_timestamp' => '0', 'copyright' => '', 'focal_length' => '0',
'iso' => '0', 'shutter_speed' => '0', 'title' => '', 'orientation' => '0',
],
]);
$metaEsc = $conn->real_escape_string($meta);
$conn->query("INSERT INTO {$prefix}postmeta (post_id, meta_key, meta_value) VALUES
({$aid}, '_wp_attached_file', '{$relEsc}'),
({$aid}, '_wp_attachment_metadata', '{$metaEsc}'),
({$aid}, '_wp_attachment_image_alt', '{$titleEsc}')");
return ['url' => $url, 'path' => $path, 'attach_id' => $aid, 'rel' => $relFile];
}
/** 正文顶部配图 HTML(居中、圆角、与截图样式一致) */
function gk_image_html($url, $alt) {
$altEsc = htmlspecialchars($alt, ENT_QUOTES, 'UTF-8');
$urlEsc = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
return '<figure class="wp-block-image size-large gk-hero-img">'
. '<img src="' . $urlEsc . '" alt="' . $altEsc . '" '
. 'style="width:100%;max-width:900px;height:auto;display:block;margin:0 auto 30px;border-radius:10px;" />'
. '</figure>';
}