!C99Shell v. 2.5 [PHP 8 Update] [24.05.2025]!

Software: Apache. PHP/8.1.30 

uname -a: Linux server1.tuhinhossain.com 5.15.0-151-generic #161-Ubuntu SMP Tue Jul 22 14:25:40 UTC
2025 x86_64
 

uid=1002(picotech) gid=1003(picotech) groups=1003(picotech),0(root)  

Safe-mode: OFF (not secure)

/home/picotech/domains/inventory.picotech.app/public_html/vendor/dompdf/dompdf/src/Css/   drwxr-xr-x
Free 28.21 GB of 117.98 GB (23.91%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Self remove    Logout    


Viewing file:     Style.php (105.05 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/**
 * @package dompdf
 * @link    http://dompdf.github.com/
 * @author  Benj Carson <benjcarson@digitaljunkies.ca>
 * @author  Helmut Tischer <htischer@weihenstephan.org>
 * @author  Fabien Ménager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace Dompdf\Css;

use 
Dompdf\Adapter\CPDF;
use 
Dompdf\Exception;
use 
Dompdf\FontMetrics;
use 
Dompdf\Frame;

/**
 * Represents CSS properties.
 *
 * The Style class is responsible for handling and storing CSS properties.
 * It includes methods to resolve colors and lengths, as well as getters &
 * setters for many CSS properties.
 *
 * Actual CSS parsing is performed in the {@link Stylesheet} class.
 *
 * @package dompdf
 */
class Style
{
    const 
CSS_IDENTIFIER "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
    const 
CSS_INTEGER "[+-]?\d+";
    const 
CSS_NUMBER "[+-]?\d*\.?\d+";

    
/**
     * Default font size, in points.
     *
     * @var float
     */
    
static $default_font_size 12;

    
/**
     * Default line height, as a fraction of the font size.
     *
     * @var float
     */
    
static $default_line_height 1.2;

    
/**
     * Default "absolute" font sizes relative to the default font-size
     * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property
     * @var array<float>
     */
    
static $font_size_keywords = [
        
"xx-small" => 0.6// 3/5
        
"x-small" => 0.75// 3/4
        
"small" => 0.889// 8/9
        
"medium" => 1// 1
        
"large" => 1.2// 6/5
        
"x-large" => 1.5// 3/2
        
"xx-large" => 2.0// 2/1
    
];

    
/**
     * List of valid text-align keywords.  Should also really be a constant.
     *
     * @var array
     */
    
static $text_align_keywords = ["left""right""center""justify"];

    
/**
     * List of valid vertical-align keywords.  Should also really be a constant.
     *
     * @var array
     */
    
static $vertical_align_keywords = ["baseline""bottom""middle""sub",
        
"super""text-bottom""text-top""top"];

    
/**
     * List of all block-level (outer) display types.
     * * https://www.w3.org/TR/css-display-3/#display-type
     * * https://www.w3.org/TR/css-display-3/#block-level
     */
    
public const BLOCK_LEVEL_TYPES = [
        
"block",
        
// "flow-root",
        
"list-item",
        
// "flex",
        // "grid",
        
"table"
    
];

    
/**
     * List of all inline-level (outer) display types.
     * * https://www.w3.org/TR/css-display-3/#display-type
     * * https://www.w3.org/TR/css-display-3/#inline-level
     */
    
public const INLINE_LEVEL_TYPES = [
        
"inline",
        
"inline-block",
        
// "inline-flex",
        // "inline-grid",
        
"inline-table"
    
];

    
/**
     * List of all table-internal (outer) display types.
     * * https://www.w3.org/TR/css-display-3/#layout-specific-display
     */
    
public const TABLE_INTERNAL_TYPES = [
        
"table-row-group",
        
"table-header-group",
        
"table-footer-group",
        
"table-row",
        
"table-cell",
        
"table-column-group",
        
"table-column",
        
"table-caption"
    
];

    
/**
     * List of all inline (inner) display types.  Should really be a constant.
     *
     * @var array
     */
    
static $INLINE_TYPES = ["inline"];

    
/**
     * List of all block (inner) display types.  Should really be a constant.
     *
     * @var array
     */
    
static $BLOCK_TYPES = ["block""inline-block""table-cell""list-item"];

    
/**
     * List of all table (inner) display types.  Should really be a constant.
     *
     * @var array
     */
    
static $TABLE_TYPES = ["table""inline-table"];

    
/**
     * Lookup table for valid display types. Initially computed from the
     * different constants.
     *
     * @var array
     */
    
protected static $valid_display_types = [];

    
/**
     * List of all positioned types.  Should really be a constant.
     *
     * @var array
     */
    
static $POSITIONNED_TYPES = ["relative""absolute""fixed"];

    
/**
     * List of valid border styles.  Should also really be a constant.
     *
     * @var array
     */
    
static $BORDER_STYLES = ["none""hidden""dotted""dashed""solid",
        
"double""groove""ridge""inset""outset"];

    
/**
     * Map of CSS shorthand properties and their corresponding sub-properties.
     * The order of the sub-properties is relevant for the fallback getter,
     * which is used in case no specific getter method is defined.
     *
     * @var array
     */
    
protected static $_props_shorthand = [
        
"background" => [
            
"background_image",
            
"background_position",
            
"background_size",
            
"background_repeat",
            
// "background_origin",
            // "background_clip",
            
"background_attachment",
            
"background_color"
        
],
        
"border" => [
            
"border_width",
            
"border_style",
            
"border_color"
        
],
        
"border_top" => [
            
"border_top_width",
            
"border_top_style",
            
"border_top_color"
        
],
        
"border_right" => [
            
"border_right_width",
            
"border_right_style",
            
"border_right_color"
        
],
        
"border_bottom" => [
            
"border_bottom_width",
            
"border_bottom_style",
            
"border_bottom_color"
        
],
        
"border_left" => [
            
"border_left_width",
            
"border_left_style",
            
"border_left_color"
        
],
        
"border_width" => [
            
"border_top_width",
            
"border_right_width",
            
"border_bottom_width",
            
"border_left_width"
        
],
        
"border_style" => [
            
"border_top_style",
            
"border_right_style",
            
"border_bottom_style",
            
"border_left_style"
        
],
        
"border_color" => [
            
"border_top_color",
            
"border_right_color",
            
"border_bottom_color",
            
"border_left_color"
        
],
        
"border_radius" => [
            
"border_top_left_radius",
            
"border_top_right_radius",
            
"border_bottom_right_radius",
            
"border_bottom_left_radius"
        
],
        
"font" => [
            
"font_family",
            
"font_size",
            
// "font_stretch",
            
"font_style",
            
"font_variant",
            
"font_weight",
            
"line_height"
        
],
        
"list_style" => [
            
"list_style_image",
            
"list_style_position",
            
"list_style_type"
        
],
        
"margin" => [
            
"margin_top",
            
"margin_right",
            
"margin_bottom",
            
"margin_left"
        
],
        
"padding" => [
            
"padding_top",
            
"padding_right",
            
"padding_bottom",
            
"padding_left"
        
],
        
"outline" => [
            
"outline_width",
            
"outline_style",
            
"outline_color"
        
]
    ];

    
/**
     * Maps legacy property names to actual property names.
     *
     * @var array
     */
    
protected static $_props_alias = [
        
"word_wrap"                           => "overflow_wrap",
        
"_dompdf_background_image_resolution" => "background_image_resolution",
        
"_dompdf_image_resolution"            => "image_resolution",
        
"_webkit_transform"                   => "transform",
        
"_webkit_transform_origin"            => "transform_origin"
    
];

    
/**
     * Default style values.
     *
     * @link http://www.w3.org/TR/CSS21/propidx.html
     *
     * @var array
     */
    
protected static $_defaults null;

    
/**
     * List of inherited properties
     *
     * @link http://www.w3.org/TR/CSS21/propidx.html
     *
     * @var array
     */
    
protected static $_inherited null;

    
/**
     * Caches method_exists result
     *
     * @var array<bool>
     */
    
protected static $_methods_cache = [];

    
/**
     * The stylesheet this style belongs to
     *
     * @var Stylesheet
     */
    
protected $_stylesheet;

    
/**
     * Media queries attached to the style
     *
     * @var array
     */
    
protected $_media_queries;

    
/**
     * Specified (or declared) values of the CSS properties.
     * https://www.w3.org/TR/css-cascade-3/#value-stages
     *
     * @var array
     */
    
protected $_props = [];

    
/**
     * Properties set by an `!important` declaration.
     *
     * @var array
     */
    
protected $_important_props = [];

    
/**
     * Computed values of the CSS properties.
     *
     * @var array
     */
    
protected $_props_computed = [];

    
/**
     * Used values of the CSS properties.
     *
     * @var array
     */
    
protected $_prop_cache = [];

    protected static 
$_dependency_map = [
        
"border_top_style" => [
            
"border_top_width"
        
],
        
"border_bottom_style" => [
            
"border_bottom_width"
        
],
        
"border_left_style" => [
            
"border_left_width"
        
],
        
"border_right_style" => [
            
"border_right_width"
        
],
        
"direction" => [
            
"text_align"
        
],
        
"font_size" => [
            
"background_position",
            
"background_size",
            
"border_top_width",
            
"border_right_width",
            
"border_bottom_width",
            
"border_left_width",
            
"line_height",
            
"margin_top",
            
"margin_right",
            
"margin_bottom",
            
"margin_left",
            
"outline_width",
            
"outline_offset",
            
"padding_top",
            
"padding_right",
            
"padding_bottom",
            
"padding_left"
        
],
        
"float" => [
            
"display"
        
],
        
"position" => [
            
"display"
        
],
        
"outline_style" => [
            
"outline_width"
        
]
    ];

    
/**
     * Lookup table for dependent properties. Initially computed from the
     * dependency map.
     *
     * @var array
     */
    
protected static $_dependent_props = [];

    
/**
     * Style of the parent element in document tree.
     *
     * @var Style
     */
    
protected $parent_style;

    
/**
     * @var Frame
     */
    
protected $_frame;

    
/**
     * The origin of the style
     *
     * @var int
     */
    
protected $_origin Stylesheet::ORIG_AUTHOR;

    
// private members
    /**
     * The computed bottom spacing
     */
    
private $_computed_bottom_spacing null;

    
/**
     * @var bool
     */
    
private $has_border_radius_cache null;

    
/**
     * @var array
     */
    
private $resolved_border_radius null;

    
/**
     * @var FontMetrics
     */
    
private $fontMetrics;

    
/**
     * Class constructor
     *
     * @param Stylesheet $stylesheet the stylesheet this Style is associated with.
     * @param int $origin
     */
    
public function __construct(Stylesheet $stylesheet$origin Stylesheet::ORIG_AUTHOR)
    {
        
$this->setFontMetrics($stylesheet->getFontMetrics());

        
$this->_stylesheet $stylesheet;
        
$this->_media_queries = [];
        
$this->_origin $origin;
        
$this->parent_style null;

        if (!isset(
self::$_defaults)) {

            
// Shorthand
            
$d =& self::$_defaults;

            
// All CSS 2.1 properties, and their default values
            
$d["azimuth"] = "center";
            
$d["background_attachment"] = "scroll";
            
$d["background_color"] = "transparent";
            
$d["background_image"] = "none";
            
$d["background_image_resolution"] = "normal";
            
$d["background_position"] = "0% 0%";
            
$d["background_repeat"] = "repeat";
            
$d["background"] = "";
            
$d["border_collapse"] = "separate";
            
$d["border_color"] = "";
            
$d["border_spacing"] = "0";
            
$d["border_style"] = "";
            
$d["border_top"] = "";
            
$d["border_right"] = "";
            
$d["border_bottom"] = "";
            
$d["border_left"] = "";
            
$d["border_top_color"] = "currentcolor";
            
$d["border_right_color"] = "currentcolor";
            
$d["border_bottom_color"] = "currentcolor";
            
$d["border_left_color"] = "currentcolor";
            
$d["border_top_style"] = "none";
            
$d["border_right_style"] = "none";
            
$d["border_bottom_style"] = "none";
            
$d["border_left_style"] = "none";
            
$d["border_top_width"] = "medium";
            
$d["border_right_width"] = "medium";
            
$d["border_bottom_width"] = "medium";
            
$d["border_left_width"] = "medium";
            
$d["border_width"] = "";
            
$d["border_bottom_left_radius"] = "0";
            
$d["border_bottom_right_radius"] = "0";
            
$d["border_top_left_radius"] = "0";
            
$d["border_top_right_radius"] = "0";
            
$d["border_radius"] = "";
            
$d["border"] = "";
            
$d["bottom"] = "auto";
            
$d["caption_side"] = "top";
            
$d["clear"] = "none";
            
$d["clip"] = "auto";
            
$d["color"] = "#000000";
            
$d["content"] = "normal";
            
$d["counter_increment"] = "none";
            
$d["counter_reset"] = "none";
            
$d["cue_after"] = "none";
            
$d["cue_before"] = "none";
            
$d["cue"] = "";
            
$d["cursor"] = "auto";
            
$d["direction"] = "ltr";
            
$d["display"] = "inline";
            
$d["elevation"] = "level";
            
$d["empty_cells"] = "show";
            
$d["float"] = "none";
            
$d["font_family"] = $stylesheet->get_dompdf()->getOptions()->getDefaultFont();
            
$d["font_size"] = "medium";
            
$d["font_style"] = "normal";
            
$d["font_variant"] = "normal";
            
$d["font_weight"] = "normal";
            
$d["font"] = "";
            
$d["height"] = "auto";
            
$d["image_resolution"] = "normal";
            
$d["left"] = "auto";
            
$d["letter_spacing"] = "normal";
            
$d["line_height"] = "normal";
            
$d["list_style_image"] = "none";
            
$d["list_style_position"] = "outside";
            
$d["list_style_type"] = "disc";
            
$d["list_style"] = "";
            
$d["margin_right"] = "0";
            
$d["margin_left"] = "0";
            
$d["margin_top"] = "0";
            
$d["margin_bottom"] = "0";
            
$d["margin"] = "";
            
$d["max_height"] = "none";
            
$d["max_width"] = "none";
            
$d["min_height"] = "auto";
            
$d["min_width"] = "auto";
            
$d["orphans"] = "2";
            
$d["outline_color"] = "currentcolor"// "invert" special color is not supported
            
$d["outline_style"] = "none";
            
$d["outline_width"] = "medium";
            
$d["outline_offset"] = "0";
            
$d["outline"] = "";
            
$d["overflow"] = "visible";
            
$d["overflow_wrap"] = "normal";
            
$d["padding_top"] = "0";
            
$d["padding_right"] = "0";
            
$d["padding_bottom"] = "0";
            
$d["padding_left"] = "0";
            
$d["padding"] = "";
            
$d["page_break_after"] = "auto";
            
$d["page_break_before"] = "auto";
            
$d["page_break_inside"] = "auto";
            
$d["pause_after"] = "0";
            
$d["pause_before"] = "0";
            
$d["pause"] = "";
            
$d["pitch_range"] = "50";
            
$d["pitch"] = "medium";
            
$d["play_during"] = "auto";
            
$d["position"] = "static";
            
$d["quotes"] = "auto";
            
$d["richness"] = "50";
            
$d["right"] = "auto";
            
$d["size"] = "auto"// @page
            
$d["speak_header"] = "once";
            
$d["speak_numeral"] = "continuous";
            
$d["speak_punctuation"] = "none";
            
$d["speak"] = "normal";
            
$d["speech_rate"] = "medium";
            
$d["stress"] = "50";
            
$d["table_layout"] = "auto";
            
$d["text_align"] = "";
            
$d["text_decoration"] = "none";
            
$d["text_indent"] = "0";
            
$d["text_transform"] = "none";
            
$d["top"] = "auto";
            
$d["unicode_bidi"] = "normal";
            
$d["vertical_align"] = "baseline";
            
$d["visibility"] = "visible";
            
$d["voice_family"] = "";
            
$d["volume"] = "medium";
            
$d["white_space"] = "normal";
            
$d["widows"] = "2";
            
$d["width"] = "auto";
            
$d["word_spacing"] = "normal";
            
$d["z_index"] = "auto";

            
// CSS3
            
$d["opacity"] = "1.0";
            
$d["background_size"] = "auto auto";
            
$d["transform"] = "none";
            
$d["transform_origin"] = "50% 50%";

            
// for @font-face
            
$d["src"] = "";
            
$d["unicode_range"] = "";

            
// vendor-prefixed properties
            
$d["_dompdf_keep"] = "";

            
// Properties that inherit by default
            
self::$_inherited = [
                
"azimuth",
                
"background_image_resolution",
                
"border_collapse",
                
"border_spacing",
                
"caption_side",
                
"color",
                
"cursor",
                
"direction",
                
"elevation",
                
"empty_cells",
                
"font_family",
                
"font_size",
                
"font_style",
                
"font_variant",
                
"font_weight",
                
"font",
                
"image_resolution",
                
"letter_spacing",
                
"line_height",
                
"list_style_image",
                
"list_style_position",
                
"list_style_type",
                
"list_style",
                
"orphans",
                
"overflow_wrap",
                
"pitch_range",
                
"pitch",
                
"quotes",
                
"richness",
                
"speak_header",
                
"speak_numeral",
                
"speak_punctuation",
                
"speak",
                
"speech_rate",
                
"stress",
                
"text_align",
                
"text_indent",
                
"text_transform",
                
"visibility",
                
"voice_family",
                
"volume",
                
"white_space",
                
"widows",
                
"word_spacing",
            ];

            
// Compute dependent props from dependency map
            
foreach (self::$_dependency_map as $props) {
                foreach (
$props as $prop) {
                    
self::$_dependent_props[$prop] = true;
                }
            }

            
// Compute valid display-type lookup table
            
self::$valid_display_types = [
                
"none"                => true,
                
"-dompdf-br"          => true,
                
"-dompdf-image"       => true,
                
"-dompdf-list-bullet" => true,
                
"-dompdf-page"        => true
            
];
            foreach (
self::BLOCK_LEVEL_TYPES as $val) {
                
self::$valid_display_types[$val] = true;
            }
            foreach (
self::INLINE_LEVEL_TYPES as $val) {
                
self::$valid_display_types[$val] = true;
            }
            foreach (
self::TABLE_INTERNAL_TYPES as $val) {
                
self::$valid_display_types[$val] = true;
            }
        }
    }

    
/**
     * "Destructor": forcibly free all references held by this object
     */
    
function dispose()
    {
    }

    
/**
     * @param $media_queries
     */
    
function set_media_queries($media_queries)
    {
        
$this->_media_queries $media_queries;
    }

    
/**
     * @return array|int
     */
    
function get_media_queries()
    {
        return 
$this->_media_queries;
    }

    
/**
     * @param Frame $frame
     */
    
function set_frame(Frame $frame)
    {
        
$this->_frame $frame;
    }

    
/**
     * @return Frame
     */
    
function get_frame()
    {
        return 
$this->_frame;
    }

    
/**
     * @param $origin
     */
    
function set_origin($origin)
    {
        
$this->_origin $origin;
    }

    
/**
     * @return int
     */
    
function get_origin()
    {
        return 
$this->_origin;
    }

    
/**
     * returns the {@link Stylesheet} this Style is associated with.
     *
     * @return Stylesheet
     */
    
function get_stylesheet()
    {
        return 
$this->_stylesheet;
    }

    public function 
is_absolute(): bool
    
{
        
$position $this->__get("position");
        return 
$position === "absolute" || $position === "fixed";
    }

    public function 
is_in_flow(): bool
    
{
        
$float $this->__get("float");
        return 
$float === "none" && !$this->is_absolute();
    }

    
/**
     * Converts any CSS length value into an absolute length in points.
     *
     * length_in_pt() takes a single length (e.g. '1em') or an array of
     * lengths and returns an absolute length.  If an array is passed, then
     * the return value is the sum of all elements. If any of the lengths
     * provided are "auto" or "none" then that value is returned.
     *
     * If a reference size is not provided, the current font size is used.
     *
     * @param float|string|array $length   The numeric length (or string measurement) or array of lengths to resolve.
     * @param float|null         $ref_size An absolute reference size to resolve percentage lengths.
     *
     * @return float|string
     */
    
function length_in_pt($length, ?float $ref_size null)
    {
        
$font_size $this->__get("font_size");
        
$ref_size $ref_size ?? $font_size;

        if (!
is_array($length)) {
            
$length = [$length];
        }

        
$ret 0.0;

        foreach (
$length as $l) {
            if (
$l === "auto" || $l === "none") {
                return 
$l;
            }

            
// Assume numeric values are already in points
            
if (is_numeric($l)) {
                
$ret += (float) $l;
                continue;
            }

            
$val $this->single_length_in_pt((string) $l$ref_size$font_size);

            
// FIXME: Using the ref size as fallback here currently ensures that
            // invalid widths or heights are treated as the corresponding
            // containing-block dimension, which can look like the declaration
            // is being ignored. Implement proper compute methods instead, and
            // fall back to 0 here
            
$ret += $val ?? $ref_size;
        }

        return 
$ret;
    }

    
/**
     * Convert a length declaration to pt.
     *
     * @param string     $l         The length declaration.
     * @param float      $ref_size  Reference size for percentage declarations.
     * @param float|null $font_size Font size for resolving font-size relative units.
     *
     * @return float|null The length in pt, or `null` for invalid declarations.
     */
    
protected function single_length_in_pt(string $lfloat $ref_size 0, ?float $font_size null): ?float
    
{
        static 
$cache = [];

        
$font_size $font_size ?? $this->__get("font_size");

        
$key "$l/$ref_size/$font_size";

        if (isset(
$cache[$key])) {
            return 
$cache[$key];
        }

        if (
is_numeric($l)) {
            
// Legacy support for unitless values, not covered by spec. Might
            // want to restrict this to unitless `0` in the future
            
$value = (float) $l;
        }

        elseif ((
$i mb_stripos($l"%")) !== false) {
            
$value = (float)mb_substr($l0$i) / 100 $ref_size;
        }

        elseif ((
$i mb_stripos($l"px")) !== false) {
            
$dpi $this->_stylesheet->get_dompdf()->getOptions()->getDpi();
            
$value = ((float)mb_substr($l0$i) * 72) / $dpi;
        }

        elseif ((
$i mb_stripos($l"pt")) !== false) {
            
$value = (float)mb_substr($l0$i);
        }

        elseif ((
$i mb_stripos($l"rem")) !== false) {
            
$root_style $this->_stylesheet->get_dompdf()->getTree()->get_root()->get_style();
            
$root_font_size $root_style === null || $root_style === $this
                
$font_size
                
$root_style->font_size;
            
$value = (float)mb_substr($l0$i) * $root_font_size;
        }

        elseif ((
$i mb_stripos($l"em")) !== false) {
            
$value = (float)mb_substr($l0$i) * $font_size;
        }

        elseif ((
$i mb_stripos($l"cm")) !== false) {
            
$value = (float)mb_substr($l0$i) * 72 2.54;
        }

        elseif ((
$i mb_stripos($l"mm")) !== false) {
            
$value = (float)mb_substr($l0$i) * 72 25.4;
        }

        elseif ((
$i mb_stripos($l"ex")) !== false) {
            
// FIXME: em:ex ratio?
            
$value = (float)mb_substr($l0$i) * $font_size 2;
        }

        elseif ((
$i mb_stripos($l"in")) !== false) {
            
$value = (float)mb_substr($l0$i) * 72;
        }

        elseif ((
$i mb_stripos($l"pc")) !== false) {
            
$value = (float)mb_substr($l0$i) * 12;
        }

        else {
            
// Invalid or unsupported declaration
            
$value null;
        }

        return 
$cache[$key] = $value;
    }


    
/**
     * Resolve inherited property values using the provided parent style or the
     * default values, in case no parent style exists.
     *
     * https://www.w3.org/TR/css-cascade-3/#inheriting
     *
     * @param Style|null $parent
     *
     * @return Style
     */
    
function inherit(?Style $parent null)
    {
        
$this->parent_style $parent;

        
// Clear the computed font size, as it might depend on the parent
        // font size
        
unset($this->_props_computed["font_size"]);
        unset(
$this->_prop_cache["font_size"]);

        if (
$parent) {
            foreach (
self::$_inherited as $prop) {
                
// For properties that inherit by default: When the cascade did
                // not result in a value, inherit the parent value. Inheritance
                // is handled via the specific sub-properties for shorthands
                
if (isset($this->_props[$prop]) || isset(self::$_props_shorthand[$prop])) {
                    continue;
                }
    
                if (isset(
$parent->_props[$prop])) {
                    
$parent_val \array_key_exists($prop$parent->_props_computed)
                        ? 
$parent->_props_computed[$prop]
                        : 
$parent->compute_prop($prop$parent->_props[$prop]);
    
                    
$this->_props[$prop] = $parent_val;
                    
$this->_props_computed[$prop] = $parent_val;
                    
$this->_prop_cache[$prop] = null;
                }
            }
        }

        foreach (
$this->_props as $prop => $val) {
            if (
$val === "inherit") {
                if (
$parent && isset($parent->_props[$prop])) {
                    
$parent_val \array_key_exists($prop$parent->_props_computed)
                        ? 
$parent->_props_computed[$prop]
                        : 
$parent->compute_prop($prop$parent->_props[$prop]);

                    
$this->_props[$prop] = $parent_val;
                    
$this->_props_computed[$prop] = $parent_val;
                    
$this->_prop_cache[$prop] = null;
                } else {
                    
// Parent prop not set, use default
                    
$this->_props[$prop] = self::$_defaults[$prop];
                    unset(
$this->_props_computed[$prop]);
                    unset(
$this->_prop_cache[$prop]);
                }
            }
        }

        return 
$this;
    }

    
/**
     * Override properties in this style with those in $style
     *
     * @param Style $style
     */
    
function merge(Style $style)
    {
        foreach (
$style->_props as $prop => $val) {
            
$important = isset($style->_important_props[$prop]);

            
// `!important` declarations take precedence over normal ones
            
if (!$important && isset($this->_important_props[$prop])) {
                continue;
            }

            
$computed \array_key_exists($prop$style->_props_computed)
                ? 
$style->_props_computed[$prop]
                : 
$style->compute_prop($prop$val);

            
// Skip invalid declarations. Because styles are merged into an
            // initially empty style object during stylesheet loading, this
            // handles all invalid declarations
            
if ($computed === null) {
                continue;
            }

            if (
$important) {
                
$this->_important_props[$prop] = true;
            }

            
$this->_props[$prop] = $val;

            
// Don't use the computed value for dependent properties; they will
            // be computed on-demand during inheritance or property access
            // instead
            
if (isset(self::$_dependent_props[$prop])) {
                unset(
$this->_props_computed[$prop]);
                unset(
$this->_prop_cache[$prop]);
            } else {
                
$this->_props_computed[$prop] = $computed;
                
$this->_prop_cache[$prop] = null;
            }
        }
    }

    
/**
     * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "alpha"=>alpha, "hex"=>"#rrggbb")
     * based on the provided CSS color value.
     *
     * @param string $color
     * @return array|string|null
     */
    
function munge_color($color)
    {
        return 
Color::parse($color);
    }

    
/**
     * @deprecated
     * @param string $prop
     */
    
function important_set($prop)
    {
        
$prop str_replace("-""_"$prop);
        
$this->_important_props[$prop] = true;
    }

    
/**
     * @deprecated
     * @param string $prop
     * @return bool
     */
    
function important_get($prop)
    {
        return isset(
$this->_important_props[$prop]);
    }

    
/**
     * Clear information about important declarations after the style has been
     * finalized during stylesheet loading.
     */
    
public function clear_important(): void
    
{
        
$this->_important_props = [];
    }

    
/**
     * Set the specified value of a property.
     *
     * Setting `$clear_dependencies` to `false` is useful for saving a bit of
     * unnecessary work while loading stylesheets.
     *
     * @param string $prop               The property to set.
     * @param mixed  $val                The value declaration.
     * @param bool   $important          Whether the declaration is important.
     * @param bool   $clear_dependencies Whether to clear computed values of dependent properties.
     */
    
function set_prop(string $prop$valbool $important falsebool $clear_dependencies true): void
    
{
        
$prop str_replace("-""_"$prop);

        
// Legacy property aliases
        
if (isset(self::$_props_alias[$prop])) {
            
$prop self::$_props_alias[$prop];
        }

        if (!isset(
self::$_defaults[$prop])) {
            global 
$_dompdf_warnings;
            
$_dompdf_warnings[] = "'$prop' is not a recognized CSS property.";
            return;
        }

        if (
$prop !== "content" && is_string($val) && mb_strpos($val"url") === false && strlen($val) > 1) {
            
$val mb_strtolower(trim(str_replace(["\n""\t"], [" "], $val)));
            
$val preg_replace("/([0-9]+) (pt|px|pc|rem|em|ex|in|cm|mm|%)/S""\\1\\2"$val);
        }

        if (isset(
self::$_props_shorthand[$prop])) {
            
// Shorthand properties directly set their respective sub-properties
            // https://www.w3.org/TR/css-cascade-3/#shorthand
            
if ($val === "initial" || $val === "inherit" || $val === "unset") {
                foreach (
self::$_props_shorthand[$prop] as $sub_prop) {
                    
$this->set_prop($sub_prop$val$important$clear_dependencies);
                }
            } else {
                
$method "set_$prop";
    
                if (!isset(
self::$_methods_cache[$method])) {
                    
self::$_methods_cache[$method] = method_exists($this$method);
                }
    
                if (
self::$_methods_cache[$method]) {
                    
$this->$method($val$important);
                }
            }
        } else {
            
// `!important` declarations take precedence over normal ones
            
if (!$important && isset($this->_important_props[$prop])) {
                return;
            }

            if (
$important) {
                
$this->_important_props[$prop] = true;
            }

            
// https://www.w3.org/TR/css-cascade-3/#inherit-initial
            
if ($val === "unset") {
                
$val in_array($propself::$_inheritedtrue)
                    ? 
"inherit"
                    
"initial";
            }

            
// https://www.w3.org/TR/css-cascade-3/#valdef-all-initial
            
if ($val === "initial") {
                
$val self::$_defaults[$prop];
            }

            
$this->_props[$prop] = $val;
            unset(
$this->_props_computed[$prop]);
            unset(
$this->_prop_cache[$prop]);

            if (
$clear_dependencies) {
                
// Clear the computed values of any dependent properties, so
                // they can be re-computed
                
if (isset(self::$_dependency_map[$prop])) {
                    foreach (
self::$_dependency_map[$prop] as $dependent) {
                        unset(
$this->_props_computed[$dependent]);
                        unset(
$this->_prop_cache[$dependent]);
                    }
                }

                
// Clear border-radius cache on setting any border-radius
                // property
                
if ($prop === "border_top_left_radius"
                    
|| $prop === "border_top_right_radius"
                    
|| $prop === "border_bottom_left_radius"
                    
|| $prop === "border_bottom_right_radius"
                
) {
                    
$this->has_border_radius_cache null;
                }
            }

            
// FIXME: temporary hack around lack of persistence of base href for
            // URLs. Compute value immediately, before the original base href is
            // no longer available
            
if ($prop === "background_image" || $prop === "list_style_image") {
                
$this->compute_prop($prop$val);
            }
        }
    }

    
/**
     * Similar to __get() without storing the result. Useful for accessing
     * properties while loading stylesheets.
     *
     * @param string $prop
     *
     * @return mixed
     * @throws Exception
     */
    
function get_prop(string $prop)
    {
        
// Legacy property aliases
        
if (isset(self::$_props_alias[$prop])) {
            
$prop self::$_props_alias[$prop];
        }

        if (!isset(
self::$_defaults[$prop])) {
            throw new 
Exception("'$prop' is not a recognized CSS property.");
        }

        
$method "get_$prop";

        if (isset(
$this->_props_computed[$prop])) {
            if (
method_exists($this$method)) {
                return 
$this->$method();
            }

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

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

    
/**
     * PHP5 overloaded setter
     *
     * This function along with {@link Style::__get()} permit a user of the
     * Style class to access any (CSS) property using the following syntax:
     * <code>
     *  Style->margin_top = "1em";
     *  echo (Style->margin_top);
     * </code>
     *
     * __set() automatically calls the provided set function, if one exists,
     * otherwise it sets the property directly.  Typically, __set() is not
     * called directly from outside of this class.
     *
     * On each modification clear cache to return accurate setting.
     * Also affects direct settings not using __set
     * For easier finding all assignments, attempted to allowing only explicite assignment:
     * Very many uses, e.g. AbstractFrameReflower.php -> for now leave as it is
     * function __set($prop, $val) {
     *   throw new Exception("Implicit replacement of assignment by __set.  Not good.");
     * }
     * function props_set($prop, $val) { ... }
     *
     * @param string $prop the property to set
     * @param mixed $val the value of the property
     *
     */
    
function __set($prop$val)
    {
        
$this->set_prop($prop$val);
    }

    
/**
     * PHP5 overloaded getter
     * Along with {@link Style::__set()} __get() provides access to all CSS
     * properties directly.  Typically __get() is not called directly outside
     * of this class.
     * On each modification clear cache to return accurate setting.
     * Also affects direct settings not using __set
     *
     * @param string $prop
     *
     * @return mixed
     * @throws Exception
     */
    
function __get($prop)
    {
        
// Legacy property aliases
        
if (isset(self::$_props_alias[$prop])) {
            
$prop self::$_props_alias[$prop];
        }

        if (!isset(
self::$_defaults[$prop])) {
            throw new 
Exception("'$prop' is not a recognized CSS property.");
        }

        if (isset(
$this->_prop_cache[$prop])) {
            return 
$this->_prop_cache[$prop];
        }

        
$method "get_$prop";
    
        if (!isset(
self::$_methods_cache[$method])) {
            
self::$_methods_cache[$method] = method_exists($this$method);
        }

        if (isset(
self::$_props_shorthand[$prop])) {
            
// Don't cache shorthand values, always use getter. If no dedicated
            // getter exists, use a simple fallback getter concatenating all
            // sub-property values
            
if (self::$_methods_cache[$method]) {
                return 
$this->$method();
            } else {
                return 
implode(" "array_map(function ($sub_prop) {
                    
$val $this->__get($sub_prop);
                    return 
is_array($val) ? implode(" "$val) : $val;
                }, 
self::$_props_shorthand[$prop]));
            }
        } else {
            
// Compute the value if needed
            
if (!\array_key_exists($prop$this->_props_computed)) {
                
$val $this->_props[$prop] ?? self::$_defaults[$prop];
                
$this->compute_prop($prop$val);
            }

            
// Invalid declarations are skipped on style merge, but during
            // style parsing, styles might contain invalid declarations. Fall
            // back to the default value in that case
            
$computed $this->_props_computed[$prop]
                ?? 
$this->compute_prop($propself::$_defaults[$prop]);
            
$used self::$_methods_cache[$method]
                ? 
$this->$method()
                : 
$computed;

            
$this->_prop_cache[$prop] = $used;
            return 
$used;
        }
    }

    
/**
     * Experimental fast setter for used values.
     *
     * If a shorthand property is specified, all of its sub-properties are set
     * to the same value.
     *
     * @param string $prop
     * @param mixed  $val
     */
    
function set_used(string $prop$val): void
    
{
        
// Legacy property aliases
        
if (isset(self::$_props_alias[$prop])) {
            
$prop self::$_props_alias[$prop];
        }

        if (!isset(
self::$_defaults[$prop])) {
            throw new 
Exception("'$prop' is not a recognized CSS property.");
        }

        if (isset(
self::$_props_shorthand[$prop])) {
            foreach (
self::$_props_shorthand[$prop] as $sub_prop) {
                
$this->set_used($sub_prop$val);
            }
        } else {
            
$this->_prop_cache[$prop] = $val;
        }
    }

    
/**
     * @param string $prop The property to compute.
     * @param mixed  $val  The value to compute.
     *
     * @return mixed The computed value.
     */
    
protected function compute_prop(string $prop$val)
    {
        
$this->_props_computed[$prop] = null;
        
$this->_prop_cache[$prop] = null;

        
$method "set_$prop";

        if (!isset(
self::$_methods_cache[$method])) {
            
self::$_methods_cache[$method] = method_exists($this$method);
        }

        
// During style merge, the parent style is not available yet, so
        // temporarily use the initial value for `inherit` properties. The
        // keyword is properly resolved during inheritance
        
if ($val === "inherit") {
            
$val self::$_defaults[$prop];
        }

        if (
self::$_methods_cache[$method]) {
            
$this->$method($val);
        } elseif (
$val !== "") {
            
$this->_props_computed[$prop] = $val;
        }

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

    
/**
     * @param float $cbw The width of the containing block.
     * @return float|null|string
     */
    
function computed_bottom_spacing(float $cbw)
    {
        
// Caching the bottom spacing independently of the given width is a bit
        // iffy, but should be okay, as the containing block should only
        // potentially change after a page break, and the style is reset in that
        // case
        
if ($this->_computed_bottom_spacing !== null) {
            return 
$this->_computed_bottom_spacing;
        }
        return 
$this->_computed_bottom_spacing $this->length_in_pt(
            [
                
$this->margin_bottom,
                
$this->padding_bottom,
                
$this->border_bottom_width
            
],
            
$cbw
        
);
    }

    
/**
     * @return string
     */
    
function get_font_family_raw()
    {
        return 
trim($this->_props["font_family"], " \t\n\r\x0B\"'");
    }

    
/**
     * Getter for the 'font-family' CSS property.
     * Uses the {@link FontMetrics} class to resolve the font family into an
     * actual font file.
     *
     * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
     * @throws Exception
     *
     * @return string
     */
    
function get_font_family()
    {
        
//TODO: we should be using the calculated prop rather than perform the entire family parsing operation again

        
$DEBUGCSS $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();

        
// Select the appropriate font.  First determine the subtype, then check
        // the specified font-families for a candidate.

        // Resolve font-weight
        
$weight $this->__get("font_weight");
        if (
$weight === 'bold') {
            
$weight 700;
        } elseif (
preg_match('/^[0-9]+$/'$weight$match)) {
            
$weight = (int)$match[0];
        } else {
            
$weight 400;
        }

        
// Resolve font-style
        
$font_style $this->__get("font_style");
        
$subtype $this->getFontMetrics()->getType($weight ' ' $font_style);

        
$families preg_split("/\s*,\s*/"$this->_props_computed["font_family"]);

        
$font null;
        foreach (
$families as $family) {
            
//remove leading and trailing string delimiters, e.g. on font names with spaces;
            //remove leading and trailing whitespace
            
$family trim($family" \t\n\r\x0B\"'");
            if (
$DEBUGCSS) {
                print 
'(' $family ')';
            }
            
$font $this->getFontMetrics()->getFont($family$subtype);

            if (
$font) {
                if (
$DEBUGCSS) {
                    print 
"<pre>[get_font_family:";
                    print 
'(' $this->_props_computed["font_family"] . '.' $font_style '.' $weight '.' $subtype ')';
                    print 
'(' $font ")get_font_family]\n</pre>";
                }
                return 
$font;
            }
        }

        
$family null;
        if (
$DEBUGCSS) {
            print 
'(default)';
        }
        
$font $this->getFontMetrics()->getFont($family$subtype);

        if (
$font) {
            if (
$DEBUGCSS) {
                print 
'(' $font ")get_font_family]\n</pre>";
            }
            return 
$font;
        }

        throw new 
Exception("Unable to find a suitable font replacement for: '" $this->_props_computed["font_family"] . "'");
    }

    
/**
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
     * @return float
     */
    
function get_word_spacing()
    {
        
$word_spacing $this->_props_computed["word_spacing"];

        if (
$word_spacing === "normal") {
            return 
0;
        }

        if (
strpos($word_spacing"%") !== false) {
            return 
$word_spacing;
        }

        return (float)
$this->length_in_pt($word_spacing$this->__get("font_size"));
    }

    
/**
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
     * @return float
     */
    
function get_letter_spacing()
    {
        
$letter_spacing $this->_props_computed["letter_spacing"];

        if (
$letter_spacing === "normal") {
            return 
0;
        }

        return (float)
$this->length_in_pt($letter_spacing$this->__get("font_size"));
    }

    
/**
     * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
     * @return float
     */
    
function get_line_height()
    {
        
$line_height $this->_props_computed["line_height"];

        if (
$line_height === "normal") {
            return 
self::$default_line_height $this->__get("font_size");
        }

        if (
is_numeric($line_height)) {
            return 
$line_height $this->__get("font_size");
        }

        return (float)
$this->length_in_pt($line_height$this->__get("font_size"));
    }

    
/**
     * @param string $prop
     * @param bool $current_is_parent
     * @return array|string
     */
    
protected function get_prop_color(string $propbool $current_is_parent false)
    {
        
$val $this->_props_computed[$prop];

        if (
$val === "currentcolor") {
            
// https://www.w3.org/TR/css-color-4/#resolving-other-colors
            
if ($current_is_parent) {
                
// Use the `color` value from the parent for the `color`
                // property itself
                
return isset($this->parent_style)
                    ? 
$this->parent_style->__get("color")
                    : 
$this->munge_color(self::$_defaults[$prop]);
            }

            return 
$this->__get("color");
        }

        return 
$this->munge_color($val) ?? "transparent";
    }

    
/**
     * Returns the color as an array
     *
     * The array has the following format:
     * <code>array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb")</code>
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
     * @return array
     */
    
function get_color()
    {
        return 
$this->get_prop_color("color"true);
    }

    
/**
     * Returns the background color as an array
     *
     * The returned array has the same format as {@link Style::get_color()}
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
     * @return array
     */
    
function get_background_color()
    {
        return 
$this->get_prop_color("background_color");
    }

    
/**
     * Returns the background image URI, or "none"
     *
     * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
     * @return string
     */
    
function get_background_image()
    {
        return 
$this->_stylesheet->resolve_url($this->_props_computed["background_image"]);
    }

    
/**
     * Returns the background position as an array
     *
     * The returned array has the following format:
     * <code>array(x,y, "x" => x, "y" => y)</code>
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
     * @return array
     */
    
function get_background_position()
    {
        
$tmp explode(" "$this->_props_computed["background_position"]);

        return [
            
=> $tmp[0], "x" => $tmp[0],
            
=> $tmp[1], "y" => $tmp[1],
        ];
    }


    
/**
     * Returns the background size as an array
     *
     * The return value has one of the following formats:
     * <code>"cover"</code>
     * <code>"contain"</code>
     * <code>array(width,height)</code>
     *
     * @link https://www.w3.org/TR/css3-background/#background-size
     * @return string|array
     */
    
function get_background_size()
    {
        switch (
$this->_props_computed["background_size"]) {
            case 
"cover":
                return 
"cover";
            case 
"contain":
                return 
"contain";
            default:
                break;
        }

        
$result explode(" "$this->_props_computed["background_size"]);
        return [
$result[0], $result[1]];
    }

    
/**#@+
     * Returns the border color as an array
     *
     * See {@link Style::get_color()}
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
     * @return array
     */
    
function get_border_top_color()
    {
        return 
$this->get_prop_color("border_top_color");
    }

    
/**
     * @return array
     */
    
function get_border_right_color()
    {
        return 
$this->get_prop_color("border_right_color");
    }

    
/**
     * @return array
     */
    
function get_border_bottom_color()
    {
        return 
$this->get_prop_color("border_bottom_color");
    }

    
/**
     * @return array
     */
    
function get_border_left_color()
    {
        return 
$this->get_prop_color("border_left_color");
    }

    
/**#@-*/

    /**
     * Return an array of all border properties.
     *
     * The returned array has the following structure:
     * <code>
     * array("top" => array("width" => [border-width],
     *                      "style" => [border-style],
     *                      "color" => [border-color (array)]),
     *       "bottom" ... )
     * </code>
     *
     * @return array
     */
    
function get_border_properties()
    {
        return [
            
"top" => [
                
"width" => $this->__get("border_top_width"),
                
"style" => $this->__get("border_top_style"),
                
"color" => $this->__get("border_top_color"),
            ],
            
"bottom" => [
                
"width" => $this->__get("border_bottom_width"),
                
"style" => $this->__get("border_bottom_style"),
                
"color" => $this->__get("border_bottom_color"),
            ],
            
"right" => [
                
"width" => $this->__get("border_right_width"),
                
"style" => $this->__get("border_right_style"),
                
"color" => $this->__get("border_right_color"),
            ],
            
"left" => [
                
"width" => $this->__get("border_left_width"),
                
"style" => $this->__get("border_left_style"),
                
"color" => $this->__get("border_left_color"),
            ],
        ];
    }

    
/**
     * Return a single border property
     *
     * @param string $side
     *
     * @return mixed
     */
    
protected function _get_border($side)
    {
        
$color $this->__get("border_" $side "_color");

        return 
$this->__get("border_" $side "_width") . " " .
            
$this->__get("border_" $side "_style") . " " .
            (
is_array($color) ? $color["hex"] : $color);
    }

    
/**#@+
     * Return full border properties as a string
     *
     * Border properties are returned just as specified in CSS:
     * <pre>[width] [style] [color]</pre>
     * e.g. "1px solid blue"
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
     * @return string
     */
    
function get_border_top()
    {
        return 
$this->_get_border("top");
    }

    
/**
     * @return mixed
     */
    
function get_border_right()
    {
        return 
$this->_get_border("right");
    }

    
/**
     * @return mixed
     */
    
function get_border_bottom()
    {
        return 
$this->_get_border("bottom");
    }

    
/**
     * @return mixed
     */
    
function get_border_left()
    {
        return 
$this->_get_border("left");
    }

    
/**
     * @deprecated
     * @param float $w
     * @param float $h
     * @return float[]
     */
    
function get_computed_border_radius($w$h)
    {
        return 
$this->resolve_border_radius([00$w$h]);
    }

    public function 
has_border_radius(): bool
    
{
        if (isset(
$this->has_border_radius_cache)) {
            return 
$this->has_border_radius_cache;
        }

        
// Use a fixed ref size here. We don't know the border-box width here
        // and font size might be 0. Since we are only interested in whether
        // there is any border radius at all, this should do
        
$tl = (float) $this->length_in_pt($this->border_top_left_radius10);
        
$tr = (float) $this->length_in_pt($this->border_top_right_radius10);
        
$br = (float) $this->length_in_pt($this->border_bottom_right_radius10);
        
$bl = (float) $this->length_in_pt($this->border_bottom_left_radius10);

        
$this->has_border_radius_cache $tl $tr $br $bl 0;
        return 
$this->has_border_radius_cache;
    }

    
/**
     * Get the final border-radius values to use.
     *
     * Percentage values are resolved relative to the width of the border box.
     * The border radius is additionally scaled for the given render box, and
     * constrained by its width and height.
     *
     * @param float[]      $border_box The border box of the frame.
     * @param float[]|null $render_box The box to resolve the border radius for.
     *
     * @return float[] A 4-tuple of top-left, top-right, bottom-right, and bottom-left radius.
     */
    
public function resolve_border_radius(
        array 
$border_box,
        ?array 
$render_box null
    
): array {
        
$render_box $render_box ?? $border_box;
        
$use_cache $render_box === $border_box;

        if (
$use_cache && isset($this->resolved_border_radius)) {
            return 
$this->resolved_border_radius;
        }

        [
$x$y$w$h] = $border_box;

        
// Resolve percentages relative to width, as long as we have no support
        // for per-axis radii
        
$tl = (float) $this->length_in_pt($this->border_top_left_radius$w);
        
$tr = (float) $this->length_in_pt($this->border_top_right_radius$w);
        
$br = (float) $this->length_in_pt($this->border_bottom_right_radius$w);
        
$bl = (float) $this->length_in_pt($this->border_bottom_left_radius$w);

        if (
$tl $tr $br $bl 0) {
            [
$rx$ry$rw$rh] = $render_box;

            
$t_offset $y $ry;
            
$r_offset $rx $rw $x $w;
            
$b_offset $ry $rh $y $h;
            
$l_offset $x $rx;

            if (
$tl 0) {
                
$tl max($tl + ($t_offset $l_offset) / 20);
            }
            if (
$tr 0) {
                
$tr max($tr + ($t_offset $r_offset) / 20);
            }
            if (
$br 0) {
                
$br max($br + ($b_offset $r_offset) / 20);
            }
            if (
$bl 0) {
                
$bl max($bl + ($b_offset $l_offset) / 20);
            }

            if (
$tl $bl $rh) {
                
$f $rh / ($tl $bl);
                
$tl $f $tl;
                
$bl $f $bl;
            }
            if (
$tr $br $rh) {
                
$f $rh / ($tr $br);
                
$tr $f $tr;
                
$br $f $br;
            }
            if (
$tl $tr $rw) {
                
$f $rw / ($tl $tr);
                
$tl $f $tl;
                
$tr $f $tr;
            }
            if (
$bl $br $rw) {
                
$f $rw / ($bl $br);
                
$bl $f $bl;
                
$br $f $br;
            }
        }

        
$values = [$tl$tr$br$bl];

        if (
$use_cache) {
            
$this->resolved_border_radius $values;
        }

        return 
$values;
    }

    
/**
     * Returns the outline color as an array
     *
     * See {@link Style::get_color()}
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
     * @return array
     */
    
function get_outline_color()
    {
        return 
$this->get_prop_color("outline_color");
    }

    
/**
     * Return full outline properties as a string
     *
     * Outline properties are returned just as specified in CSS:
     * <pre>[width] [style] [color]</pre>
     * e.g. "1px solid blue"
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
     * @return string
     */
    
function get_outline()
    {
        
$color $this->__get("outline_color");

        return 
$this->__get("outline_width") . " " .
            
$this->__get("outline_style") . " " .
            (
is_array($color) ? $color["hex"] : $color);
    }

    
/**
     * Returns border spacing as an array
     *
     * The array has the format (h_space,v_space)
     *
     * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
     * @return array
     */
    
function get_border_spacing()
    {
        
$arr explode(" "$this->_props_computed["border_spacing"]);
        if (
count($arr) == 1) {
            
$arr[1] = $arr[0];
        }
        return 
$arr;
    }

    
/**
     * Returns the list style image URI, or "none"
     *
     * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
     * @return string
     */
    
function get_list_style_image()
    {
        return 
$this->_stylesheet->resolve_url($this->_props_computed["list_style_image"]);
    }

    
/**
     * @param string $value
     * @param int    $default
     *
     * @return array|string
     */
    
protected function parse_counter_prop(string $valueint $default)
    {
        
$ident self::CSS_IDENTIFIER;
        
$integer self::CSS_INTEGER;
        
$pattern "/($ident)(?:\s+($integer))?/";

        if (!
preg_match_all($pattern$value$matchesPREG_SET_ORDER)) {
            return 
"none";
        }

        
$counters = [];

        foreach (
$matches as $match) {
            
$counter $match[1];
            
$value = isset($match[2]) ? (int) $match[2] : $default;
            
$counters[$counter] = $value;
        }

        return 
$counters;
    }

    
/**
     * @return array|string
     */
    
function get_counter_increment()
    {
        
$val $this->_props_computed["counter_increment"];

        if (
$val === "none" || $val === "inherit") {
            return 
"none";
        }

        return 
$this->parse_counter_prop($val1);
    }

    
/**
     * @return array|string
     */
    
protected function get_counter_reset()
    {
        
$val $this->_props_computed["counter_reset"];

        if (
$val === "none") {
            return 
"none";
        }

        return 
$this->parse_counter_prop($val0);
    }

    
/**
     * @return string[]|string
     */
    
protected function get_content()
    {
        
$val $this->_props_computed["content"];

        if (
$val === "normal" || $val === "none") {
            return 
$val;
        }

        return 
$this->parse_property_value($val);
    }

    
/*==============================*/

    /**
     * Parse a property value into its components.
     *
     * @param string $value
     *
     * @return string[]
     */
    
protected function parse_property_value(string $value): array
    {
        
$ident self::CSS_IDENTIFIER;
        
$number self::CSS_NUMBER;

        
$pattern "/\n" .
            
"\s* \" ( (?:[^\"]|\\\\[\"])* ) (?<!\\\\)\" |\n" // String ""
            
"\s* '  ( (?:[^']|\\\\['])* )   (?<!\\\\)'  |\n" // String ''
            
"\s* ($ident \\([^)]*\\) )                  |\n" // Functional
            
"\s* ($ident)                               |\n" // Keyword
            
"\s* (\#[0-9a-fA-F]*)                       |\n" // Hex value
            
"\s* ($number [a-zA-Z%]*)                   |\n" // Number (+ unit/percentage)
            
"\s* ([\/,;])                                \n" // Delimiter
            
"/Sx";

        if (!
preg_match_all($pattern$value$matches)) {
            return [];
        }

        return 
array_map("trim"$matches[0]);
    }

    protected function 
is_color_value(string $val): bool
    
{
        return 
$val === "currentcolor"
            
|| $val === "transparent"
            
|| isset(Color::$cssColorNames[$val])
            || 
preg_match("/^#|rgb\(|rgba\(|cmyk\(/"$val);
    }

    protected function 
prop_name(string $stylestring $sidestring $type): string
    
{
        
$prop $style;
        if (
$side !== "") {
            
$prop .= "_" $side;
        };
        if (
$type !== "") {
            
$prop .= "_" $type;
        };
        return 
$prop;
    }

    
/**
     * Generalized set function for individual attribute of combined style.
     *
     * Applicable for margin, border, padding, outline.
     *
     * @param string $style
     * @param string $side
     * @param string $type
     * @param mixed $val
     */
    
protected function _set_style_side_type($style$side$type$val)
    {
        
$prop $this->prop_name($style$side$type);

        
$this->_prop_cache[$prop] = null;

        if (
$val === "inherit") {
            
$this->_props_computed[$prop] = null;
            return;
        }

        if (
$side === "bottom") {
            
$this->_computed_bottom_spacing null//reset computed cache, border style can disable/enable border calculations
        
}

        if (
$type === "color") {
            
$this->set_prop_color($prop$val);
        } elseif ((
$style === "border" || $style === "outline") && $type === "width") {
            
// Border-width keywords
            
if ($val === "thin") {
                
$val_computed 0.5;
            } elseif (
$val === "medium") {
                
$val_computed 1.5;
            } elseif (
$val === "thick") {
                
$val_computed 2.5;
            } elseif (
mb_strpos($val"%") !== false) {
                
$val_computed null;
            } else {
                
$val_computed $this->single_length_in_pt($val);

                if (
$val_computed 0) {
                    
$val_computed null;
                }
            }

            if (
$val_computed === null) {
                
$this->_props_computed[$prop] = null;
            } else {
                
$line_style_prop $this->prop_name($style$side"style");
                
$line_style $this->__get($line_style_prop);
                
$has_line_style $line_style !== "none" && $line_style !== "hidden";

                
$this->_props_computed[$prop] = $has_line_style $val_computed 0;
            }
        } elseif ((
$style === "border" || $style === "outline") && $type === "style") {
            if (
in_array($valStyle::$BORDER_STYLEStrue)) {
                
$this->_props_computed[$prop] = $val;
            } else {
                
$this->_props_computed[$prop] = null;
            }
        } elseif (
$style === "margin" || $style === "padding") {
            if (
$val === "none") {
                
// Legacy support for `none` keyword, not covered by spec
                
$val_computed 0;
            } elseif (
$style === "margin" && $val === "auto") {
                
$val_computed $val;
            } elseif (
mb_strpos($val"%") !== false) {
                
$val_computed $val;
            } else {
                
$val_computed $this->single_length_in_pt($val);

                if (
$style === "padding" && $val_computed 0) {
                    
$val_computed null;
                }
            }

            
$this->_props_computed[$prop] = $val_computed;
        } elseif (
$val !== "") {
            
$this->_props_computed[$prop] = $val;
        } else {
            
$this->_props_computed[$prop] = null;
        }
    }

    
/**
     * @param string $style
     * @param string $type
     * @param mixed $val
     * @param bool $important
     */
    
protected function _set_style_type($style$type$val$important)
    {
        
$v $this->parse_property_value($val);

        switch (
count($v)) {
            case 
1:
                [
$top$right$bottom$left] = [$v[0], $v[0], $v[0], $v[0]];
                break;
            case 
2:
                [
$top$right$bottom$left] = [$v[0], $v[1], $v[0], $v[1]];
                break;
            case 
3:
                [
$top$right$bottom$left] = [$v[0], $v[1], $v[2], $v[1]];
                break;
            case 
4:
                [
$top$right$bottom$left] = [$v[0], $v[1], $v[2], $v[3]];
                break;
            default:
                return;
        }

        
$this->set_prop($this->prop_name($style"top"$type), $top$important);
        
$this->set_prop($this->prop_name($style"right"$type), $right$important);
        
$this->set_prop($this->prop_name($style"bottom"$type), $bottom$important);
        
$this->set_prop($this->prop_name($style"left"$type), $left$important);
    }

    
/*======================*/

    /**
     * https://www.w3.org/TR/CSS21/visuren.html#display-prop
     *
     * @param string $val
     */
    
protected function set_display(string $val): void
    
{
        
// Make sure that common valid, but unsupported display types have an
        // appropriate fallback display type
        
switch ($val) {
            case 
"flow-root":
            case 
"flex":
            case 
"grid":
            case 
"table-caption":
                
$val "block";
                break;
            case 
"inline-flex":
            case 
"inline-grid":
                
$val "inline-block";
                break;
        }

        if (!isset(
self::$valid_display_types[$val])) {
            return;
        }

        
// https://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
        
if ($this->is_in_flow()) {
            
$computed $val;
        } else {
            switch (
$val) {
                case 
"inline":
                case 
"inline-block":
                
// case "table-row-group":
                // case "table-header-group":
                // case "table-footer-group":
                // case "table-row":
                // case "table-cell":
                // case "table-column-group":
                // case "table-column":
                // case "table-caption":
                    
$computed "block";
                    break;
                case 
"inline-table":
                    
$computed "table";
                    break;
                default:
                    
$computed $val;
                    break;
            }
        }

        
$this->_props_computed["display"] = $computed;
    }

    protected function 
set_prop_color($prop$val)
    {
        
$this->_prop_cache[$prop] = null;

        
// https://www.w3.org/TR/css-color-4/#resolving-other-colors
        
$munged_color $val !== "currentcolor"
            
$this->munge_color($val)
            : 
$val;

        if (
is_null($munged_color)) {
            
$this->_props_computed[$prop] = null;
            return;
        }

        
$this->_props_computed[$prop] = is_array($munged_color) ? $munged_color["hex"] : $munged_color;
    }

    
/**
     * Sets color
     *
     * The color parameter can be any valid CSS color value
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
     * @param string $color
     */
    
function set_color($color)
    {
        
$this->set_prop_color("color"$color);
    }

    
/**
     * Sets the background color
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
     * @param string $color
     */
    
function set_background_color($color)
    {
        
$this->set_prop_color("background_color"$color);
    }

    
/**
     * Set the background image url
     * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
     *
     * @param string $val
     */
    
function set_background_image($val)
    {
        
$this->_prop_cache["background_image"] = null;

        
$parsed_val $this->_stylesheet->resolve_url($val);

        if (
$parsed_val === "none") {
            
$this->_props_computed["background_image"] = "none";
        } else {
            
$this->_props_computed["background_image"] = "url(" $parsed_val ")";
        }
    }

    
/**
     * Sets the background repeat
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
     * @param string $val
     */
    
function set_background_repeat($val)
    {
        
$this->_prop_cache["background_repeat"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["background_repeat"] = null;
            return;
        }

        
$this->_props_computed["background_repeat"] = $val;
    }

    
/**
     * Sets the background attachment
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
     * @param string $val
     */
    
function set_background_attachment($val)
    {
        
$this->_prop_cache["background_attachment"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["background_attachment"] = null;
            return;
        }

        
$this->_props_computed["background_attachment"] = $val;
    }

    
/**
     * Sets the background position
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
     * @param string $val
     */
    
function set_background_position($val)
    {
        
$this->_prop_cache["background_position"] = null;

        
$tmp explode(" "$val);

        switch (
$tmp[0]) {
            case 
"left":
                
$x "0%";
                break;

            case 
"right":
                
$x "100%";
                break;

            case 
"top":
                
$y "0%";
                break;

            case 
"bottom":
                
$y "100%";
                break;

            case 
"center":
                
$x "50%";
                
$y "50%";
                break;

            default:
                
$x $tmp[0];
                break;
        }

        if (isset(
$tmp[1])) {
            switch (
$tmp[1]) {
                case 
"left":
                    
$x "0%";
                    break;

                case 
"right":
                    
$x "100%";
                    break;

                case 
"top":
                    
$y "0%";
                    break;

                case 
"bottom":
                    
$y "100%";
                    break;

                case 
"center":
                    if (
$tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center") {
                        
$y "50%";
                    } else {
                        
$x "50%";
                    }
                    break;

                default:
                    
$y $tmp[1];
                    break;
            }
        } else {
            
$y "50%";
        }

        if (!isset(
$x)) {
            
$x "0%";
        }

        if (!isset(
$y)) {
            
$y "0%";
        }
        
        
$this->_props_computed["background_position"] = "$x $y";
    }

    
/**
     * Sets the background size
     *
     * @link https://www.w3.org/TR/css3-background/#background-size
     * @param string $val
     */
    
function set_background_size($val)
    {
        
$this->_prop_cache["background_size"] = null;

        
$result explode(" "$val);
        
$width $result[0];

        switch (
$width) {
            case 
"cover":
            case 
"contain":
                
$this->_props_computed["background_size"] = $width;
                return;
            case 
"inherit":
                
$this->_props_computed["background_size"] = null;
                return;
        }

        if (
$width !== "auto" && strpos($width"%") === false) {
            
$width = (float)$this->length_in_pt($width);
        }

        
$height $result[1] ?? "auto";
        if (
$height !== "auto" && strpos($height"%") === false) {
            
$height = (float)$this->length_in_pt($height);
        }

        
$this->_props_computed["background_size"] = "$width $height";
    }

    
/**
     * Sets the background - combined options
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
     * @param string $value
     * @param bool $important
     */
    
function set_background($valuebool $important false)
    {
        if (
$value === "none") {
            
$this->set_prop("background_image""none"$important);
            
$this->set_prop("background_color""transparent"$important);
        } else {
            
$components $this->parse_property_value($value);
            
$pos_size = [];

            foreach (
$components as $val) {
                if (
$val === "none" || mb_substr($val04) === "url(") {
                    
$this->set_prop("background_image"$val$important);
                } elseif (
$val === "fixed" || $val === "scroll") {
                    
$this->set_prop("background_attachment"$val$important);
                } elseif (
$val === "repeat" || $val === "repeat-x" || $val === "repeat-y" || $val === "no-repeat") {
                    
$this->set_prop("background_repeat"$val$important);
                } elseif (
$this->is_color_value($val)) {
                    
$this->set_prop("background_color"$val$important);
                } else {
                    
$pos_size[] = $val;
                }
            }

            if (
count($pos_size)) {
                
// Split value list at "/"
                
$index array_search("/"$pos_sizetrue);

                if (
$index !== false) {
                    
$pos array_slice($pos_size0$index);
                    
$size array_slice($pos_size$index 1);
                } else {
                    
$pos $pos_size;
                    
$size = [];
                }

                
$this->set_prop("background_position"implode(" "$pos), $important);

                if (
count($size)) {
                    
$this->set_prop("background_size"implode(" "$size), $important);
                }
            }
        }
    }

    
/**
     * Sets the font size
     *
     * $size can be any acceptable CSS size
     *
     * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
     * @param string|float $size
     */
    
function set_font_size($size)
    {
        
$this->_prop_cache["font_size"] = null;

        if (
$size === "inherit") {
            
$this->_props_computed["font_size"] = null;
            return;
        }

        
$parent_font_size = isset($this->parent_style)
            ? 
$this->parent_style->__get("font_size")
            : 
self::$default_font_size;

        switch ((string)
$size) {
            case 
"xx-small":
            case 
"x-small":
            case 
"small":
            case 
"medium":
            case 
"large":
            case 
"x-large":
            case 
"xx-large":
                
$fs self::$default_font_size self::$font_size_keywords[$size];
                break;

            case 
"smaller":
                
$fs $parent_font_size;
                break;

            case 
"larger":
                
$fs $parent_font_size;
                break;

            default:
                
$fs $this->single_length_in_pt($size$parent_font_size$parent_font_size);
                break;
        }

        
$this->_props_computed["font_size"] = $fs;
    }

    
/**
     * Sets the font weight
     *
     * @param string|int $weight
     */
    
function set_font_weight($weight)
    {
        
$this->_prop_cache["font_weight"] = null;

        if (
$weight === "inherit") {
            
$this->_props_computed["font_weight"] = null;
            return;
        }

        
$computed_weight $weight;

        if (
$weight === "bolder") {
            
//TODO: One font weight heavier than the parent element (among the available weights of the font).
            
$computed_weight "bold";
        } elseif (
$weight === "lighter") {
            
//TODO: One font weight lighter than the parent element (among the available weights of the font).
            
$computed_weight "normal";
        }

        
$this->_props_computed["font_weight"] = $computed_weight;
    }

    
/**
     * Sets the font style
     *
     * combined attributes
     * set individual attributes also, respecting !important mark
     * exactly this order, separate by space. Multiple fonts separated by comma:
     * font-style, font-variant, font-weight, font-size, line-height, font-family
     *
     * Other than with border and list, existing partial attributes should
     * reset when starting here, even when not mentioned.
     * If individual attribute is !important and explicit or implicit replacement is not,
     * keep individual attribute
     *
     * require whitespace as delimiters for single value attributes
     * On delimiter "/" treat first as font height, second as line height
     * treat all remaining at the end of line as font
     * font-style, font-variant, font-weight, font-size, line-height, font-family
     *
     * missing font-size and font-family might be not allowed, but accept it here and
     * use default (medium size, empty font name)
     *
     * @link https://www.w3.org/TR/CSS21/fonts.html#font-shorthand
     * @param string $val
     * @param bool $important
     */
    
function set_font($valbool $important false)
    {
        if (
preg_match("/^(italic|oblique|normal)\s*(.*)$/i"$val$match)) {
            
$this->set_prop("font_style"$match[1], $important);
            
$val $match[2];
        }

        if (
preg_match("/^(small-caps|normal)\s*(.*)$/i"$val$match)) {
            
$this->set_prop("font_variant"$match[1], $important);
            
$val $match[2];
        }

        
//matching numeric value followed by unit -> this is indeed a subsequent font size. Skip!
        
if (preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i"$val$match) &&
            !
preg_match("/^(?:pt|px|pc|rem|em|ex|in|cm|mm|%)/"$match[2])
        ) {
            
$this->set_prop("font_weight"$match[1], $important);
            
$val $match[2];
        }

        if (
preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|rem|em|ex|in|cm|mm|%))(?:\/|\s*)(.*)$/i"$val$match)) {
            
$this->set_prop("font_size"$match[1], $important);
            
$val $match[2];
            if (
preg_match("/^(?:\/|\s*)(\d+\s*(?:pt|px|pc|rem|em|ex|in|cm|mm|%)?)\s*(.*)$/i"$val$match)) {
                
$this->set_prop("line_height"$match[1], $important);
                
$val $match[2];
            }
        }

        if (
strlen($val) != 0) {
            
$this->set_prop("font_family"$val$important);
        }
    }

    
/**
     * Sets the text alignment
     *
     * If no alignment is set on the element and the direction is rtl then
     * the property is set to "right", otherwise it is set to "left".
     *
     * @link https://www.w3.org/TR/CSS21/text.html#propdef-text-align
     */
    
public function set_text_align($val)
    {
        
$this->_prop_cache["text_align"] = null;

        
$alignment $val;
        if (
$alignment === "") {
            
$alignment "left";
            if (
$this->__get("direction") === "rtl") {
                
$alignment "right";
            }
        }

        if (!
in_array($alignmentself::$text_align_keywordstrue)) {
            
$this->_props_computed["text_align"] = null;
            return;
        }

        
$this->_props_computed["text_align"] = $alignment;
    }

    
/**
     * Sets word spacing property
     *
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
     * @param $val
     */
    
function set_word_spacing($val)
    {
        
$this->_prop_cache["word_spacing"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["word_spacing"] = null;
            return;
        }

        if (
$val === "normal" || strpos($val"%") !== false) {
            
$this->_props_computed["word_spacing"] = $val;
        } else {
            
$this->_props_computed["word_spacing"] = ((float)$this->length_in_pt($val$this->__get("font_size"))) . "pt";
        }
    }

    
/**
     * Sets letter spacing property
     *
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
     * @param $val
     */
    
function set_letter_spacing($val)
    {
        
$this->_prop_cache["letter_spacing"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["letter_spacing"] = null;
            return;
        }

        if (
$val === "normal") {
            
$this->_props_computed["letter_spacing"] = $val;
        } else {
            
$this->_props_computed["letter_spacing"] = ((float)$this->length_in_pt($val$this->__get("font_size"))) . "pt";
        }
    }

    
/**
     * Sets line height property
     *
     * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
     * @param $val
     */
    
function set_line_height($val)
    {
        
$this->_prop_cache["line_height"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["line_height"] = null;
            return;
        }

        if (
$val === "normal" || is_numeric($val)) {
            
$this->_props_computed["line_height"] = $val;
        } else {
            
$this->_props_computed["line_height"] = ((float)$this->length_in_pt($val$this->__get("font_size"))) . "pt";
        }
    }

    
/**
     * Sets page break properties
     *
     * @link http://www.w3.org/TR/CSS21/page.html#page-breaks
     * @param string $break
     */
    
function set_page_break_before($break)
    {
        
$this->_prop_cache["page_break_before"] = null;

        if (
$break === "inherit") {
            
$this->_props_computed["page_break_before"] = null;
            return;
        }

        if (
$break === "left" || $break === "right") {
            
$break "always";
        }

        
$this->_props_computed["page_break_before"] = $break;
    }

    
/**
     * @param $break
     */
    
function set_page_break_after($break)
    {
        
$this->_prop_cache["page_break_after"] = null;

        if (
$break === "inherit") {
            
$this->_props_computed["page_break_after"] = null;
            return;
        }

        if (
$break === "left" || $break === "right") {
            
$break "always";
        }

        
$this->_props_computed["page_break_after"] = $break;
    }

    
/**
     * Sets the margin size
     *
     * @link http://www.w3.org/TR/CSS21/box.html#margin-properties
     * @param $val
     */
    
function set_margin_top($val)
    {
        
$this->_set_style_side_type("margin""top"""$val);
    }

    
/**
     * @param $val
     */
    
function set_margin_right($val)
    {
        
$this->_set_style_side_type("margin""right"""$val);
    }

    
/**
     * @param $val
     */
    
function set_margin_bottom($val)
    {
        
$this->_set_style_side_type("margin""bottom"""$val);
    }

    
/**
     * @param $val
     */
    
function set_margin_left($val)
    {
        
$this->_set_style_side_type("margin""left"""$val);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_margin($valbool $important false)
    {
        
$this->_set_style_type("margin"""$val$important);
    }

    
/**
     * Sets the padding size
     *
     * @link http://www.w3.org/TR/CSS21/box.html#padding-properties
     * @param $val
     */
    
function set_padding_top($val)
    {
        
$this->_set_style_side_type("padding""top"""$val);
    }

    
/**
     * @param $val
     */
    
function set_padding_right($val)
    {
        
$this->_set_style_side_type("padding""right"""$val);
    }

    
/**
     * @param $val
     */
    
function set_padding_bottom($val)
    {
        
$this->_set_style_side_type("padding""bottom"""$val);
    }

    
/**
     * @param $val
     */
    
function set_padding_left($val)
    {
        
$this->_set_style_side_type("padding""left"""$val);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_padding($valbool $important false)
    {
        
$this->_set_style_type("padding"""$val$important);
    }

    
/**
     * Sets a single border
     *
     * @param string $side
     * @param string $border_spec ([width] [style] [color])
     * @param bool $important
     */
    
protected function _set_border($side$border_specbool $important)
    {
        
$components $this->parse_property_value($border_spec);

        foreach (
$components as $val) {
            if (
in_array($valself::$BORDER_STYLEStrue)) {
                
$this->set_prop("border_${side}_style"$val$important);
            } elseif (
$this->is_color_value($val)) {
                
$this->set_prop("border_${side}_color"$val$important);
            } else {
                
// Assume width
                
$this->set_prop("border_${side}_width"$val$important);
            }
        }
    }

    
/**
     * @link http://www.w3.org/TR/CSS21/box.html#border-properties
     * @param string $val
     * @param bool $important
     */
    
function set_border_top($valbool $important false)
    {
        
$this->_set_border("top"$val$important);
    }

    function 
set_border_top_color($val)
    {
        
$this->_set_style_side_type("border""top""color"$val);
    }

    function 
set_border_top_style($val)
    {
        
$this->_set_style_side_type("border""top""style"$val);
    }

    function 
set_border_top_width($val)
    {
        
$this->_set_style_side_type("border""top""width"$val);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border_right($valbool $important false)
    {
        
$this->_set_border("right"$val$important);
    }

    function 
set_border_right_color($val)
    {
        
$this->_set_style_side_type("border""right""color"$val);
    }

    function 
set_border_right_style($val)
    {
        
$this->_set_style_side_type("border""right""style"$val);
    }

    function 
set_border_right_width($val)
    {
        
$this->_set_style_side_type("border""right""width"$val);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border_bottom($valbool $important false)
    {
        
$this->_set_border("bottom"$val$important);
    }

    function 
set_border_bottom_color($val)
    {
        
$this->_set_style_side_type("border""bottom""color"$val);
    }

    function 
set_border_bottom_style($val)
    {
        
$this->_set_style_side_type("border""bottom""style"$val);
    }

    function 
set_border_bottom_width($val)
    {
        
$this->_set_style_side_type("border""bottom""width"$val);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border_left($valbool $important false)
    {
        
$this->_set_border("left"$val$important);
    }

    function 
set_border_left_color($val)
    {
        
$this->_set_style_side_type("border""left""color"$val);
    }

    function 
set_border_left_style($val)
    {
        
$this->_set_style_side_type("border""left""style"$val);
    }

    function 
set_border_left_width($val)
    {
        
$this->_set_style_side_type("border""left""width"$val);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border($valbool $important false)
    {
        
$this->_set_border("top"$val$important);
        
$this->_set_border("right"$val$important);
        
$this->_set_border("bottom"$val$important);
        
$this->_set_border("left"$val$important);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border_width($valbool $important false)
    {
        
$this->_set_style_type("border""width"$val$important);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border_color($valbool $important false)
    {
        
$this->_set_style_type("border""color"$val$important);
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border_style($valbool $important false)
    {
        
$this->_set_style_type("border""style"$val$important);
    }

    
/**
     * Sets the border radius size
     *
     * http://www.w3.org/TR/css3-background/#corners
     *
     * @param string $val
     */
    
function set_border_top_left_radius($val)
    {
        
$this->_set_border_radius_corner($val"top_left");
    }

    
/**
     * @param string $val
     */
    
function set_border_top_right_radius($val)
    {
        
$this->_set_border_radius_corner($val"top_right");
    }

    
/**
     * @param string $val
     */
    
function set_border_bottom_left_radius($val)
    {
        
$this->_set_border_radius_corner($val"bottom_left");
    }

    
/**
     * @param string $val
     */
    
function set_border_bottom_right_radius($val)
    {
        
$this->_set_border_radius_corner($val"bottom_right");
    }

    
/**
     * @param string $val
     * @param bool $important
     */
    
function set_border_radius($valbool $important false)
    {
        
$r $this->parse_property_value($val);

        switch (
count($r)) {
            case 
1:
                [
$tl$tr$br$bl] = [$r[0], $r[0], $r[0], $r[0]];
                break;
            case 
2:
                [
$tl$tr$br$bl] = [$r[0], $r[1], $r[0], $r[1]];
                break;
            case 
3:
                [
$tl$tr$br$bl] = [$r[0], $r[1], $r[2], $r[1]];
                break;
            case 
4:
                [
$tl$tr$br$bl] = [$r[0], $r[1], $r[2], $r[3]];
                break;
            default:
                return;
        }

        
$this->set_prop("border_top_left_radius"$tl$important);
        
$this->set_prop("border_top_right_radius"$tr$important);
        
$this->set_prop("border_bottom_right_radius"$br$important);
        
$this->set_prop("border_bottom_left_radius"$bl$important);
    }

    
/**
     * @param string $val
     * @param string $corner
     */
    
protected function _set_border_radius_corner($val$corner)
    {
        
$prop "border_" $corner "_radius";

        
$this->_prop_cache[$prop] = null;

        if (
$val === "inherit") {
            
$this->_props_computed[$prop] = null;
            return;
        }

        
$computed mb_strpos($val"%") === false
            
$this->single_length_in_pt($val)
            : 
$val;

        
$this->_props_computed[$prop] = $computed;
    }

    
/**
     * @return float|int|string
     */
    
function get_border_top_left_radius()
    {
        return 
$this->_get_border_radius_corner("top_left");
    }

    
/**
     * @return float|int|string
     */
    
function get_border_top_right_radius()
    {
        return 
$this->_get_border_radius_corner("top_right");
    }

    
/**
     * @return float|int|string
     */
    
function get_border_bottom_left_radius()
    {
        return 
$this->_get_border_radius_corner("bottom_left");
    }

    
/**
     * @return float|int|string
     */
    
function get_border_bottom_right_radius()
    {
        return 
$this->_get_border_radius_corner("bottom_right");
    }

    
/**
     * @param $corner
     * @return float|int|string
     */
    
protected function _get_border_radius_corner($corner)
    {
        
$prop "border_" $corner "_radius";

        if (!isset(
$this->_props_computed[$prop])) {
            return 
0;
        }

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

    
/**
     * Sets the outline styles
     *
     * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines
     * @param string $value
     * @param bool $important
     */
    
function set_outline($valuebool $important false)
    {
        
$components $this->parse_property_value($value);

        foreach (
$components as $val) {
            if (
in_array($valself::$BORDER_STYLEStrue)) {
                
$this->set_prop("outline_style"$val$important);
            } elseif (
$this->is_color_value($val)) {
                
$this->set_prop("outline_color"$val$important);
            } else {
                
// Assume width
                
$this->set_prop("outline_width"$val$important);
            }
        }
    }

    
/**
     * @param $val
     */
    
function set_outline_width($val)
    {
        
$this->_set_style_side_type("outline""""width"$val);
    }

    
/**
     * @param $val
     */
    
function set_outline_color($val)
    {
        
$this->_set_style_side_type("outline""""color"$val);
    }

    
/**
     * @param $val
     */
    
function set_outline_style($val)
    {
        
$this->_set_style_side_type("outline""""style"$val);
    }

    
/**
     * Sets the border spacing
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-properties
     * @param float $val
     */
    
function set_border_spacing($val)
    {
        
$this->_prop_cache["border_spacing"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["border_spacing"] = null;
            return;
        }

        
$arr explode(" "$val);

        if (
count($arr) === 1) {
            
$arr[1] = $arr[0];
        }

        
$this->_props_computed["border_spacing"] = "$arr[0] $arr[1]";
    }

    
/**
     * Sets the list style image
     *
     * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
     * @param $val
     */
    
function set_list_style_image($val)
    {
        
$this->_prop_cache["list_style_image"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["list_style_image"] = null;
            return;
        }

        
$parsed_val $this->_stylesheet->resolve_url($val);

        if (
$parsed_val === "none") {
            
$this->_props_computed["list_style_image"] = "none";
        } else {
            
$this->_props_computed["list_style_image"] = "url(" $parsed_val ")";
        }
    }

    
/**
     * Sets the list style
     *
     * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
     * @param string $value
     * @param bool $important
     */
    
function set_list_style($valuebool $important false)
    {
        static 
$positions = ["inside""outside"];
        static 
$types = [
            
"disc""circle""square",
            
"decimal-leading-zero""decimal""1",
            
"lower-roman""upper-roman""a""A",
            
"lower-greek",
            
"lower-latin""upper-latin",
            
"lower-alpha""upper-alpha",
            
"armenian""georgian""hebrew",
            
"cjk-ideographic""hiragana""katakana",
            
"hiragana-iroha""katakana-iroha""none"
        
];

        
$components $this->parse_property_value($value);

        foreach (
$components as $val) {
            
/* http://www.w3.org/TR/CSS21/generate.html#list-style
             * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
             */
            
if ($val === "none") {
                
$this->set_prop("list_style_type"$val$important);
                
$this->set_prop("list_style_image"$val$important);
                continue;
            }

            
//On setting or merging or inheriting list_style_image as well as list_style_type,
            //and url exists, then url has precedence, otherwise fall back to list_style_type
            //Firefox is wrong here (list_style_image gets overwritten on explicit list_style_type)
            //Internet Explorer 7/8 and dompdf is right.

            
if (mb_substr($val04) === "url(") {
                
$this->set_prop("list_style_image"$val$important);
                continue;
            }

            if (
in_array($val$typestrue)) {
                
$this->set_prop("list_style_type"$val$important);
            } elseif (
in_array($val$positionstrue)) {
                
$this->set_prop("list_style_position"$val$important);
            }
        }
    }

    
/**
     * @param $val
     */
    
function set_size($val)
    {
        
$this->_prop_cache["size"] = null;

        
$length_re "/(\d+\s*(?:pt|px|pc|rem|em|ex|in|cm|mm|%))/";

        
$val mb_strtolower($val);

        if (
$val === "auto") {
            
$this->_props_computed["size"] = $val;
            return;
        }

        
$parts preg_split("/\s+/"$val);

        
$computed = [];
        if (
preg_match($length_re$parts[0])) {
            
$computed[] = $this->length_in_pt($parts[0]);

            if (isset(
$parts[1]) && preg_match($length_re$parts[1])) {
                
$computed[] = $this->length_in_pt($parts[1]);
            } else {
                
$computed[] = $computed[0];
            }

            if (isset(
$parts[2]) && $parts[2] === "landscape") {
                
$computed array_reverse($computed);
            }
        } elseif (isset(
CPDF::$PAPER_SIZES[$parts[0]])) {
            
$computed array_slice(CPDF::$PAPER_SIZES[$parts[0]], 22);

            if (isset(
$parts[1]) && $parts[1] === "landscape") {
                
$computed array_reverse($computed);
            }
        } else {
            
$this->_props_computed["size"] = null;
            return;
        }

        
$this->_props_computed["size"] = $computed;
    }

    
/**
     * Gets the CSS3 transform property
     *
     * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property
     * @return array|null
     */
    
function get_transform()
    {
        
//TODO: should be handled in setter (lengths set to absolute)

        
$number "\s*([^,\s]+)\s*";
        
$tr_value "\s*([^,\s]+)\s*";
        
$angle "\s*([^,\s]+(?:deg|rad)?)\s*";

        if (!
preg_match_all("/[a-z]+\([^\)]+\)/i"$this->_props_computed["transform"], $partsPREG_SET_ORDER)) {
            return 
null;
        }

        
$functions = [
            
//"matrix"     => "\($number,$number,$number,$number,$number,$number\)",

            
"translate" => "\($tr_value(?:,$tr_value)?\)",
            
"translateX" => "\($tr_value\)",
            
"translateY" => "\($tr_value\)",

            
"scale" => "\($number(?:,$number)?\)",
            
"scaleX" => "\($number\)",
            
"scaleY" => "\($number\)",

            
"rotate" => "\($angle\)",

            
"skew" => "\($angle(?:,$angle)?\)",
            
"skewX" => "\($angle\)",
            
"skewY" => "\($angle\)",
        ];

        
$transforms = [];

        foreach (
$parts as $part) {
            
$t $part[0];

            foreach (
$functions as $name => $pattern) {
                if (
preg_match("/$name\s*$pattern/i"$t$matches)) {
                    
$values array_slice($matches1);

                    switch (
$name) {
                        
// <angle> units
                        
case "rotate":
                        case 
"skew":
                        case 
"skewX":
                        case 
"skewY":

                            foreach (
$values as $i => $value) {
                                if (
strpos($value"rad")) {
                                    
$values[$i] = rad2deg(floatval($value));
                                } else {
                                    
$values[$i] = floatval($value);
                                }
                            }

                            switch (
$name) {
                                case 
"skew":
                                    if (!isset(
$values[1])) {
                                        
$values[1] = 0;
                                    }
                                    break;
                                case 
"skewX":
                                    
$name "skew";
                                    
$values = [$values[0], 0];
                                    break;
                                case 
"skewY":
                                    
$name "skew";
                                    
$values = [0$values[0]];
                                    break;
                            }
                            break;

                        
// <translation-value> units
                        
case "translate":
                            
$values[0] = $this->length_in_pt($values[0], (float)$this->length_in_pt($this->width));

                            if (isset(
$values[1])) {
                                
$values[1] = $this->length_in_pt($values[1], (float)$this->length_in_pt($this->height));
                            } else {
                                
$values[1] = 0;
                            }
                            break;

                        case 
"translateX":
                            
$name "translate";
                            
$values = [$this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)), 0];
                            break;

                        case 
"translateY":
                            
$name "translate";
                            
$values = [0$this->length_in_pt($values[0], (float)$this->length_in_pt($this->height))];
                            break;

                        
// <number> units
                        
case "scale":
                            if (!isset(
$values[1])) {
                                
$values[1] = $values[0];
                            }
                            break;

                        case 
"scaleX":
                            
$name "scale";
                            
$values = [$values[0], 1.0];
                            break;

                        case 
"scaleY":
                            
$name "scale";
                            
$values = [1.0$values[0]];
                            break;
                    }

                    
$transforms[] = [
                        
$name,
                        
$values,
                    ];
                }
            }
        }

        return 
$transforms;
    }

    
/**
     * @param $val
     */
    
function set_transform($val)
    {
        
$this->_prop_cache["transform"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["transform"] = null;
            return;
        }
        
        
$this->_props_computed["transform"] = $val;
    }

    
/**
     * Sets the CSS3 transform-origin property
     *
     * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
     * @param string $val
     */
    
function set_transform_origin($val)
    {
        
$this->_prop_cache["transform_origin"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["transform_origin"] = null;
            return;
        }

        
$this->_props_computed["transform_origin"] = $val;
    }

    
/**
     * Gets the CSS3 transform-origin property
     *
     * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
     * @return mixed[]
     */
    
function get_transform_origin()
    {
        
//TODO: should be handled in setter
        
        
$values preg_split("/\s+/"$this->_props_computed["transform_origin"]);

        
$values array_map(function ($value) {
            if (
in_array($value, ["top""left"])) {
                return 
0;
            } else if (
in_array($value, ["bottom""right"])) {
                return 
"100%";
            } else {
                return 
$value;
            }
        }, 
$values);

        if (!isset(
$values[1])) {
            
$values[1] = $values[0];
        }

        return 
$values;
    }

    
/**
     * @param $val
     * @return null
     */
    
protected function parse_image_resolution($val)
    {
        
// If exif data could be get:
        // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';

        
$re '/^\s*(\d+|normal|auto)\s*$/';

        if (!
preg_match($re$val$matches)) {
            return 
null;
        }

        return 
$matches[1];
    }

    
/**
     * auto | normal | dpi
     *
     * @param $val
     */
    
function set_background_image_resolution($val)
    {
        
$this->_prop_cache["background_image_resolution"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["background_image_resolution"] = null;
            return;
        }

        
$parsed $this->parse_image_resolution($val);

        
$this->_props_computed["background_image_resolution"] = $parsed;
    }

    
/**
     * auto | normal | dpi
     *
     * @param $val
     */
    
function set_image_resolution($val)
    {
        
$this->_prop_cache["image_resolution"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["image_resolution"] = null;
            return;
        }

        
$parsed $this->parse_image_resolution($val);

        
$this->_props_computed["image_resolution"] = $parsed;
    }

    
/**
     * @param $val
     */
    
function set_z_index($val)
    {
        
$this->_prop_cache["z_index"] = null;

        if (
$val === "inherit") {
            
$this->_props_computed["z_index"] = null;
            return;
        }

        if (
$val !== "auto" && round((float) $val) != $val) {
            
$this->_props_computed["z_index"] = null;
            return;
        }

        
$this->_props_computed["z_index"] = $val;
    }

    
/**
     * @param FontMetrics $fontMetrics
     * @return $this
     */
    
public function setFontMetrics(FontMetrics $fontMetrics)
    {
        
$this->fontMetrics $fontMetrics;
        return 
$this;
    }

    
/**
     * @return FontMetrics
     */
    
public function getFontMetrics()
    {
        return 
$this->fontMetrics;
    }

    
/**
     * Generate a string representation of the Style
     *
     * This dumps the entire property array into a string via print_r.  Useful
     * for debugging.
     *
     * @return string
     */
    /*DEBUGCSS print: see below additional debugging util*/
    
function __toString()
    {
        
$parent_font_size $this->parent_style
            
$this->parent_style->font_size
            
self::$default_font_size;

        return 
print_r(array_merge(["parent_font_size" => $parent_font_size ],
            
$this->_props), true);
    }

    
/*DEBUGCSS*/
    
function debug_print()
    {
        
$parent_font_size $this->parent_style
            
$this->parent_style->font_size
            
self::$default_font_size;

        print 
"    parent_font_size:" $parent_font_size ";\n";
        print 
"    Props [\n";
        print 
"      specified [\n";
        foreach (
$this->_props as $prop => $val) {
            print 
'        ' $prop ': ' preg_replace("/\r\n/"' 'print_r($valtrue));
            if (isset(
$this->_important_props[$prop])) {
                print 
' !important';
            }
            print 
";\n";
        }
        print 
"      ]\n";
        print 
"      computed [\n";
        foreach (
$this->_props_computed as $prop => $val) {
            print 
'        ' $prop ': ' preg_replace("/\r\n/"' 'print_r($valtrue));
            print 
";\n";
        }
        print 
"      ]\n";
        print 
"      cached [\n";
        foreach (
$this->_prop_cache as $prop => $val) {
            print 
'        ' $prop ': ' preg_replace("/\r\n/"' 'print_r($valtrue));
            print 
";\n";
        }
        print 
"      ]\n";
        print 
"    ]\n";
    }
}

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ ok ]

:: Make Dir ::
 
[ ok ]
:: Make File ::
 
[ ok ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0107 ]--