Skip to content

Commit 42bc29e

Browse files
authored
Merge pull request #1154 from cloudinary/feature/crop-gutenberg-integration
Crop and Gravity Gutenberg Capability
2 parents 171b946 + 2ecfe2c commit 42bc29e

2 files changed

Lines changed: 126 additions & 3 deletions

File tree

php/class-delivery.php

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,38 @@ public function get_media_tags( $content, $tags = 'img|video' ) {
10061006
return $media;
10071007
}
10081008

1009+
/**
1010+
* Get the image size dimensions from the tags if available.
1011+
*
1012+
* @param array $tags The media tags found in the content.
1013+
* @param array $relation The relation data for the media item.
1014+
*
1015+
* @return array An array with width and height, or empty if not found.
1016+
*/
1017+
private function get_image_size_within_tags( $tags, $relation ) {
1018+
// If we don't have a post ID, we can't find a size, so return empty.
1019+
if ( empty( $relation['post_id'] ) ) {
1020+
return array();
1021+
}
1022+
$post_id = intval( $relation['post_id'] );
1023+
1024+
// Look through the tags to find one that matches our post ID and has a size slug, then get the size from that slug.
1025+
foreach ( $tags as $set ) {
1026+
// If this set doesn't have a size slug, skip.
1027+
if ( empty( $set['atts']['data-size-slug'] ) ) {
1028+
continue;
1029+
}
1030+
1031+
// If this tag's post ID matches our relation's post ID, get the size from the slug and return it.
1032+
if ( $post_id === $set['id'] ) {
1033+
$size = $this->media->get_size_from_slug( $set['atts']['data-size-slug'] );
1034+
return ( empty( $size ) ) ? array() : $size; // Return the size if found, otherwise return empty array.
1035+
}
1036+
}
1037+
1038+
return array();
1039+
}
1040+
10091041
/**
10101042
* Convert media tags from Local to Cloudinary, and register with String_Replace.
10111043
*
@@ -1025,7 +1057,12 @@ public function convert_tags( $content, $context = 'view' ) {
10251057
}
10261058

10271059
$tags = $this->get_media_tags( $content, 'img|video|article|source' );
1028-
$tags = array_map( array( $this, 'parse_element' ), $tags );
1060+
$tags = array_map(
1061+
function ( $tag ) use ( $content ) {
1062+
return $this->parse_element( $tag, $content );
1063+
},
1064+
$tags
1065+
);
10291066
$tags = array_filter( $tags );
10301067

10311068
$replacements = array();
@@ -1082,7 +1119,9 @@ public function convert_tags( $content, $context = 'view' ) {
10821119
$public_id = ! is_admin() ? $relation['public_id'] . '.' . $relation['format'] : null;
10831120
// Get merged transformations including overlays.
10841121
$merged_transformations = Relate::get_transformations( $relation['post_id'], true );
1085-
$cloudinary_url = $this->media->cloudinary_url( $relation['post_id'], array(), $merged_transformations, $public_id );
1122+
1123+
$size = $this->get_image_size_within_tags( $tags, $relation );
1124+
$cloudinary_url = $this->media->cloudinary_url( $relation['post_id'], $size, $merged_transformations, $public_id );
10861125
if ( empty( $cloudinary_url ) ) {
10871126
continue;
10881127
}
@@ -1220,6 +1259,15 @@ protected function standardize_tag( $tag_element ) {
12201259
$size = $has_wp_size;
12211260
}
12221261
}
1262+
1263+
// Retrieve size from the parent figure class of the image if it exists.
1264+
if ( ! empty( $tag_element['atts']['data-size-slug'] ) && 'img' === $tag_element['tag'] ) {
1265+
$slug_size = $this->media->get_size_from_slug( $tag_element['atts']['data-size-slug'] );
1266+
if ( ! empty( $slug_size ) ) {
1267+
$size = $slug_size;
1268+
}
1269+
}
1270+
12231271
// Unset srcset and sizes.
12241272
unset( $tag_element['atts']['srcset'], $tag_element['atts']['sizes'] );
12251273

@@ -1437,10 +1485,11 @@ public function rebuild_tag( $tag_element ) {
14371485
* Parse an html element into tag, and attributes.
14381486
*
14391487
* @param string $element The HTML element.
1488+
* @param string $content Optional full HTML content for parent context.
14401489
*
14411490
* @return array|null
14421491
*/
1443-
public function parse_element( $element ) {
1492+
public function parse_element( $element, $content = '' ) {
14441493
/**
14451494
* Filter to skip parsing an element.
14461495
*
@@ -1551,6 +1600,21 @@ public function parse_element( $element ) {
15511600
if ( in_array( $tag_element['tag'], array( 'img', 'source' ), true ) ) {
15521601
// Check if this is a crop or a scale.
15531602
$has_size = $this->media->get_size_from_url( $this->sanitize_url( $raw_url ) );
1603+
1604+
// If no size found in URL, try to extract from parent figure element so we can apply cropping if needed.
1605+
if ( empty( $has_size ) && $has_sized_transformation ) {
1606+
$size_slug = $this->media->get_size_slug_from_parent_figure_class( $tag_element['original'], $content );
1607+
1608+
if ( ! empty( $size_slug ) ) {
1609+
$has_size = $this->media->get_size_from_slug( $size_slug );
1610+
if ( ! empty( $has_size ) ) {
1611+
$attributes['data-size-slug'] = $size_slug;
1612+
$tag_element['width'] = $has_size[0];
1613+
$tag_element['height'] = $has_size[1];
1614+
}
1615+
}
1616+
}
1617+
15541618
if ( ! empty( $has_size ) && ! empty( $item['height'] ) ) {
15551619
$file_ratio = round( $has_size[0] / $has_size[1], 2 );
15561620
$original_ratio = round( $item['width'] / $item['height'], 2 );

php/class-media.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,4 +3267,63 @@ public function upgrade_settings( $previous_version, $new_version ) {
32673267
$setting->save_value();
32683268
}
32693269
}
3270+
3271+
3272+
/**
3273+
* Get the size dimensions based on the size slug.
3274+
*
3275+
* Uses WordPress core API to retrieve registered image subsizes, which handles both
3276+
* built-in sizes (thumbnail, medium, large, etc.) and custom registered sizes.
3277+
*
3278+
* @param string $size_slug The WordPress size slug (e.g., 'thumbnail', 'medium', 'large').
3279+
*
3280+
* @return array|null An array with width and height, or null if not found.
3281+
*/
3282+
public function get_size_from_slug( $size_slug ) {
3283+
// Use WordPress core API to get all registered image subsizes.
3284+
$image_subsizes = wp_get_registered_image_subsizes();
3285+
3286+
// Check if the requested size exists in registered subsizes.
3287+
if ( ! isset( $image_subsizes[ $size_slug ] ) ) {
3288+
return null;
3289+
}
3290+
3291+
$size_data = $image_subsizes[ $size_slug ];
3292+
3293+
// Return width and height if both are present and valid.
3294+
if ( ! empty( $size_data['width'] ) && ! empty( $size_data['height'] ) ) {
3295+
return array( (int) $size_data['width'], (int) $size_data['height'] );
3296+
}
3297+
3298+
return null;
3299+
}
3300+
3301+
3302+
/**
3303+
* Extract WordPress image size from parent figure element.
3304+
*
3305+
* @param string $element The img tag element.
3306+
* @param string $content The full HTML content.
3307+
*
3308+
* @return string|null The WordPress size slug (e.g., 'thumbnail', 'medium'), or null if not found.
3309+
*/
3310+
public function get_size_slug_from_parent_figure_class( $element, $content ) {
3311+
// If content is empty, we can't find a parent figure, so return null.
3312+
if ( empty( $content ) ) {
3313+
return null;
3314+
}
3315+
3316+
// Escape the element for use in regex.
3317+
$escaped_element = preg_quote( $element, '#' );
3318+
3319+
// Pattern: <figure class="...size-{size}...">...img element...[optional figcaption]</figure> .
3320+
$pattern = '#<figure\s+[^>]*class="[^"]*\bsize-(\w+)\b[^"]*"[^>]*>.*?' . $escaped_element . '.*?</figure>#is';
3321+
3322+
// Look for the parent figure tag that contains this img element.
3323+
if ( preg_match( $pattern, $content, $matches ) ) {
3324+
return $matches[1];
3325+
}
3326+
3327+
return null;
3328+
}
32703329
}

0 commit comments

Comments
 (0)