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( + '' + . '
Landscape
' +); +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( + '
Wide
', + '
Authored
', + '
Ordinary
', +) 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");