diff --git a/php-transformer/src/HtmlToBlocks/Style/EngineSupportCss.php b/php-transformer/src/HtmlToBlocks/Style/EngineSupportCss.php
index 618806958..b023f95e5 100644
--- a/php-transformer/src/HtmlToBlocks/Style/EngineSupportCss.php
+++ b/php-transformer/src/HtmlToBlocks/Style/EngineSupportCss.php
@@ -92,6 +92,13 @@ public function beforeAuthorCss(string $serializedBlocks, string $layoutShellBlo
if ( str_contains($serializedBlocks, SourceBlockAttributeProjector::SYNTHETIC_IMAGE_FIGURE_CLASS) ) {
$parts[] = '.' . SourceBlockAttributeProjector::SYNTHETIC_IMAGE_FIGURE_CLASS . '{margin:0}';
}
+ if ( str_contains($serializedBlocks, SourceBlockAttributeProjector::SYNTHETIC_FILL_IMAGE_FIGURE_CLASS) ) {
+ // The source image fills both axes of its parent. Its core/image
+ // figure is an extra box; give that box the source's full inline
+ // extent so the image's percentage width does not size from an
+ // aspect-ratio-constrained, shrink-to-fit figure instead.
+ $parts[] = ':root :where(figure.' . SourceBlockAttributeProjector::SYNTHETIC_FILL_IMAGE_FIGURE_CLASS . '){width:100%}';
+ }
if ( str_contains($serializedBlocks, 'syntheticImageFigureFollowsInlineFlow ) {
$attrs['className'] = SourceDom::mergeClassNames((string) ($attrs['className'] ?? ''), self::SYNTHETIC_INLINE_IMAGE_FIGURE_CLASS);
+ } elseif ( $this->sourceImageFillsParent($sourceElement) ) {
+ $attrs['className'] = SourceDom::mergeClassNames((string) ($attrs['className'] ?? ''), self::SYNTHETIC_FILL_IMAGE_FIGURE_CLASS);
}
}
if ( 'core/paragraph' === $name && $facts->isInlineSourceElement ) {
@@ -119,6 +122,21 @@ public function project(
return ( new EditorListViewContainerNamer() )->apply($name, $attrs, $sourceElement);
}
+ private function sourceImageFillsParent(DOMElement $image): bool
+ {
+ if ( 'img' !== strtolower($image->tagName) ) {
+ return false;
+ }
+ $shape = $this->styleResolver->imageShapeDeclarations($image);
+ foreach ( array( 'width', 'height' ) as $axis ) {
+ $value = $this->styleResolver->resolveCssVariablesInValue((string) ($shape[$axis]['value'] ?? ''), $image);
+ if ( '100%' !== CssValueInspector::comparable($value) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private static function isHiddenAccessibilitySupportElement(DOMElement $element): bool
{
$identity = strtolower(SourceDom::attr($element, 'id') . ' ' . SourceDom::attr($element, 'class'));
diff --git a/php-transformer/tests/fixtures/parity/html-linked-fill-image-percentage-height-chain.json b/php-transformer/tests/fixtures/parity/html-linked-fill-image-percentage-height-chain.json
index 0ef600101..fca7b878c 100644
--- a/php-transformer/tests/fixtures/parity/html-linked-fill-image-percentage-height-chain.json
+++ b/php-transformer/tests/fixtures/parity/html-linked-fill-image-percentage-height-chain.json
@@ -65,7 +65,7 @@
{
"path": "serialized_blocks",
"assert": "contains",
- "value": " transform(
''
@@ -17,4 +18,42 @@
exit(1);
}
+$transform = static function (string $html): array {
+ $result = ( new HtmlTransformer() )->transform($html)->toArray();
+ $css = implode("\n", array_map(
+ static fn (array $asset): string => 'css' === ($asset['kind'] ?? '') ? (string) ($asset['content'] ?? '') : '',
+ $result['assets'] ?? array()
+ ));
+ return array( (string) ($result['serialized_blocks'] ?? ''), $css );
+};
+$fillClass = SourceBlockAttributeProjector::SYNTHETIC_FILL_IMAGE_FIGURE_CLASS;
+$fillRule = ':root :where(figure.' . $fillClass . '){width:100%}';
+
+// A source image explicitly fills both axes of its parent. The aspect ratio
+// belongs to the image, while the extra core/image figure needs the parent's
+// full inline extent rather than an intrinsic/shrink-to-fit width.
+list( $fillMarkup, $fillCss ) = $transform(
+ ''
+ . ' '
+);
+if ( ! preg_match('/]*\b' . preg_quote($fillClass, '/') . '\b[^>]*> ]*style="[^"]*width:100%;height:100%/', $fillMarkup)
+ || ! str_contains($fillMarkup, '"aspectRatio":"1440/562"')
+ || ! str_contains($fillCss, $fillRule)
+) {
+ fwrite(STDERR, "FAIL: source-owned fill image needs a full-width synthetic figure without altering native image height\n" . $fillMarkup . "\n" . $fillCss . "\n");
+ exit(1);
+}
+
+foreach ( array(
+ ' ',
+ ' ',
+ ' ',
+) as $source ) {
+ list( $otherMarkup, $otherCss ) = $transform($source);
+ if ( str_contains($otherMarkup, $fillClass) || str_contains($otherCss, $fillRule) ) {
+ fwrite(STDERR, "FAIL: full-width figure sizing must require a synthetic figure and source-owned two-axis fill\n" . $otherMarkup . "\n" . $otherCss . "\n");
+ exit(1);
+ }
+}
+
fwrite(STDOUT, "section background object-fit tests: passed\n");