From 09a8d9223dcbd2b056eaf373f14f597f80ea6de6 Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Thu, 5 Mar 2026 10:21:02 +0100 Subject: [PATCH 1/4] Pick up size for cropping from img parent figure class name --- php/class-delivery.php | 72 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/php/class-delivery.php b/php/class-delivery.php index ea597a1a..58ba8e20 100644 --- a/php/class-delivery.php +++ b/php/class-delivery.php @@ -1006,6 +1006,62 @@ public function get_media_tags( $content, $tags = 'img|video' ) { return $media; } + /** + * Get the size dimensions from the parent figure class if available. + * + * @param string $element The img tag element. + * @param string $content The full HTML content. + * + * @return array|null An array with width and height, or null if not found. + */ + private function get_size_from_parent( $element, $content ) { + $size_slug = $this->get_size_slug_from_parent_figure_class( $element, $content ); + + // If no size slug is found in the parent class, there's no specific size to use for transformation. + if ( empty( $size_slug ) ) { + return null; + } + + // Get the dimensions of the WordPress size from options. + $size_width = get_option( $size_slug . '_size_w' ); + $size_height = get_option( $size_slug . '_size_h' ); + + // Check if we have valid dimensions. If not, return null to indicate no specific size. + if ( empty( $size_width ) || empty( $size_height ) ) { + return null; + } + + return array( (int) $size_width, (int) $size_height ); + } + + /** + * Extract WordPress image size from parent figure element. + * + * @param string $element The img tag element. + * @param string $content The full HTML content. + * + * @return string|null The WordPress size slug (e.g., 'thumbnail', 'medium'), or null if not found. + */ + private function get_size_slug_from_parent_figure_class( $element, $content ) { + // If content is empty, we can't find a parent figure, so return null. + if ( empty( $content ) ) { + return null; + } + + // Escape the element for use in regex. + $escaped_element = preg_quote( $element, '#' ); + + // Pattern:
...img element...[optional figcaption]
. + $pattern = '#]*class="[^"]*\bsize-(\w+)\b[^"]*"[^>]*>.*?' . $escaped_element . '.*?#is'; + + // Look for the parent figure tag that contains this img element. + if ( preg_match( $pattern, $content, $matches ) ) { + return $matches[1]; + } + + return null; + } + /** * Convert media tags from Local to Cloudinary, and register with String_Replace. * @@ -1025,7 +1081,12 @@ public function convert_tags( $content, $context = 'view' ) { } $tags = $this->get_media_tags( $content, 'img|video|article|source' ); - $tags = array_map( array( $this, 'parse_element' ), $tags ); + $tags = array_map( + function ( $tag ) use ( $content ) { + return $this->parse_element( $tag, $content ); + }, + $tags + ); $tags = array_filter( $tags ); $replacements = array(); @@ -1437,10 +1498,11 @@ public function rebuild_tag( $tag_element ) { * Parse an html element into tag, and attributes. * * @param string $element The HTML element. + * @param string $content Optional full HTML content for parent context. * * @return array|null */ - public function parse_element( $element ) { + public function parse_element( $element, $content = '' ) { /** * Filter to skip parsing an element. * @@ -1551,6 +1613,12 @@ public function parse_element( $element ) { if ( in_array( $tag_element['tag'], array( 'img', 'source' ), true ) ) { // Check if this is a crop or a scale. $has_size = $this->media->get_size_from_url( $this->sanitize_url( $raw_url ) ); + + // If no size found in URL, try to extract from parent figure element so we can apply cropping if needed. + if ( empty( $has_size ) && $has_sized_transformation ) { + $has_size = $this->get_size_from_parent( $tag_element['original'], $content ); + } + if ( ! empty( $has_size ) && ! empty( $item['height'] ) ) { $file_ratio = round( $has_size[0] / $has_size[1], 2 ); $original_ratio = round( $item['width'] / $item['height'], 2 ); From ad95a7c8b736ca8a2b975e9363991ae6ba378e2c Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Fri, 6 Mar 2026 14:11:32 +0100 Subject: [PATCH 2/4] Apply image crop on editor and frontend with eager loading --- php/class-delivery.php | 68 +++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/php/class-delivery.php b/php/class-delivery.php index 58ba8e20..f4addc6c 100644 --- a/php/class-delivery.php +++ b/php/class-delivery.php @@ -1007,21 +1007,46 @@ public function get_media_tags( $content, $tags = 'img|video' ) { } /** - * Get the size dimensions from the parent figure class if available. + * Get the image size dimensions from the tags if available. * - * @param string $element The img tag element. - * @param string $content The full HTML content. + * @param array $tags The media tags found in the content. + * @param array $relation The relation data for the media item. * - * @return array|null An array with width and height, or null if not found. + * @return array An array with width and height, or empty if not found. */ - private function get_size_from_parent( $element, $content ) { - $size_slug = $this->get_size_slug_from_parent_figure_class( $element, $content ); + private function get_image_size_within_tags( $tags, $relation ) { + // If we don't have a post ID, we can't find a size, so return empty. + if ( empty( $relation['post_id'] ) ) { + return array(); + } + $post_id = intval( $relation['post_id'] ); - // If no size slug is found in the parent class, there's no specific size to use for transformation. - if ( empty( $size_slug ) ) { - return null; + // Look through the tags to find one that matches our post ID and has a size slug, then get the size from that slug. + foreach ( $tags as $set ) { + // If this set doesn't have a size slug, skip. + if ( empty( $set['atts']['data-size-slug'] ) ) { + continue; + } + + // If this tag's post ID matches our relation's post ID, get the size from the slug and return it. + if ( $post_id === $set['id'] ) { + $size = $this->get_size_from_slug( $set['atts']['data-size-slug'] ); + return ( empty( $size ) ) ? array() : $size; // Return the size if found, otherwise return empty array. + } } + return array(); + } + + /** + * Get the size dimensions based on the size slug. + * + * @param string $size_slug The WordPress size slug (e.g., 'thumbnail', 'medium', 'large'). + * + * @return array|null An array with width and height, or null if not found. + */ + private function get_size_from_slug( $size_slug ) { + // Get the dimensions of the WordPress size from options. $size_width = get_option( $size_slug . '_size_w' ); $size_height = get_option( $size_slug . '_size_h' ); @@ -1143,7 +1168,9 @@ function ( $tag ) use ( $content ) { $public_id = ! is_admin() ? $relation['public_id'] . '.' . $relation['format'] : null; // Get merged transformations including overlays. $merged_transformations = Relate::get_transformations( $relation['post_id'], true ); - $cloudinary_url = $this->media->cloudinary_url( $relation['post_id'], array(), $merged_transformations, $public_id ); + + $size = $this->get_image_size_within_tags( $tags, $relation ); + $cloudinary_url = $this->media->cloudinary_url( $relation['post_id'], $size, $merged_transformations, $public_id ); if ( empty( $cloudinary_url ) ) { continue; } @@ -1281,6 +1308,15 @@ protected function standardize_tag( $tag_element ) { $size = $has_wp_size; } } + + // Retrieve size from the parent figure class of the image if it exists. + if ( ! empty( $tag_element['atts']['data-size-slug'] ) && 'img' === $tag_element['tag'] ) { + $slug_size = $this->get_size_from_slug( $tag_element['atts']['data-size-slug'] ); + if ( ! empty( $slug_size ) ) { + $size = $slug_size; + } + } + // Unset srcset and sizes. unset( $tag_element['atts']['srcset'], $tag_element['atts']['sizes'] ); @@ -1616,7 +1652,17 @@ public function parse_element( $element, $content = '' ) { // If no size found in URL, try to extract from parent figure element so we can apply cropping if needed. if ( empty( $has_size ) && $has_sized_transformation ) { - $has_size = $this->get_size_from_parent( $tag_element['original'], $content ); + $size_slug = $this->get_size_slug_from_parent_figure_class( $tag_element['original'], $content ); + + if ( ! empty( $size_slug ) ) { + $has_size = $this->get_size_from_slug( $size_slug ); + if ( ! empty( $has_size ) ) { + $attributes['data-size-slug'] = $size_slug; + $tag_element['width'] = $has_size[0]; + $tag_element['height'] = $has_size[1]; + + } + } } if ( ! empty( $has_size ) && ! empty( $item['height'] ) ) { From 214812ef2f377c4b5c09c2b227b51ae64566e8cc Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Tue, 10 Mar 2026 14:08:18 +0100 Subject: [PATCH 3/4] Extract image size methods into media class --- php/class-delivery.php | 58 +++--------------------------------------- php/class-media.php | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/php/class-delivery.php b/php/class-delivery.php index f4addc6c..61f14fa3 100644 --- a/php/class-delivery.php +++ b/php/class-delivery.php @@ -1030,7 +1030,7 @@ private function get_image_size_within_tags( $tags, $relation ) { // If this tag's post ID matches our relation's post ID, get the size from the slug and return it. if ( $post_id === $set['id'] ) { - $size = $this->get_size_from_slug( $set['atts']['data-size-slug'] ); + $size = $this->media->get_size_from_slug( $set['atts']['data-size-slug'] ); return ( empty( $size ) ) ? array() : $size; // Return the size if found, otherwise return empty array. } } @@ -1038,55 +1038,6 @@ private function get_image_size_within_tags( $tags, $relation ) { return array(); } - /** - * Get the size dimensions based on the size slug. - * - * @param string $size_slug The WordPress size slug (e.g., 'thumbnail', 'medium', 'large'). - * - * @return array|null An array with width and height, or null if not found. - */ - private function get_size_from_slug( $size_slug ) { - - // Get the dimensions of the WordPress size from options. - $size_width = get_option( $size_slug . '_size_w' ); - $size_height = get_option( $size_slug . '_size_h' ); - - // Check if we have valid dimensions. If not, return null to indicate no specific size. - if ( empty( $size_width ) || empty( $size_height ) ) { - return null; - } - - return array( (int) $size_width, (int) $size_height ); - } - - /** - * Extract WordPress image size from parent figure element. - * - * @param string $element The img tag element. - * @param string $content The full HTML content. - * - * @return string|null The WordPress size slug (e.g., 'thumbnail', 'medium'), or null if not found. - */ - private function get_size_slug_from_parent_figure_class( $element, $content ) { - // If content is empty, we can't find a parent figure, so return null. - if ( empty( $content ) ) { - return null; - } - - // Escape the element for use in regex. - $escaped_element = preg_quote( $element, '#' ); - - // Pattern:
...img element...[optional figcaption]
. - $pattern = '#]*class="[^"]*\bsize-(\w+)\b[^"]*"[^>]*>.*?' . $escaped_element . '.*?#is'; - - // Look for the parent figure tag that contains this img element. - if ( preg_match( $pattern, $content, $matches ) ) { - return $matches[1]; - } - - return null; - } - /** * Convert media tags from Local to Cloudinary, and register with String_Replace. * @@ -1311,7 +1262,7 @@ protected function standardize_tag( $tag_element ) { // Retrieve size from the parent figure class of the image if it exists. if ( ! empty( $tag_element['atts']['data-size-slug'] ) && 'img' === $tag_element['tag'] ) { - $slug_size = $this->get_size_from_slug( $tag_element['atts']['data-size-slug'] ); + $slug_size = $this->media->get_size_from_slug( $tag_element['atts']['data-size-slug'] ); if ( ! empty( $slug_size ) ) { $size = $slug_size; } @@ -1652,15 +1603,14 @@ public function parse_element( $element, $content = '' ) { // If no size found in URL, try to extract from parent figure element so we can apply cropping if needed. if ( empty( $has_size ) && $has_sized_transformation ) { - $size_slug = $this->get_size_slug_from_parent_figure_class( $tag_element['original'], $content ); + $size_slug = $this->media->get_size_slug_from_parent_figure_class( $tag_element['original'], $content ); if ( ! empty( $size_slug ) ) { - $has_size = $this->get_size_from_slug( $size_slug ); + $has_size = $this->media->get_size_from_slug( $size_slug ); if ( ! empty( $has_size ) ) { $attributes['data-size-slug'] = $size_slug; $tag_element['width'] = $has_size[0]; $tag_element['height'] = $has_size[1]; - } } } diff --git a/php/class-media.php b/php/class-media.php index d034e210..3034054a 100644 --- a/php/class-media.php +++ b/php/class-media.php @@ -3267,4 +3267,53 @@ public function upgrade_settings( $previous_version, $new_version ) { $setting->save_value(); } } + + + /** + * Get the size dimensions based on the size slug. + * + * @param string $size_slug The WordPress size slug (e.g., 'thumbnail', 'medium', 'large'). + * + * @return array|null An array with width and height, or null if not found. + */ + public function get_size_from_slug( $size_slug ) { + // Get the dimensions of the WordPress size from options. + $size_width = get_option( $size_slug . '_size_w' ); + $size_height = get_option( $size_slug . '_size_h' ); + + // Check if we have valid dimensions. If not, return null to indicate no specific size. + if ( empty( $size_width ) || empty( $size_height ) ) { + return null; + } + + return array( (int) $size_width, (int) $size_height ); + } + + /** + * Extract WordPress image size from parent figure element. + * + * @param string $element The img tag element. + * @param string $content The full HTML content. + * + * @return string|null The WordPress size slug (e.g., 'thumbnail', 'medium'), or null if not found. + */ + public function get_size_slug_from_parent_figure_class( $element, $content ) { + // If content is empty, we can't find a parent figure, so return null. + if ( empty( $content ) ) { + return null; + } + + // Escape the element for use in regex. + $escaped_element = preg_quote( $element, '#' ); + + // Pattern:
...img element...[optional figcaption]
. + $pattern = '#]*class="[^"]*\bsize-(\w+)\b[^"]*"[^>]*>.*?' . $escaped_element . '.*?#is'; + + // Look for the parent figure tag that contains this img element. + if ( preg_match( $pattern, $content, $matches ) ) { + return $matches[1]; + } + + return null; + } } From 2ecfe2cc61343d9f7098f9f239799fb00c4e1ff6 Mon Sep 17 00:00:00 2001 From: Gabriel de Tassigny Date: Tue, 10 Mar 2026 14:25:45 +0100 Subject: [PATCH 4/4] Improve image sub size handling --- php/class-media.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/php/class-media.php b/php/class-media.php index 3034054a..4b1aedd6 100644 --- a/php/class-media.php +++ b/php/class-media.php @@ -3272,23 +3272,33 @@ public function upgrade_settings( $previous_version, $new_version ) { /** * Get the size dimensions based on the size slug. * + * Uses WordPress core API to retrieve registered image subsizes, which handles both + * built-in sizes (thumbnail, medium, large, etc.) and custom registered sizes. + * * @param string $size_slug The WordPress size slug (e.g., 'thumbnail', 'medium', 'large'). * * @return array|null An array with width and height, or null if not found. */ public function get_size_from_slug( $size_slug ) { - // Get the dimensions of the WordPress size from options. - $size_width = get_option( $size_slug . '_size_w' ); - $size_height = get_option( $size_slug . '_size_h' ); + // Use WordPress core API to get all registered image subsizes. + $image_subsizes = wp_get_registered_image_subsizes(); - // Check if we have valid dimensions. If not, return null to indicate no specific size. - if ( empty( $size_width ) || empty( $size_height ) ) { + // Check if the requested size exists in registered subsizes. + if ( ! isset( $image_subsizes[ $size_slug ] ) ) { return null; } - return array( (int) $size_width, (int) $size_height ); + $size_data = $image_subsizes[ $size_slug ]; + + // Return width and height if both are present and valid. + if ( ! empty( $size_data['width'] ) && ! empty( $size_data['height'] ) ) { + return array( (int) $size_data['width'], (int) $size_data['height'] ); + } + + return null; } + /** * Extract WordPress image size from parent figure element. *