Skip to content

Commit

Permalink
Improve vertical alignment and related adjustments
Browse files Browse the repository at this point in the history
WIP
  • Loading branch information
bsweeney committed Jan 26, 2022
1 parent 271c651 commit d82a425
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 95 deletions.
32 changes: 32 additions & 0 deletions src/FrameDecorator/AbstractFrameDecorator.php
Expand Up @@ -339,6 +339,38 @@ public function get_border_box(): array
return $this->_frame->get_border_box();
}

/**
* Return the height of the baseline for the object
*
* @return float
*/
public function get_baseline()
{
$style = $this->_frame->get_style();
$baseline = $this->_dompdf->getCanvas()->get_font_baseline($style->font_family, $style->font_size);

$isInlineBlock = $style->display !== "inline"
&& $style->display !== "-dompdf-list-bullet";

if ($this instanceof \Dompdf\FrameDecorator\Image) {
$baseline = $style->length_in_pt([
$this->_frame->get_border_box()['h'],
$style->margin_top
]);
} else if ($isInlineBlock) {
foreach ($this->get_children() as $child) {
$child_baseline = $child->get_baseline();
if ($child_baseline > $baseline) {
$baseline = $child_baseline;
}
}
$baseline += $style->length_in_pt([$style->margin_top]);
}

return $baseline;
}


