Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions src/Layout/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
12 changes: 12 additions & 0 deletions tests/Html/HtmlCssLayoutModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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('<table><tr style="height: 80pt">'
.'<td style="position: relative; vertical-align: bottom"><div style="position: absolute; top: 0; left: 0">Logo</div></td>'
.'<td style="vertical-align: top">Top</td></tr></table>');

// 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
{
Expand Down
Loading