Skip to content

Commit

Permalink
Add auto-width calculation for float and inline-block elements
Browse files Browse the repository at this point in the history
Plus:

 * Fix style specificity based on the origin of the stylesheet.
 * Add :focus psudo-class to stylesheet parser.
 * Fix 'content' css property being set to 'normal' for all :before/:after
   selectors before determining their specificity.
 * Add extra check to prevent Helpers::build_url() from throwing a warning
   for URIs of 1 character (eg: '#').

Closes #1158
Addresses #627, addresses #371
Partially addresses #631 and #26
Fixes #1074, fixes #1031
Partial fix for #672

... and many more
  • Loading branch information
Andrew Scott authored and bsweeney committed Apr 4, 2016
1 parent 92da469 commit 542483a
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 7 deletions.
5 changes: 4 additions & 1 deletion lib/res/html.css
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,13 @@ input[type=reset],
input[type=file],
button {
background: #CCC;
width: 8em;
text-align: center;
}

input[type=file] {
width: 8em;
}

input[type=text]:before,
input[type=button]:before,
input[type=submit]:before,
Expand Down
26 changes: 26 additions & 0 deletions src/Css/Style.php
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,32 @@ function __get($prop)
return $this->_prop_cache[$prop] = $this->_props[$prop];
}

/**
* Similar to __get() without storing the result. Useful for accessing
* properties while loading stylesheets.
*
* @return string
*/
function get_prop($prop)
{
if (!isset(self::$_defaults[$prop])) {
throw new Exception("'$prop' is not a valid CSS2 property.");
}

$method = "get_$prop";

// Fall back on defaults if property is not set
if (!isset($this->_props[$prop])) {
return self::$_defaults[$prop];
}

if (method_exists($this, $method)) {
return $this->$method();
}

return $this->_props[$prop];
}

function get_font_family_raw()
{
return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
Expand Down
5 changes: 3 additions & 2 deletions src/Css/Stylesheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ private function _css_selector_to_xpath($selector, $first_pass = false)
case "first-letter": // TODO

// N/A
case "focus":
case "active":
case "hover":
case "visited":
Expand Down Expand Up @@ -861,7 +862,7 @@ function apply_styles(FrameTree $tree)
continue;
}

if (($src = $this->_image($style->content)) !== "none") {
if (($src = $this->_image($style->get_prop('content'))) !== "none") {
$new_node = $node->ownerDocument->createElement("img_generated");
$new_node->setAttribute("src", $src);
} else {
Expand Down Expand Up @@ -896,7 +897,7 @@ function apply_styles(FrameTree $tree)
$id = $node->getAttribute("frame_id");

// Assign the current style to the scratch array
$spec = $this->_specificity($selector);
$spec = $this->_specificity($selector, $style->get_origin());
$styles[$id][$spec][] = $style;
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/Frame.php
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,18 @@ public function is_block()
return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES);
}

/**
* @return bool
*/
public function is_inline_block()
{
if (isset($this->_is_cache["inline_block"])) {
return $this->_is_cache["inline_block"];
}

return $this->_is_cache["inline_block"] = ($this->get_style()->display === 'inline-block');
}

/**
* @return bool
*/
Expand Down
10 changes: 10 additions & 0 deletions src/FrameDecorator/AbstractFrameDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -805,4 +805,14 @@ final function get_min_max_width()
{
return $this->_reflower->get_min_max_width();
}

/**
* Determine current frame width based on contents
*
* @return float
*/
final function calculate_auto_width()
{
return $this->_reflower->calculate_auto_width();
}
}
15 changes: 13 additions & 2 deletions src/FrameReflower/AbstractFrameReflower.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ protected function _collapse_margins()
$cb = $frame->get_containing_block();
$style = $frame->get_style();

if (!$frame->is_in_flow()) {
// Margins of float/absolutely positioned/inline-block elements do not collapse.
if (!$frame->is_in_flow() || $frame->is_inline_block()) {
return;
}

Expand Down Expand Up @@ -436,7 +437,7 @@ protected function _set_content()
$frame->increment_counters($increment);
}

