PluginProbe
Extendify / trunk
Extendify vtrunk
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | app/Draft/Controllers/ImageController.php +88 -0 3.0.6 → trunk View file →
@@ -23,8 +23,16 @@
23 23
24 24 class ImageController
25 25 {
26 26 /**
27 + * Must match `PILL_RIGHT_EDGE` in `src/Shared/lib/ai-label.js`.
28 + *
29 + * @var float
30 + */
31 + // phpcs:ignore PSR12.Properties.ConstantVisibility.NotFound -- 7.0 floor: no const visibility
32 + const PILL_RIGHT_EDGE = 0.85;
33 +
34 + /**
27 35 * Upload the provided image
28 36 *
29 37 * @param \WP_REST_Request $request - The request.
30 38 * @return \WP_REST_Response
@@ -38,8 +46,21 @@
38 46 }
39 47
40 48 $imageId = \media_sideload_image($request->get_param('source'), 0, null, 'id');
41 49
50 + // Without this the caller can't tell a failed upload from a declined one.
51 + if (\is_wp_error($imageId)) {
52 + return new \WP_REST_Response(['message' => $imageId->get_error_message()], 500);
53 + }
54 +
55 + if ($request->get_param('ai_generated')) {
56 + update_post_meta($imageId, 'extendify_ai_generated', true);
57 + $label = $request->get_file_params()['disclosure_label'] ?? null;
58 + if (isset($label['tmp_name'])) {
59 + self::stampImage($imageId, $label['tmp_name']);
60 + }
61 + }
62 +
42 63 if ($request->get_param('alt_text')) {
43 64 update_post_meta(
44 65 $imageId,
45 66 '_wp_attachment_image_alt',
@@ -68,6 +89,73 @@
68 89 'source_url' => wp_get_attachment_url($imageId),
69 90 'alt_text' => $altText,
70 91 ]
71 92 );
93 + }
94 +
95 + /**
96 + * Composite the client-rendered disclosure label onto the attachment. The
97 + * PNG already includes its border, unlike the canvas path's fill box.
98 + *
99 + * @param int $imageId - The attachment ID.
100 + * @param string $labelPath - Path to the uploaded label PNG.
101 + * @return void
102 + */
103 + private static function stampImage($imageId, $labelPath)
104 + {
105 + $file = \get_attached_file($imageId);
106 + $type = $file ? (\wp_check_filetype($file)['type'] ?? null) : null;
107 + $loaders = [
108 + 'image/jpeg' => 'imagecreatefromjpeg',
109 + 'image/png' => 'imagecreatefrompng',
110 + 'image/webp' => 'imagecreatefromwebp',
111 + ];
112 + if (!isset($loaders[$type]) || !function_exists($loaders[$type]) || !is_file($file)) {
113 + return;
114 + }
115 +
116 + // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
117 + $image = @call_user_func($loaders[$type], $file);
118 + // phpcs:ignore WordPress.PHP.NoSilencedErrors, Generic.PHP.NoSilencedErrors.Discouraged
119 + $label = @imagecreatefrompng($labelPath);
120 + if (!$image || !$label) {
121 + return;
122 + }
123 +
124 + $width = imagesx($image);
125 + $height = imagesy($image);
126 + // The client renders the label at a 48px font; the canvas stamp uses 2% of the shorter side.
127 + $scale = max(12, round(min($width, $height) * 0.02)) / 48;
128 + $labelWidth = (int) round(imagesx($label) * $scale);
129 + $labelHeight = (int) round(imagesy($label) * $scale);
130 + imagealphablending($image, true);
131 + imagecopyresampled(
132 + $image,
133 + $label,
134 + (int) round(($width * self::PILL_RIGHT_EDGE) - $labelWidth),
135 + (int) round(($height - $labelHeight) / 2),
136 + 0,
137 + 0,
138 + $labelWidth,
139 + $labelHeight,
140 + imagesx($label),
141 + imagesy($label)
142 + );
143 +
144 + if ($type === 'image/jpeg') {
145 + imagejpeg($image, $file, 90);
146 + }
147 +
148 + if ($type === 'image/png') {
149 + imagesavealpha($image, true);
150 + imagepng($image, $file);
151 + }
152 +
153 + if ($type === 'image/webp') {
154 + imagewebp($image, $file, 90);
155 + }
156 +
157 + // Sub-sizes were generated from the unstamped original; rebuild them.
158 + $metadata = \wp_generate_attachment_metadata($imageId, $file);
159 + \wp_update_attachment_metadata($imageId, $metadata);
72 160 }
73 161 }