diff --git a/CHANGELOG.md b/CHANGELOG.md index f139b41..1d682ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to `dskripchenko/php-pdf` are documented in this file. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed +- An absolutely placed block inside a table cell was drawn from where the + cell's vertical alignment put its content: a logo anchored in a + bottom-aligned cell landed at the cell's bottom, over the next row. It is + placed from the cell's own top now, as in CSS. + ## [1.24.0] — 2026-10-19 ### Added diff --git a/src/Layout/Engine.php b/src/Layout/Engine.php index 29f128c..7851bb3 100644 --- a/src/Layout/Engine.php +++ b/src/Layout/Engine.php @@ -5478,6 +5478,17 @@ private function renderCellContent( ); foreach ($cell->children as $block) { + // An absolutely placed block counts from the cell's own box, not + // from where the vertical alignment put its content: a logo + // anchored in a bottom-aligned cell was drawn at its bottom. + if ($block instanceof Positioned && $block->inFlow && ! $block->fixed) { + $savedY = $sub->cursorY; + $sub->cursorY = $cellTopY - $cs->paddingTopPt; + $this->renderBlock($block, $sub); + $sub->cursorY = $savedY; + + continue; + } $this->renderBlock($block, $sub); } diff --git a/tests/Html/HtmlCssLayoutModelTest.php b/tests/Html/HtmlCssLayoutModelTest.php index c2ed0e3..c123052 100644 --- a/tests/Html/HtmlCssLayoutModelTest.php +++ b/tests/Html/HtmlCssLayoutModelTest.php @@ -137,6 +137,18 @@ public function absolute_inside_a_positioned_box_starts_from_the_box(): void self::assertEqualsWithDelta(575.28 - 50, self::at($pdf, 'Badge')['xMin'], 1.0); } + #[Test] + public function absolute_inside_a_bottom_aligned_cell_starts_from_the_cell_top(): void + { + $pdf = self::render('' + .'' + .'
Logo
Top
'); + + // The placed block is at the cell's top, not where bottom alignment + // would put the cell's content. + self::assertEqualsWithDelta(self::at($pdf, 'Top')['yMin'], self::at($pdf, 'Logo')['yMin'], 2.0); + } + #[Test] public function a_block_kept_together_moves_to_the_next_page_whole(): void {