if ($style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated") {
if ($style->content && /*!$frame->get_first_child() && */$frame->get_node()->nodeName === "dompdf_generated") {
$content = $this->_parse_content();
// add generated content to the font subset
// FIXME: This is currently too late because the font subset has already been generated.
Expand All @@ -457,4 +458,14 @@ protected function _set_content()
$frame->append_child($new_frame);
}
}

/**
* Determine current frame width based on contents
*
* @return float
*/
public function calculate_auto_width()
{
return $this->_frame->get_margin_width();
}
}
50 changes: 49 additions & 1 deletion src/FrameReflower/Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -753,11 +753,12 @@ function reflow(BlockFrameDecorator $block = null)
$style->top = $top;
$style->bottom = $bottom;

$orig_style = $this->_frame->get_original_style();

$needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));

// Absolute positioning measurement
if ($needs_reposition) {
$orig_style = $this->_frame->get_original_style();
if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) {
$width = 0;
foreach ($this->_frame->get_line_boxes() as $line) {
Expand All @@ -770,6 +771,25 @@ function reflow(BlockFrameDecorator $block = null)
$style->right = $orig_style->right;
}

// Calculate inline-block / float auto-widths
if (($style->display === "inline-block" || $style->float !== 'none') && $orig_style->width === 'auto') {
$width = 0;

foreach ($this->_frame->get_line_boxes() as $line) {
$line->recalculate_width();

$width = max($line->w, $width);
}

if ($width === 0) {
foreach ($this->_frame->get_children() as $child) {
$width += $child->calculate_auto_width();
}
}

$style->width = $width;
}

$this->_text_align();
$this->vertical_align();

Expand All @@ -790,4 +810,32 @@ function reflow(BlockFrameDecorator $block = null)
}
}
}

/**
* Determine current frame width based on contents
*
* @return float
*/
public function calculate_auto_width()
{
$width = 0;

foreach ($this->_frame->get_line_boxes() as $line) {
$line_width = 0;

foreach ($line->get_frames() as $frame) {
if ($frame->get_original_style()->width == 'auto') {
$line_width += $frame->calculate_auto_width();
} else {
$line_width += $frame->get_margin_width();
}
}

$width = max($line_width, $width);
}

$this->_frame->get_style()->width = $width;

return $this->_frame->get_margin_width();
}
}
22 changes: 22 additions & 0 deletions src/FrameReflower/Inline.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,26 @@ function reflow(BlockFrameDecorator $block = null)
$child->reflow($block);
}
}

/**
* Determine current frame width based on contents
*
* @return float
*/
public function calculate_auto_width()
{
$width = 0;

foreach ($this->_frame->get_children() as $child) {
if ($child->get_original_style()->width == 'auto') {
$width += $child->calculate_auto_width();
} else {
$width += $child->get_margin_width();
}
}

$this->_frame->get_style()->width = $width;

return $this->_frame->get_margin_width();
}
}
10 changes: 10 additions & 0 deletions src/FrameReflower/Text.php
Original file line number Diff line number Diff line change
Expand Up @@ -484,4 +484,14 @@ public function getFontMetrics()
{
return $this->fontMetrics;
}

/**
* Determine current frame width based on contents
*
* @return float
*/
public function calculate_auto_width()
{
return $this->_frame->recalculate_width();
}
}
2 changes: 1 addition & 1 deletion src/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public static function build_url($protocol, $host, $base_path, $url)
//drive: followed by a relative path would be a drive specific default folder.
//not known in php app code, treat as abs path
//($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/'))
if ($url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':'))) {
if ($url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || (mb_strlen($url) > 1 && $url[0] !== '\\' && $url[1] !== ':'))) {
// For rel path and local acess we ignore the host, and run the path through realpath()
$ret .= realpath($base_path) . '/';
}
Expand Down
16 changes: 16 additions & 0 deletions src/LineBox.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,22 @@ function add_frame(Frame $frame)
$this->_frames[] = $frame;
}

/**
* Recalculate LineBox width based on the contained frames total width.
*
* @return float
*/
public function recalculate_width()
{
$width = 0;

foreach ($this->get_frames() as $frame) {
$width += $frame->calculate_auto_width();
}

return $this->w = $width;
}

function __toString()
{
$props = array("wc", "y", "w", "h", "left", "right", "br");
Expand Down

0 comments on commit 542483a

Please sign in to comment.