PluginProbe
WPWaterMark 轻水印插件 / 5.0.0
WPWaterMark 轻水印插件 v5.0.0
5.2.4 5.2.2 5.2.1 5.1.7 5.1.6 trunk 4.3 5.0.0 5.0.1 5.1.2 5.1.3 5.1.4 5.1.5
wpwatermark / WaterMarkHandler.php

WaterMarkHandler.php in WPWaterMark 轻水印插件 5.0.0, at WaterMarkHandler.php

391 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WaterMark Handler Class
4 *
5 * Handles all watermark related operations with improved organization and caching
6 */
7 class WaterMarkHandler {
8 /** @var string */
9 private $cache_dir;
10
11 /** @var string */
12 private $font_dir;
13
14 /** @var array */
15 private $options;
16
17 /**
18 * Constructor
19 *
20 * @param array $options Watermark options
21 */
22 public function __construct(array $options) {
23 $this->options = $options;
24 $this->cache_dir = plugin_dir_path(__FILE__) . 'cache/';
25 $this->font_dir = plugin_dir_path(__FILE__) . 'fonts/';
26
27 // Ensure cache directory exists
28 if (!file_exists($this->cache_dir)) {
29 wp_mkdir_p($this->cache_dir);
30 }
31 }
32
33 /**
34 * Generate cache key for watermark options
35 *
36 * @param string $img_url
37 * @param array $options
38 * @return string
39 */
40 private function generateCacheKey(string $img_url, array $options): string {
41 return md5($img_url . serialize($options));
42 }
43
44 /**
45 * Check if cached version exists
46 *
47 * @param string $cache_key
48 * @return string|false
49 */
50 private function getCachedImage(string $cache_key) {
51 $cache_file = $this->cache_dir . $cache_key . '.jpg';
52 if (file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) {
53 return $cache_file;
54 }
55 return false;
56 }
57
58 /**
59 * Create text watermark with improved error handling and caching
60 *
61 * @param string $img_url
62 * @param string $new_img_url
63 * @param string $text
64 * @param array $options
65 * @return bool
66 */
67 public function createTextWatermark(string $img_url, string $new_img_url, string $text, array $options = []): bool {
68 try {
69 // Generate cache key
70 $cache_key = $this->generateCacheKey($img_url, array_merge($this->options, $options));
71
72 // Check cache
73 $cached_file = $this->getCachedImage($cache_key);
74 if ($cached_file) {
75 copy($cached_file, $new_img_url);
76 return true;
77 }
78
79 // Validate image
80 $img_size = getimagesize($img_url);
81 if (empty($img_size)) {
82 throw new Exception('Invalid image file');
83 }
84
85 // Validate dimensions
86 if ($img_size[0] < $this->options['watermark_min_width'] ||
87 $img_size[1] < $this->options['watermark_min_height']) {
88 throw new Exception('Image dimensions too small for watermark');
89 }
90
91 // Create image resource
92 $im = $this->createImageResource($img_url, $img_size['mime']);
93 if (!$im) {
94 throw new Exception('Failed to create image resource');
95 }
96
97 // Apply text watermark
98 $text_color = $this->parseColor($options['text_color'] ?? $this->options['text_color']);
99 $font_file = $this->font_dir . ($options['text_font'] ?? $this->options['text_font']);
100
101 if (!file_exists($font_file)) {
102 throw new Exception('Font file not found');
103 }
104
105 $text_color = imagecolorallocate($im, $text_color['r'], $text_color['g'], $text_color['b']);
106 $position = $this->calculatePosition($options['position'] ?? $this->options['watermark_position'],
107 $img_size[0],
108 $img_size[1],
109 $text);
110
111 // Add watermark
112 imagettftext(
113 $im,
114 $options['text_size'] ?? $this->options['text_size'],
115 $options['text_angle'] ?? $this->options['text_angle'],
116 $position['x'],
117 $position['y'],
118 $text_color,
119 $font_file,
120 $text
121 );
122
123 // Save image
124 $this->saveImage($im, $new_img_url, $img_size['mime']);
125
126 // Cache result
127 copy($new_img_url, $this->cache_dir . $cache_key . '.jpg');
128
129 imagedestroy($im);
130 return true;
131
132 } catch (Exception $e) {
133 error_log('WaterMark Error: ' . $e->getMessage());
134 return false;
135 }
136 }
137
138 /**
139 * Create image watermark with improved error handling and caching
140 *
141 * @param string $img_url
142 * @param string $watermark_url
143 * @param string $new_img_url
144 * @param array $options
145 * @return bool
146 */
147 public function createImageWatermark(string $img_url, string $watermark_url, string $new_img_url, array $options = []): bool {
148 try {
149 // Generate cache key
150 $cache_key = $this->generateCacheKey($img_url, array_merge($this->options, $options));
151
152 // Check cache
153 $cached_file = $this->getCachedImage($cache_key);
154 if ($cached_file) {
155 copy($cached_file, $new_img_url);
156 return true;
157 }
158
159 // Validate image
160 $img_size = getimagesize($img_url);
161 if (empty($img_size)) {
162 throw new Exception('Invalid image file');
163 }
164
165 // Validate dimensions
166 if ($img_size[0] < $this->options['watermark_min_width'] ||
167 $img_size[1] < $this->options['watermark_min_height']) {
168 throw new Exception('Image dimensions too small for watermark');
169 }
170
171 // Create image resource
172 $im = $this->createImageResource($img_url, $img_size['mime']);
173 if (!$im) {
174 throw new Exception('Failed to create image resource');
175 }
176
177 // Load watermark image
178 $watermark_size = getimagesize($watermark_url);
179 if (!$watermark_size) {
180 throw new Exception('Invalid watermark image');
181 }
182
183 $watermark = $this->createImageResource($watermark_url, $watermark_size['mime']);
184 if (!$watermark) {
185 throw new Exception('Failed to create watermark resource');
186 }
187
188 // Calculate position
189 $position = $this->calculatePosition(
190 $options['watermark_position'] ?? $this->options['watermark_position'],
191 $img_size[0],
192 $img_size[1],
193 '',
194 $watermark_size[0],
195 $watermark_size[1]
196 );
197
198 // Set transparency
199 $opacity = ($options['watermark_diaphaneity'] ?? $this->options['watermark_diaphaneity']) / 100;
200 imagealphablending($watermark, true);
201 imagesavealpha($watermark, true);
202
203 // Apply watermark
204 $this->imagecopymerge_alpha($im, $watermark, $position['x'], $position['y'], 0, 0,
205 $watermark_size[0], $watermark_size[1], $opacity * 100);
206
207 // Save image
208 $this->saveImage($im, $new_img_url, $img_size['mime']);
209
210 // Cache result
211 copy($new_img_url, $this->cache_dir . $cache_key . '.jpg');
212
213 // Clean up
214 imagedestroy($im);
215 imagedestroy($watermark);
216
217 return true;
218
219 } catch (Exception $e) {
220 error_log('WaterMark Error: ' . $e->getMessage());
221 return false;
222 }
223 }
224
225 /**
226 * Helper function to create image resource
227 *
228 * @param string $img_url
229 * @param string $mime_type
230 * @return resource|false
231 */
232 private function createImageResource(string $img_url, string $mime_type) {
233 $create_functions = [
234 'image/jpeg' => 'imagecreatefromjpeg',
235 'image/png' => 'imagecreatefrompng',
236 'image/gif' => 'imagecreatefromgif'
237 ];
238
239 if (isset($create_functions[$mime_type])) {
240 return call_user_func($create_functions[$mime_type], $img_url);
241 }
242
243 return false;
244 }
245
246 /**
247 * Helper function to save image
248 *
249 * @param resource $im
250 * @param string $filename
251 * @param string $mime_type
252 * @return bool
253 */
254 private function saveImage($im, string $filename, string $mime_type): bool {
255 $save_functions = [
256 'image/jpeg' => 'imagejpeg',
257 'image/png' => 'imagepng',
258 'image/gif' => 'imagegif'
259 ];
260
261 if (isset($save_functions[$mime_type])) {
262 return call_user_func($save_functions[$mime_type], $im, $filename);
263 }
264
265 return false;
266 }
267
268 /**
269 * Parse hex color to RGB
270 */
271 private function parseColor($hex_color) {
272 $hex_color = ltrim($hex_color, '#');
273 return [
274 'r' => hexdec(substr($hex_color, 0, 2)),
275 'g' => hexdec(substr($hex_color, 2, 2)),
276 'b' => hexdec(substr($hex_color, 4, 2))
277 ];
278 }
279
280 /**
281 * Helper function for alpha-enabled imagecopymerge
282 *
283 * @param resource $dst_im
284 * @param resource $src_im
285 * @param int $dst_x
286 * @param int $dst_y
287 * @param int $src_x
288 * @param int $src_y
289 * @param int $src_w
290 * @param int $src_h
291 * @param int $pct
292 * @return void
293 */
294 private function imagecopymerge_alpha($dst_im, $src_im, int $dst_x, int $dst_y, int $src_x, int $src_y, int $src_w, int $src_h, int $pct): void {
295 // Create a new transparent image
296 $cut = imagecreatetruecolor($src_w, $src_h);
297 imagealphablending($cut, false);
298 imagesavealpha($cut, true);
299
300 // Copy source image
301 imagecopy($cut, $dst_im, 0, 0, intval($dst_x), intval($dst_y), $src_w, $src_h);
302
303 // Copy watermark with transparency
304 imagecopy($cut, $src_im, 0, 0, intval($src_x), intval($src_y), $src_w, $src_h);
305 imagecopymerge($dst_im, $cut, intval($dst_x), intval($dst_y), 0, 0, $src_w, $src_h, intval($pct));
306
307 imagedestroy($cut);
308 }
309
310 /**
311 * Calculate watermark position
312 *
313 * @param string $position
314 * @param int $img_width
315 * @param int $img_height
316 * @param string $text
317 * @param int $mark_width
318 * @param int $mark_height
319 * @return array{x: int, y: int}
320 */
321 private function calculatePosition(string $position, int $img_width, int $img_height, string $text = '', int $mark_width = 0, int $mark_height = 0): array {
322 $margin = intval($this->options['watermark_margin']);
323
324 // For text watermark
325 if ($text !== '') {
326 $font_size = $this->options['text_size'];
327 $font_file = $this->font_dir . $this->options['text_font'];
328 $text_box = imagettfbbox($font_size, 0, $font_file, $text);
329 $mark_width = abs($text_box[4] - $text_box[0]);
330 $mark_height = abs($text_box[1] - $text_box[5]);
331 }
332
333 // Calculate position
334 switch ($position) {
335 case 'top-left':
336 return [
337 'x' => $margin,
338 'y' => $margin + $mark_height
339 ];
340
341 case 'top-center':
342 return [
343 'x' => intval(($img_width - $mark_width) / 2),
344 'y' => $margin + $mark_height
345 ];
346
347 case 'top-right':
348 return [
349 'x' => $img_width - $mark_width - $margin,
350 'y' => $margin + $mark_height
351 ];
352
353 case 'middle-left':
354 return [
355 'x' => $margin,
356 'y' => intval(($img_height + $mark_height) / 2)
357 ];
358
359 case 'middle-center':
360 return [
361 'x' => intval(($img_width - $mark_width) / 2),
362 'y' => intval(($img_height + $mark_height) / 2)
363 ];
364
365 case 'middle-right':
366 return [
367 'x' => $img_width - $mark_width - $margin,
368 'y' => intval(($img_height + $mark_height) / 2)
369 ];
370
371 case 'bottom-left':
372 return [
373 'x' => $margin,
374 'y' => $img_height - $margin
375 ];
376
377 case 'bottom-center':
378 return [
379 'x' => intval(($img_width - $mark_width) / 2),
380 'y' => $img_height - $margin
381 ];
382
383 case 'bottom-right':
384 default:
385 return [
386 'x' => $img_width - $mark_width - $margin,
387 'y' => $img_height - $margin
388 ];
389 }
390 }
391 }