From 5a3027b8dc86f763b911e65fc69c5d1da16f3d96 Mon Sep 17 00:00:00 2001 From: XIGE <710062962@qq.com> Date: Mon, 23 Feb 2026 17:28:10 +0800 Subject: [PATCH] 1.0 --- Plugin.php | 1244 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1244 insertions(+) create mode 100644 Plugin.php diff --git a/Plugin.php b/Plugin.php new file mode 100644 index 0000000..b9f214b --- /dev/null +++ b/Plugin.php @@ -0,0 +1,1244 @@ +contentEx = array('ImageCaptionWatermark_Plugin', 'parseContent'); + Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('ImageCaptionWatermark_Plugin', 'parseContent'); + Typecho_Plugin::factory('Widget_Archive')->footer = array('ImageCaptionWatermark_Plugin', 'addFooter'); + + return '插件已激活,请到设置页面配置参数。'; + } + + /** + * 禁用插件 + */ + public static function deactivate() + { + return '插件已禁用'; + } + + /** + * 插件配置面板 + */ + public static function config(Typecho_Widget_Helper_Form $form) + { + // 标题设置部分 + $titleEnable = new Typecho_Widget_Helper_Form_Element_Radio( + 'titleEnable', + array('1' => '开启', '0' => '关闭'), + '1', + '启用图片标题', + '是否自动为文章中的图片添加标题' + ); + $form->addInput($titleEnable); + + $titlePosition = new Typecho_Widget_Helper_Form_Element_Select( + 'titlePosition', + array( + 'bottom-center' => '底部居中', + 'bottom-right' => '右下角', + 'bottom-left' => '左下角', + 'top-center' => '顶部居中', + 'top-right' => '右上角', + 'top-left' => '左上角' + ), + 'bottom-center', + '标题显示位置', + '标题在图片上的显示位置' + ); + $form->addInput($titlePosition); + + $titleMinWidth = new Typecho_Widget_Helper_Form_Element_Text( + 'titleMinWidth', + NULL, + '300', + '标题最小图片宽度', + '页面显示宽度大于此值(像素)的图片才显示标题' + ); + $titleMinWidth->input->setAttribute('style', 'width: 120px;'); + $form->addInput($titleMinWidth->addRule('isInteger', '请输入整数')); + + // 新增:标题最小高度 + $titleMinHeight = new Typecho_Widget_Helper_Form_Element_Text( + 'titleMinHeight', + NULL, + '300', + '标题最小图片高度', + '页面显示高度大于此值(像素)的图片才显示标题' + ); + $titleMinHeight->input->setAttribute('style', 'width: 120px;'); + $form->addInput($titleMinHeight->addRule('isInteger', '请输入整数')); + + $titleChineseOnly = new Typecho_Widget_Helper_Form_Element_Radio( + 'titleChineseOnly', + array('1' => '开启', '0' => '关闭'), + '1', + '仅显示中文标题', + '开启:如果图片alt/title中没有中文,则不显示标题
关闭:显示所有标题' + ); + $form->addInput($titleChineseOnly); + + $titleColor = new Typecho_Widget_Helper_Form_Element_Text( + 'titleColor', + NULL, + '#FFFFFF', + '标题文字颜色', + '标题文字的颜色' + ); + $titleColor->input->setAttribute('style', 'width: 150px;'); + $form->addInput($titleColor); + + $titleBgColor = new Typecho_Widget_Helper_Form_Element_Text( + 'titleBgColor', + NULL, + 'rgba(0,0,0,0.85)', + '标题背景颜色', + '标题背景的颜色' + ); + $titleBgColor->input->setAttribute('style', 'width: 150px;'); + $form->addInput($titleBgColor); + + // 新增:标题背景透明度 + $titleBgOpacity = new Typecho_Widget_Helper_Form_Element_Text( + 'titleBgOpacity', + NULL, + '0.85', + '标题背景透明度', + '0-1之间,1为不透明' + ); + $titleBgOpacity->input->setAttribute('style', 'width: 100px;'); + $titleBgOpacity->input->setAttribute('placeholder', '0.0-1.0'); + $form->addInput($titleBgOpacity); + + // 修改:标题显示分类 + $titleIncludeCategories = new Typecho_Widget_Helper_Form_Element_Text( + 'titleIncludeCategories', + NULL, + '', + '标题显示分类', + '仅在此分类下的文章显示图片标题,留空表示所有分类都显示
多个分类用逗号间隔,如:技术,生活' + ); + $titleIncludeCategories->input->setAttribute('style', 'width: 300px;'); + $form->addInput($titleIncludeCategories); + + $titleFontSize = new Typecho_Widget_Helper_Form_Element_Text( + 'titleFontSize', + NULL, + '14', + '标题字体大小', + '标题文字的大小(像素)' + ); + $titleFontSize->input->setAttribute('style', 'width: 120px;'); + $form->addInput($titleFontSize->addRule('isInteger', '请输入整数')); + + $titlePadding = new Typecho_Widget_Helper_Form_Element_Text( + 'titlePadding', + NULL, + '8px 15px', + '标题内边距', + '标题的内边距,格式:上下 左右' + ); + $titlePadding->input->setAttribute('style', 'width: 150px;'); + $form->addInput($titlePadding); + + // 新增:是否跳过横排组内的图片标题 + $skipFlexGroupTitles = new Typecho_Widget_Helper_Form_Element_Radio( + 'skipFlexGroupTitles', + array('1' => '跳过', '0' => '不跳过'), + '1', + '跳过横排组内的图片标题', + '开启:在横排图片组(flex-group)中不显示单张图片的标题,只显示组标题
关闭:同时显示组标题和单图标题' + ); + $form->addInput($skipFlexGroupTitles); + + // 水印设置部分 + $watermarkEnable = new Typecho_Widget_Helper_Form_Element_Radio( + 'watermarkEnable', + array('1' => '开启', '0' => '关闭'), + '1', + '启用图片水印', + '是否自动为文章中的图片添加水印' + ); + $form->addInput($watermarkEnable); + + $watermarkText = new Typecho_Widget_Helper_Form_Element_Text( + 'watermarkText', + NULL, + 'SHITOUCUO.COM', + '水印文字内容', + '水印文字的内容' + ); + $watermarkText->input->setAttribute('style', 'width: 300px;'); + $form->addInput($watermarkText); + + $watermarkPosition = new Typecho_Widget_Helper_Form_Element_Select( + 'watermarkPosition', + array( + 'top-left' => '左上角', + 'top-center' => '顶部居中', + 'top-right' => '右上角', + 'center' => '正中央', + 'bottom-left' => '左下角', + 'bottom-center' => '底部居中', + 'bottom-right' => '右下角' + ), + 'top-center', + '水印显示位置', + '避免与标题重叠,建议使用顶部位置' + ); + $form->addInput($watermarkPosition); + + $watermarkMinWidth = new Typecho_Widget_Helper_Form_Element_Text( + 'watermarkMinWidth', + NULL, + '200', + '水印最小图片宽度', + '页面显示宽度大于此值(像素)的图片才添加水印' + ); + $watermarkMinWidth->input->setAttribute('style', 'width: 120px;'); + $form->addInput($watermarkMinWidth->addRule('isInteger', '请输入整数')); + + // 新增:水印最小高度 + $watermarkMinHeight = new Typecho_Widget_Helper_Form_Element_Text( + 'watermarkMinHeight', + NULL, + '500', + '水印最小图片高度', + '页面显示高度大于此值(像素)的图片才添加水印' + ); + $watermarkMinHeight->input->setAttribute('style', 'width: 120px;'); + $form->addInput($watermarkMinHeight->addRule('isInteger', '请输入整数')); + + // 修改:水印显示分类 + $watermarkIncludeCategories = new Typecho_Widget_Helper_Form_Element_Text( + 'watermarkIncludeCategories', + NULL, + '', + '水印显示分类', + '仅在此分类下的文章显示图片水印,留空表示所有分类都显示
多个分类用逗号间隔,如:技术,生活' + ); + $watermarkIncludeCategories->input->setAttribute('style', 'width: 300px;'); + $form->addInput($watermarkIncludeCategories); + + $watermarkOpacity = new Typecho_Widget_Helper_Form_Element_Text( + 'watermarkOpacity', + NULL, + '0.7', + '水印透明度', + '0-1之间,1为不透明' + ); + $watermarkOpacity->input->setAttribute('style', 'width: 100px;'); + $watermarkOpacity->input->setAttribute('placeholder', '0.0-1.0'); + $form->addInput($watermarkOpacity); + + $watermarkFontSize = new Typecho_Widget_Helper_Form_Element_Text( + 'watermarkFontSize', + NULL, + '24', + '水印字体大小', + '文字水印的字体大小(像素)' + ); + $watermarkFontSize->input->setAttribute('style', 'width: 100px;'); + $form->addInput($watermarkFontSize->addRule('isInteger', '请输入整数')); + + $watermarkFontColor = new Typecho_Widget_Helper_Form_Element_Text( + 'watermarkFontColor', + NULL, + 'rgba(255,255,255,0.9)', + '水印文字颜色', + '文字水印的颜色' + ); + $watermarkFontColor->input->setAttribute('style', 'width: 150px;'); + $form->addInput($watermarkFontColor); + + // 性能设置部分 + $batchSize = new Typecho_Widget_Helper_Form_Element_Text( + 'batchSize', + NULL, + '10', + '批量处理数量', + '每次同时处理的图片数量,减少一次性处理压力' + ); + $batchSize->input->setAttribute('style', 'width: 120px;'); + $form->addInput($batchSize->addRule('isInteger', '请输入整数')); + + $processDelay = new Typecho_Widget_Helper_Form_Element_Text( + 'processDelay', + NULL, + '100', + '处理延迟(ms)', + '图片加载后延迟处理的时间(毫秒)' + ); + $processDelay->input->setAttribute('style', 'width: 120px;'); + $form->addInput($processDelay->addRule('isInteger', '请输入整数')); + + $debugMode = new Typecho_Widget_Helper_Form_Element_Radio( + 'debugMode', + array('1' => '开启', '0' => '关闭'), + '0', + '调试模式', + '开启后在控制台显示处理信息(调试完成后建议关闭)' + ); + $form->addInput($debugMode); + } + + /** + * 个人用户配置面板 + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form) + { + // 不需要个人配置 + } + + /** + * 处理文章内容 + */ + public static function parseContent($content, $widget, $lastResult) + { + $content = empty($lastResult) ? $content : $lastResult; + + if ($widget instanceof Widget_Archive) { + $content = self::parseImages($content, $widget); + } + + return $content; + } + + /** + * 解析图片并添加标题和水印 + */ + private static function parseImages($text, $widget) + { + $options = Helper::options(); + $config = $options->plugin('ImageCaptionWatermark'); + + if (!$config) { + return $text; + } + + // 获取当前文章分类 + $currentCategories = array(); + if ($widget->categories && is_array($widget->categories)) { + foreach ($widget->categories as $category) { + if (isset($category['name'])) { + $currentCategories[] = $category['name']; + } + } + } + + // 处理显示分类设置 + $titleIncludeCategories = array(); + if (!empty($config->titleIncludeCategories) && trim($config->titleIncludeCategories) !== '') { + $titleIncludeCategories = array_map('trim', explode(',', $config->titleIncludeCategories)); + } + + $watermarkIncludeCategories = array(); + if (!empty($config->watermarkIncludeCategories) && trim($config->watermarkIncludeCategories) !== '') { + $watermarkIncludeCategories = array_map('trim', explode(',', $config->watermarkIncludeCategories)); + } + + // 检查当前分类是否在显示列表中 + $isTitleIncluded = true; // 默认显示,除非设置了显示分类且当前分类不在其中 + $isWatermarkIncluded = true; // 默认显示,除非设置了显示分类且当前分类不在其中 + + // 如果设置了显示分类,则检查当前分类是否在其中 + if (!empty($titleIncludeCategories)) { + $isTitleIncluded = false; + foreach ($currentCategories as $category) { + if (in_array($category, $titleIncludeCategories)) { + $isTitleIncluded = true; + break; + } + } + } + + if (!empty($watermarkIncludeCategories)) { + $isWatermarkIncluded = false; + foreach ($currentCategories as $category) { + if (in_array($category, $watermarkIncludeCategories)) { + $isWatermarkIncluded = true; + break; + } + } + } + + // 匹配图片标签 + $text = preg_replace_callback('/]*>/i', function($matches) use ($config, $isTitleIncluded, $isWatermarkIncluded) { + $imgTag = $matches[0]; + + // 跳过特定图片 + if (preg_match('/class=["\'][^"\']*(no-watermark|no-caption)[^"\']*["\']/i', $imgTag)) { + return $imgTag; + } + + // 检查图片是否在横排组中(通过父元素判断) + $skipTitle = false; + if ($config->skipFlexGroupTitles == '1') { + // 检查img标签周围是否有flexphoto-group的迹象 + if (strpos($imgTag, 'no-title') !== false || + preg_match('/class=["\'][^"\']*no-title[^"\']*["\']/i', $imgTag)) { + $skipTitle = true; + } + } + + $newTag = $imgTag; + + // 获取图片URL + $imgUrl = ''; + if (preg_match('/src=["\']([^"\']+)["\']/', $imgTag, $srcMatch)) { + $imgUrl = $srcMatch[1]; + } + + // 获取标题(从alt或title属性) + $caption = ''; + $hasChinese = false; + + if (!$skipTitle) { // 只有需要显示标题时才处理 + if (preg_match('/alt=["\']([^"\']*)["\']/', $imgTag, $altMatch)) { + $caption = $altMatch[1]; + $hasChinese = self::hasChineseText($caption); + } elseif (preg_match('/title=["\']([^"\']*)["\']/', $imgTag, $titleMatch)) { + $caption = $titleMatch[1]; + $hasChinese = self::hasChineseText($caption); + } + } + + // 如果开启了仅显示中文标题,且没有中文,则清空标题 + if ($config->titleChineseOnly == '1' && !$hasChinese) { + $caption = ''; + } else { + // 提取中文部分 + $caption = self::extractChineseText($caption); + } + + // 添加水印数据属性(如果当前分类在显示列表中) + $watermarkData = ''; + if ($isWatermarkIncluded && $config->watermarkEnable == '1' && !empty($config->watermarkText)) { + $watermarkClass = 'icw-watermark'; + $watermarkData = ' data-watermark="' . htmlspecialchars($config->watermarkText) . '"'; + $watermarkData .= ' data-watermark-position="' . htmlspecialchars($config->watermarkPosition) . '"'; + $watermarkData .= ' data-watermark-minwidth="' . intval($config->watermarkMinWidth) . '"'; + $watermarkData .= ' data-watermark-minheight="' . intval($config->watermarkMinHeight) . '"'; + + $newTag = preg_replace('/titleEnable == '1' && !empty($caption) && !empty($imgUrl)) || + ($isWatermarkIncluded && $config->watermarkEnable == '1' && !empty($imgUrl))) { + + $wrapperClass = 'icw-wrapper'; + + // 添加标题数据(如果当前分类在显示列表中且需要显示标题) + $titleData = ''; + if (!$skipTitle && $isTitleIncluded && $config->titleEnable == '1' && !empty($caption)) { + $titleData .= ' data-caption="' . htmlspecialchars($caption) . '"'; + $titleData .= ' data-position="' . htmlspecialchars($config->titlePosition) . '"'; + $titleData .= ' data-minwidth="' . intval($config->titleMinWidth) . '"'; + $titleData .= ' data-minheight="' . intval($config->titleMinHeight) . '"'; + $titleData .= ' data-chinese-only="' . $config->titleChineseOnly . '"'; + + if ($isWatermarkIncluded && $config->watermarkEnable == '1') { + $wrapperClass .= ' icw-has-both'; + } else { + $wrapperClass .= ' icw-title-only'; + } + } elseif ($isWatermarkIncluded && $config->watermarkEnable == '1') { + $wrapperClass .= ' icw-watermark-only'; + } + + // 如果是横排组内的图片,添加特殊标记 + if ($skipTitle) { + $wrapperClass .= ' icw-in-flexgroup'; + } + + $newTag = '
+ ' . $newTag . ' +
'; + } + + return $newTag; + }, $text); + + return $text; + } + + /** + * 检查是否包含中文字符 + */ + private static function hasChineseText($text) + { + return preg_match('/[\x{4e00}-\x{9fa5}]/u', $text); + } + + /** + * 提取中文字符 + */ + private static function extractChineseText($text) + { + preg_match_all('/[\x{4e00}-\x{9fa5}\x{3000}-\x{303F}\x{FF00}-\x{FFEF}]+/u', $text, $matches); + if (!empty($matches[0])) { + return trim(implode('', $matches[0])); + } + return $text; + } + + /** + * 添加页脚脚本和样式(前台) + */ + public static function addFooter() + { + $options = Helper::options(); + $config = $options->plugin('ImageCaptionWatermark'); + + if (!$config) { + return; + } + + $debugMode = $config->debugMode == '1' ? 'true' : 'false'; + $batchSize = intval($config->batchSize) ?: 10; + $processDelay = intval($config->processDelay) ?: 100; + $skipFlexGroupTitles = $config->skipFlexGroupTitles == '1' ? 'true' : 'false'; + + // 输出CSS样式 + echo ''; + + // 输出JavaScript - 修改以支持跳过横排组标题 + echo ''; + } +} \ No newline at end of file