Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Take min/max-height into account in get_min_max_width for images #2740

Merged
merged 2 commits into from Jan 18, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
107 changes: 74 additions & 33 deletions src/FrameReflower/AbstractFrameReflower.php
Expand Up @@ -10,8 +10,9 @@
use Dompdf\Dompdf;
use Dompdf\Helpers;
use Dompdf\Frame;
use Dompdf\FrameDecorator\Block;
use Dompdf\Frame\Factory;
use Dompdf\FrameDecorator\AbstractFrameDecorator;
use Dompdf\FrameDecorator\Block;

/**
* Base reflower class
Expand All @@ -27,7 +28,7 @@ abstract class AbstractFrameReflower
/**
* Frame for this reflower
*
* @var Frame
* @var AbstractFrameDecorator
*/
protected $_frame;

Expand All @@ -47,9 +48,9 @@ abstract class AbstractFrameReflower

/**
* AbstractFrameReflower constructor.
* @param Frame $frame
* @param AbstractFrameDecorator $frame
*/
function __construct(Frame $frame)
function __construct(AbstractFrameDecorator $frame)
{
$this->_frame = $frame;
$this->_min_max_child_cache = null;
Expand Down Expand Up @@ -256,9 +257,9 @@ private function _get_collapsed_margin_length($length1, $length2)
* Handle relative positioning according to
* https://www.w3.org/TR/CSS21/visuren.html#relative-positioning.
*
* @param Frame $frame The frame to handle.
* @param AbstractFrameDecorator $frame The frame to handle.
*/
protected function position_relative(Frame $frame): void
protected function position_relative(AbstractFrameDecorator $frame): void
{
$style = $frame->get_style();

Expand Down Expand Up @@ -293,43 +294,83 @@ protected function position_relative(Frame $frame): void
abstract function reflow(Block $block = null);

/**
* Handle `min-width`/`max-width` properties while calculating the min/max
* width of this frame.
* Resolve the `min-width` property.
*
* Resolves to 0 if not set or if a percentage and the containing-block
* width is not defined.
*
* @param float $min Original min width.
* @param float $max Original max width.
* @param float|null $cbw Width of the containing block.
*
* @return array Restricted min and max width.
* @return float
*/
protected function restrict_min_max_width(float $min, float $max): array
protected function resolve_min_width(?float $cbw): float
{
// Ignore percentage values here, as the containing block is not
// defined yet
$style = $this->_frame->get_style();
$display = $style->display;

$min_width = $style->min_width;

return $min_width !== "auto" && $min_width !== "none"
? $style->length_in_pt($min_width, $cbw ?? 0)
: 0.0;
}

/**
* Resolve the `max-width` property.
*
* Resolves to `INF` if not set or if a percentage and the containing-block
* width is not defined.
*
* @param float|null $cbw Width of the containing block.
*
* @return float
*/
protected function resolve_max_width(?float $cbw): float
{
$style = $this->_frame->get_style();
$max_width = $style->max_width;
$has_min_width = $min_width !== "auto" && $min_width !== "none";
$has_max_width = $max_width !== "none" && $max_width !== "auto";

if ($has_max_width && !Helpers::is_percent($max_width)) {
$max = min($max, (float) $style->length_in_pt($max_width, 0));
$min = min($min, $max);
}
return $max_width !== "none" && $max_width !== "auto"
? $style->length_in_pt($max_width, $cbw ?? INF)
: INF;
}

if ($has_min_width && !Helpers::is_percent($min_width)) {
$min = max($min, (float) $style->length_in_pt($min_width, 0));
$max = max($max, $min);
} elseif ($has_max_width && Helpers::is_percent($max_width) && $display !== "table-cell") {
// Don't enforce any minimum width when max width depends on the
// containing block and there is no fixed min width
// Don't apply this logic to table cells, as percentage min/max
// columns widths are not supported
$min = 0.0;
}
/**
* Resolve the `min-height` property.
*
* Resolves to 0 if not set or if a percentage and the containing-block
* height is not defined.
*
* @param float|null $cbh Height of the containing block.
*
* @return float
*/
protected function resolve_min_height(?float $cbh): float
{
$style = $this->_frame->get_style();
$min_height = $style->min_height;

return $min_height !== "auto" && $min_height !== "none"
? $style->length_in_pt($min_height, $cbh ?? 0)
: 0.0;
}

/**
* Resolve the `max-height` property.
*
* Resolves to `INF` if not set or if a percentage and the containing-block
* height is not defined.
*
* @param float|null $cbh Height of the containing block.
*
* @return float
*/
protected function resolve_max_height(?float $cbh): float
{
$style = $this->_frame->get_style();
$max_height = $style->max_height;

return [$min, $max];
return $max_height !== "none" && $max_height !== "auto"
? $style->length_in_pt($style->max_height, $cbh ?? INF)
: INF;
}

/**
Expand Down
61 changes: 27 additions & 34 deletions src/FrameReflower/Block.php
Expand Up @@ -242,10 +242,10 @@ protected function _calculate_restricted_width()

// Handle min/max width
// https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
$min_width = $style->length_in_pt($style->min_width, $cb["w"]);
$max_width = $style->length_in_pt($style->max_width, $cb["w"]);
$min_width = $this->resolve_min_width($cb["w"]);
$max_width = $this->resolve_max_width($cb["w"]);

if ($max_width !== "none" && $max_width !== "auto" && $width > $max_width) {
if ($width > $max_width) {
$values = $this->_calculate_width($max_width);
$margin_left = $values["margin_left"];
$margin_right = $values["margin_right"];
Expand All @@ -254,7 +254,7 @@ protected function _calculate_restricted_width()
$right = $values["right"];
}

if ($min_width !== "auto" && $min_width !== "none" && $width < $min_width) {
if ($width < $min_width) {
$values = $this->_calculate_width($min_width);
$margin_left = $values["margin_left"];
$margin_right = $values["margin_right"];
Expand Down Expand Up @@ -394,8 +394,8 @@ protected function _calculate_restricted_height()
}
}
} else {
// http://www.w3.org/TR/CSS21/visudet.html#normal-block
// http://www.w3.org/TR/CSS21/visudet.html#block-root-margin
// https://www.w3.org/TR/CSS21/visudet.html#normal-block
// https://www.w3.org/TR/CSS21/visudet.html#block-root-margin

if ($height === "auto") {
$height = $content_height;
Expand All @@ -407,33 +407,19 @@ protected function _calculate_restricted_height()
$margin_bottom = 0;
}

// FIXME: this should probably be moved to a separate function as per
// _calculate_restricted_width

$min_height = $style->min_height;
$max_height = $style->max_height;

if (isset($cb["h"])) {
$min_height = $style->length_in_pt($min_height, $cb["h"]);
$max_height = $style->length_in_pt($max_height, $cb["h"]);
} else {
$min_height = !Helpers::is_percent($min_height)
? $style->length_in_pt($min_height, $cb["w"])
: "auto";
$max_height = !Helpers::is_percent($max_height)
? $style->length_in_pt($max_height, $cb["w"])
: "none";
}

if ($max_height !== "none" && $max_height !== "auto" && $height > $max_height) {
$height = $max_height;
}

if ($min_height !== "auto" && $min_height !== "none" && $height < $min_height) {
$height = $min_height;
}
// Handle min/max height
// https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
$min_height = $this->resolve_min_height($cb["h"]);
$max_height = $this->resolve_max_height($cb["h"]);
$height = Helpers::clamp($height, $min_height, $max_height);
}

// TODO: Need to also take min/max height into account for absolute
// positioning, using similar logic to the `_calculate_width`/
// `calculate_restricted_width` split above. The non-absolute case
// can simply clamp height within min/max, as margins and offsets are
// not affected

return [$height, $margin_top, $margin_bottom, $top, $bottom];
}

Expand Down Expand Up @@ -935,8 +921,10 @@ function reflow(BlockFrameDecorator $block = null)

public function get_min_max_content_width(): array
{
// Ignore percentage values for a specified width here, as the
// containing block is not defined yet
// TODO: While the containing block is not set yet on the frame, it can
// already be determined in some cases due to fixed dimensions on the
// ancestor forming the containing block. In such cases, percentage
// values could be resolved here
$style = $this->_frame->get_style();
$width = $style->width;
$fixed_width = $width !== "auto" && !Helpers::is_percent($width);
Expand All @@ -951,6 +939,11 @@ public function get_min_max_content_width(): array
}

// Handle min/max width style properties
return $this->restrict_min_max_width($min, $max);
$min_width = $this->resolve_min_width(null);
$max_width = $this->resolve_max_width(null);
$min = Helpers::clamp($min, $min_width, $max_width);
$max = Helpers::clamp($max, $min_width, $max_width);

return [$min, $max];
}
}