diff --git a/php/class-delivery.php b/php/class-delivery.php index ea597a1a..61f14fa3 100644 --- a/php/class-delivery.php +++ b/php/class-delivery.php @@ -1006,6 +1006,38 @@ public function get_media_tags( $content, $tags = 'img|video' ) { return $media; } + /** + * Get the image size dimensions from the tags if available. + * + * @param array $tags The media tags found in the content. + * @param array $relation The relation data for the media item. + * + * @return array An array with width and height, or empty if not found. + */ + 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'] ); + + // 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->media->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(); + } + /** * Convert media tags from Local to Cloudinary, and register with String_Replace. * @@ -1025,7 +1057,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(); @@ -1082,7 +1119,9 @@ public function convert_tags( $content, $context = 'view' ) { $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; } @@ -1220,6 +1259,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->media->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'] ); @@ -1437,10 +1485,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 +1600,21 @@ 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 ) { + $size_slug = $this->media->get_size_slug_from_parent_figure_class( $tag_element['original'], $content ); + + if ( ! empty( $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]; + } + } + } + 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 ); diff --git a/php/class-media.php b/php/class-media.php index d034e210..4b1aedd6 100644 --- a/php/class-media.php +++ b/php/class-media.php @@ -3267,4 +3267,63 @@ public function upgrade_settings( $previous_version, $new_version ) { $setting->save_value(); } } + + + /** + * 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 ) { + // Use WordPress core API to get all registered image subsizes. + $image_subsizes = wp_get_registered_image_subsizes(); + + // Check if the requested size exists in registered subsizes. + if ( ! isset( $image_subsizes[ $size_slug ] ) ) { + return null; + } + + $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. + * + * @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; + } }