function set_id($id)
{
$this->_frame->set_id($id);
Expand Down
193 changes: 98 additions & 95 deletions src/FrameReflower/Block.php
Expand Up @@ -559,9 +559,20 @@ protected function _text_align()
function vertical_align()
{
$fontMetrics = $this->get_dompdf()->getFontMetrics();
$style = $this->_frame->get_style();
$baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size);

$line_top_adjust = 0.0;
$line_bottom_adjust = 0.0;

foreach ($this->_frame->get_line_boxes() as $line) {
$height = $line->h;
$line->y += $line_top_adjust + $line_bottom_adjust;
$line_top_adjust = 0.0;
$line_bottom_adjust = 0.0;

$line_baseline = $baseline;
$top_frames = [];
$bottom_frames = [];

// Move all markers to the top of the line box
foreach ($line->get_list_markers() as $marker) {
Expand All @@ -570,115 +581,107 @@ function vertical_align()
}

foreach ($line->frames_to_align() as $frame) {
$style = $frame->get_style();
$isInlineBlock = $style->display !== "inline"
&& $style->display !== "-dompdf-list-bullet";

$baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size);
$y_offset = 0;

//FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?)
if ($isInlineBlock) {
// Workaround: Skip vertical alignment if the frame is the
// only one one the line, excluding empty text frames, which
// may be the result of trailing white space
// FIXME: This special case should be removed once vertical
// alignment is properly fixed
$skip = true;

foreach ($line->get_frames() as $other) {
if ($other !== $frame
&& !($other->is_text_node() && $other->get_node()->nodeValue === "")
) {
$skip = false;
break;
}
}
$frame_baseline = $frame->get_baseline();
if ($frame_baseline > $line_baseline) {
$line_baseline = $frame_baseline;
}
}

if ($skip) {
continue;
}
foreach ($line->frames_to_align() as $frame) {
$frame_style = $frame->get_style();
$frame_baseline = $frame->get_baseline();
$frame_height = $frame->get_margin_height();

$parent = $frame->get_parent();
if (!$frame->is_inline_level()) {
$align = $frame_style->vertical_align;
} else if ($parent instanceof TableCellFrameDecorator) {
$align = "baseline";
} else {
$align = $parent->get_style()->vertical_align;
}

$marginHeight = $frame->get_margin_height();
$imageHeightDiff = $height * 0.8 - $marginHeight;
$y_offset = 0.0;
if (in_array($align, Style::$vertical_align_keywords, true)) {
switch ($align) {
case "middle":
// Aligns the middle of the element with the baseline plus half the x-height of the parent
$y_offset = $line_baseline - ($frame_height / 2.0) - ($fontMetrics->getFontHeight($style->font_family, $style->font_size) / 5.0);
break;

$align = $frame->get_style()->vertical_align;
if (in_array($align, Style::$vertical_align_keywords, true)) {
switch ($align) {
case "middle":
$y_offset = $imageHeightDiff / 2;
break;
case "sub":
// Aligns the baseline of the element with the subscript-baseline of its parent
$y_offset = ($line_baseline - $frame_baseline) + ($baseline * 0.3);
break;

case "sub":
$y_offset = 0.3 * $height + $imageHeightDiff;
break;
case "super":
// Aligns the baseline of the element with the superscript-baseline of its parent
$y_offset = ($line_baseline - $frame_baseline) - ($baseline * 0.5);
break;

case "super":
$y_offset = -0.2 * $height + $imageHeightDiff;
break;
case "text-top":
// Aligns the top of the element with the top of the parent element's font
$y_offset = $line_baseline - $style->line_height;
break;

case "text-top": // FIXME: this should be the height of the frame minus the height of the text
$y_offset = $height - $style->line_height;
break;
case "top":
// Aligns the top of the element and its descendants with the top of the entire line
// ... *after* other adjustments :/
$top_frames[] = $frame;
break;

case "top":
break;
case "text-bottom":
// Aligns the bottom of the element with the bottom of the parent element's font
$y_offset = $line->h - $frame_height;
break;

case "text-bottom": // FIXME: align bottom of image with the descender?
case "bottom":
$y_offset = 0.3 * $height + $imageHeightDiff;
break;
case "bottom":
// Aligns the bottom of the element and its descendants with the bottom of the entire line
// ... *after* other adjustments :/
$y_offset = $line->h - $frame_height;
$bottom_frames[] = $frame;
break;

case "baseline":
default:
$y_offset = $imageHeightDiff;
break;
}
} else {
$y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - $marginHeight;
case "baseline":
default:
// Aligns the baseline of the element with the baseline of its parent
$y_offset = $line_baseline - $frame_baseline;
break;
}
} else if (Helpers::is_percent($align)) {
// Aligns the baseline of the element to the given percentage above the baseline of its parent, with the value being a percentage of the line-height property
$y_offset = $line_baseline - $frame_baseline - (float)$style->length_in_pt($align, $style->line_height);
} else {
$parent = $frame->get_parent();
if ($parent instanceof TableCellFrameDecorator) {
$align = "baseline";
} else {
$align = $parent->get_style()->vertical_align;
}
if (in_array($align, Style::$vertical_align_keywords, true)) {
switch ($align) {
case "middle":
$y_offset = ($height * 0.8 - $baseline) / 2;
break;

case "sub":
$y_offset = $height * 0.8 - $baseline * 0.5;
break;

case "super":
$y_offset = $height * 0.8 - $baseline * 1.4;
break;

case "text-top":
case "top": // Not strictly accurate, but good enough for now
break;

case "text-bottom":
case "bottom":
$y_offset = $height * 0.8 - $baseline;
break;

case "baseline":
default:
$y_offset = $height * 0.8 - $baseline;
break;
}
} else {
$y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size);
}
// Aligns the baseline of the element to the given length above the baseline of its parent.
$y_offset = $line_baseline - $frame_baseline - (float)$frame_style->length_in_pt($align, $style->font_size);
}

if ($y_offset !== 0) {
$frame->move(0, $y_offset);
if ($frame->get_position("y") < $line->y) {
if ($line->y - $frame->get_position("y") > $line_top_adjust) {
$line_top_adjust = $line->y - $frame->get_position("y");
}
} else if ($frame->get_position("y") + $frame_height > $line->y + $line->h) {
if (abs($line->y + $line->h - $frame->get_position("y") - $frame_height) > $line_bottom_adjust) {
$line_bottom_adjust = abs($line->y + $line->h - $frame->get_position("y") - $frame_height);
}
}
}
}

// FIXME: adjust line height to encapsulate frames that are now outside the original line box, then adjust positioning of the line box and frames
$line->h += $line_top_adjust + $line_bottom_adjust;
$style->height += $line_top_adjust + $line_bottom_adjust;
if ($line_top_adjust > 0) {
foreach ($line->get_frames() as $frame) {
if (in_array($frame, $top_frames)) {
continue;
} else if (in_array($frame, $bottom_frames)) {
$frame->move(0, $line->h - $frame->get_margin_height());
} else {
$frame->move(0, $line_top_adjust);
}
}
}
}
Expand Down

0 comments on commit d82a425

Please sign in to comment.