applies all current styles to a particular document tree

apply_styles() applies all currently loaded styles to the provided
{@link FrameTree}.  Aside from parsing CSS, this is the main purpose
of this class.

@param \Dompdf\Frame\FrameTree $tree
483437
function apply_styles(FrameTree $tree)
    {
        // Use XPath to select nodes.  This would be easier if we could attach
        // Frame objects directly to DOMNodes using the setUserData() method, but
        // we can't do that just yet.  Instead, we set a _node attribute_ in
        // Frame->set_id() and use that as a handle on the Frame object via
        // FrameTree::$_registry.

        // We create a scratch array of styles indexed by frame id.  Once all
        // styles have been assigned, we order the cached styles by specificity
        // and create a final style object to assign to the frame.

        // FIXME: this is not particularly robust...

        $styles = array();
        $xp = new DOMXPath($tree->get_dom());
        $DEBUGCSS = $this->_dompdf->getOptions()->getDebugCss();

        // Add generated content
        foreach ($this->_styles as $selector => $selector_styles) {
            /** @var Style $style */
            foreach ($selector_styles as $style) {
                if (strpos($selector, ":before") === false && strpos($selector, ":after") === false) {
                    continue;
                }

                $query = $this->_css_selector_to_xpath($selector, true);

                // Retrieve the nodes, limit to body for generated content
                //TODO: If we use a context node can we remove the leading dot?
                $nodes = @$xp->query('.' . $query["query"]);
                if ($nodes == null) {
                    Helpers::record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
                    continue;
                }

                /** @var \DOMElement $node */
                foreach ($nodes as $node) {
                    // Only DOMElements get styles
                    if ($node->nodeType != XML_ELEMENT_NODE) {
                        continue;
                    }

                    foreach (array_keys($query["pseudo_elements"], true, true) as $pos) {
                        // Do not add a new pseudo element if another one already matched
                        if ($node->hasAttribute("dompdf_{$pos}_frame_id")) {
                            continue;
                        }

                        if (($src = $this->_image($style->get_prop('content'))) !== "none") {
                            $new_node = $node->ownerDocument->createElement("img_generated");
                            $new_node->setAttribute("src", $src);
                        } else {
                            $new_node = $node->ownerDocument->createElement("dompdf_generated");
                        }

                        $new_node->setAttribute($pos, $pos);
                        $new_frame_id = $tree->insert_node($node, $new_node, $pos);
                        $node->setAttribute("dompdf_{$pos}_frame_id", $new_frame_id);
                    }
                }
            }
        }

        // Apply all styles in stylesheet
        foreach ($this->_styles as $selector => $selector_styles) {
            /** @var Style $style */
            foreach ($selector_styles as $style) {
                $query = $this->_css_selector_to_xpath($selector);

                // Retrieve the nodes
                $nodes = @$xp->query($query["query"]);
                if ($nodes == null) {
                    Helpers::record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
                    continue;
                }

                $spec = $this->_specificity($selector, $style->get_origin());

                foreach ($nodes as $node) {
                    // Retrieve the node id
                    // Only DOMElements get styles
                    if ($node->nodeType != XML_ELEMENT_NODE) {
                        continue;
                    }

                    $id = $node->getAttribute("frame_id");

                    // Assign the current style to the scratch array
                    $styles[$id][$spec][] = $style;
                }
            }
        }

        // Set the page width, height, and orientation based on the canvas paper size
        $canvas = $this->_dompdf->getCanvas();
        $paper_width = $canvas->get_width();
        $paper_height = $canvas->get_height();
        $paper_orientation = ($paper_width > $paper_height ? "landscape" : "portrait");

        if ($this->_page_styles["base"] && is_array($this->_page_styles["base"]->size)) {
            $paper_width = $this->_page_styles['base']->size[0];
            $paper_height = $this->_page_styles['base']->size[1];
            $paper_orientation = ($paper_width > $paper_height ? "landscape" : "portrait");
        }

        // Now create the styles and assign them to the appropriate frames. (We
        // iterate over the tree using an implicit FrameTree iterator.)
        $root_flg = false;
        foreach ($tree->get_frames() as $frame) {
            // Helpers::pre_r($frame->get_node()->nodeName . ":");
            if (!$root_flg && $this->_page_styles["base"]) {
                $style = $this->_page_styles["base"];
            } else {
                $style = $this->create_style();
            }

            // Find nearest DOMElement parent
            $p = $frame;
            while ($p = $p->get_parent()) {
                if ($p->get_node()->nodeType == XML_ELEMENT_NODE) {
                    break;
                }
            }

            // Styles can only be applied directly to DOMElements; anonymous
            // frames inherit from their parent
            if ($frame->get_node()->nodeType != XML_ELEMENT_NODE) {
                if ($p) {
                    $style->inherit($p->get_style());
                }

                $frame->set_style($style);
                continue;
            }

            $id = $frame->get_id();

            // Handle HTML 4.0 attributes
            AttributeTranslator::translate_attributes($frame);
            if (($str = $frame->get_node()->getAttribute(AttributeTranslator::$_style_attr)) !== "") {
                $styles[$id][self::SPEC_NON_CSS][] = $this->_parse_properties($str);
            }

            // Locate any additional style attributes
            if (($str = $frame->get_node()->getAttribute("style")) !== "") {
                // Destroy CSS comments
                $str = preg_replace("'/\*.*?\*/'si", "", $str);

                $spec = $this->_specificity("!attr", self::ORIG_AUTHOR);
                $styles[$id][$spec][] = $this->_parse_properties($str);
            }

            // Grab the applicable styles
            if (isset($styles[$id])) {

                /** @var array[][] $applied_styles */
                $applied_styles = $styles[$frame->get_id()];

                // Sort by specificity
                ksort($applied_styles);

                if ($DEBUGCSS) {
                    $debug_nodename = $frame->get_node()->nodeName;
                    print "<pre>\n[$debug_nodename\n";
                    foreach ($applied_styles as $spec => $arr) {
                        printf("specificity: 0x%08x\n", $spec);
                        /** @var Style $s */
                        foreach ($arr as $s) {
                            print "[\n";
                            $s->debug_print();
                            print "]\n";
                        }
                    }
                }

                // Merge the new styles with the inherited styles
                $acceptedmedia = self::$ACCEPTED_GENERIC_MEDIA_TYPES;
                $acceptedmedia[] = $this->_dompdf->getOptions()->getDefaultMediaType();
                foreach ($applied_styles as $arr) {
                    /** @var Style $s */
                    foreach ($arr as $s) {
                        $media_queries = $s->get_media_queries();
                        foreach ($media_queries as $media_query) {
                            list($media_query_feature, $media_query_value) = $media_query;
                            // if any of the Style's media queries fail then do not apply the style
                            //TODO: When the media query logic is fully developed we should not apply the Style when any of the media queries fail or are bad, per https://www.w3.org/TR/css3-mediaqueries/#error-handling
                            if (in_array($media_query_feature, self::$VALID_MEDIA_TYPES)) {
                                if ((strlen($media_query_feature) === 0 && !in_array($media_query, $acceptedmedia)) || (in_array($media_query, $acceptedmedia) && $media_query_value == "not")) {
                                    continue (3);
                                }
                            } else {
                                switch ($media_query_feature) {
                                    case "height":
                                        if ($paper_height !== (float)$style->length_in_pt($media_query_value)) {
                                            continue (3);
                                        }
                                        break;
                                    case "min-height":
                                        if ($paper_height < (float)$style->length_in_pt($media_query_value)) {
                                            continue (3);
                                        }
                                        break;
                                    case "max-height":
                                        if ($paper_height > (float)$style->length_in_pt($media_query_value)) {
                                            continue (3);
                                        }
                                        break;
                                    case "width":
                                        if ($paper_width !== (float)$style->length_in_pt($media_query_value)) {
                                            continue (3);
                                        }
                                        break;
                                    case "min-width":
                                        //if (min($paper_width, $media_query_width) === $paper_width) {
                                        if ($paper_width < (float)$style->length_in_pt($media_query_value)) {
                                            continue (3);
                                        }
                                        break;
                                    case "max-width":
                                        //if (max($paper_width, $media_query_width) === $paper_width) {
                                        if ($paper_width > (float)$style->length_in_pt($media_query_value)) {
                                            continue (3);
                                        }
                                        break;
                                    case "orientation":
                                        if ($paper_orientation !== $media_query_value) {
                                            continue (3);
                                        }
                                        break;
                                    default:
                                        Helpers::record_warnings(E_USER_WARNING, "Unknown media query: $media_query_feature", __FILE__, __LINE__);
                                        break;
                                }
                            }
                        }

                        $style->merge($s);
                    }
                }
            }

            // Inherit parent's styles if required
            if ($p) {

                if ($DEBUGCSS) {
                    print "inherit:\n";
                    print "[\n";
                    $p->get_style()->debug_print();
                    print "]\n";
                }

                $style->inherit($p->get_style());
            }

            if ($DEBUGCSS) {
                print "DomElementStyle:\n";
                print "[\n";
                $style->debug_print();
                print "]\n";
                print "/$debug_nodename]\n</pre>";
            }

            /*DEBUGCSS print: see below different print debugging method
            Helpers::pre_r($frame->get_node()->nodeName . ":");
            echo "<pre>";
            echo $style;
            echo "</pre>";*/
            $frame->set_style($style);

            if (!$root_flg && $this->_page_styles["base"]) {
                $root_flg = true;

                // set the page width, height, and orientation based on the parsed page style
                if ($style->size !== "auto") {
                    list($paper_width, $paper_height) = $style->size;
                }
                $paper_width = $paper_width - (float)$style->length_in_pt($style->margin_left) - (float)$style->length_in_pt($style->margin_right);
                $paper_height = $paper_height - (float)$style->length_in_pt($style->margin_top) - (float)$style->length_in_pt($style->margin_bottom);
                $paper_orientation = ($paper_width > $paper_height ? "landscape" : "portrait");
            }
        }

        // We're done!  Clean out the registry of all styles since we
        // won't be needing this later.
        foreach (array_keys($this->_styles) as $key) {
            $this->_styles[$key] = null;
            unset($this->_styles[$key]);
        }

    }
1120753
private function applyCurrentStyle($text) {
        return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
    }
1045118
public function setParentInstance(SiteTree $parent)
    {
        $this->parentInstance = $parent;
        
        $this->setStyleIDFrom($parent);
        
        return $this;
    }
22875
public function editBlockConfiguration(Tree $tree, int $block_id): void
    {
        $show_other      = $this->getBlockSetting($block_id, 'show_other', self::DEFAULT_SHOW_OTHER);
        $show_unassigned = $this->getBlockSetting($block_id, 'show_unassigned', self::DEFAULT_SHOW_UNASSIGNED);
        $show_future     = $this->getBlockSetting($block_id, 'show_future', self::DEFAULT_SHOW_FUTURE);

        echo view('modules/todo/config', [
            'show_future'     => $show_future,
            'show_other'      => $show_other,
            'show_unassigned' => $show_unassigned,
        ]);
    }
1223156
function add_frame(Frame $frame) {
    
    $style = $frame->get_style();
    $display = $style->display;

    $collapse = $this->_table->get_style()->border_collapse == "collapse";

    // Recursively add the frames within tables, table-row-groups and table-rows
    if ( $display === "table-row" ||
         $display === "table" ||
         $display === "inline-table" ||
         in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {

      $start_row = $this->__row;
      foreach ( $frame->get_children() as $child )
        $this->add_frame( $child );

      if ( $display === "table-row" )
        $this->add_row();

      $num_rows = $this->__row - $start_row - 1;
      $key = $frame->get_id();

      // Row groups always span across the entire table
      $this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1));
      $this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
      $this->_frames[$key]["frame"] = $frame;

      if ( $display !== "table-row" && $collapse ) {

        $bp = $style->get_border_properties();

        // Resolve the borders
        for ( $i = 0; $i < $num_rows+1; $i++) {
          $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
          $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
        }

        for ( $j = 0; $j < $this->_num_cols; $j++) {
          $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
          $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
        }
      }


      return;
    }
    
    $node = $frame->get_node();
    
    // Determine where this cell is going
    $colspan = $node->getAttribute("colspan");
    $rowspan = $node->getAttribute("rowspan");

    if ( !$colspan ) {
      $colspan = 1;
      $node->setAttribute("colspan",1);
    }

    if ( !$rowspan ) {
      $rowspan = 1;
      $node->setAttribute("rowspan",1);
    }
    $key = $frame->get_id();

    $bp = $style->get_border_properties();


    // Add the frame to the cellmap
    $max_left = $max_right = 0;

    // Find the next available column (fix by Ciro Mondueri)
    $ac = $this->__col;
    while ( isset($this->_cells[$this->__row][$ac]) )
       $ac++;
    $this->__col = $ac;

    // Rows:
    for ( $i = 0; $i < $rowspan; $i++ ) {
      $row = $this->__row + $i;

      $this->_frames[$key]["rows"][] = $row;

      for ( $j = 0; $j < $colspan; $j++)
        $this->_cells[$row][$this->__col + $j] = $frame;

      if ( $collapse ) {
        // Resolve vertical borders
        $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
        $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
      }
    }

    $max_top = $max_bottom = 0;

    // Columns:
    for ( $j = 0; $j < $colspan; $j++ ) {
      $col = $this->__col + $j;
      $this->_frames[$key]["columns"][] = $col;

      if ( $collapse ) {
        // Resolve horizontal borders
        $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
        $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
      }
    }

    $this->_frames[$key]["frame"] = $frame;

    // Handle seperated border model
    if ( !$collapse ) {
      list($h, $v) = $this->_table->get_style()->border_spacing;

      // Border spacing is effectively a margin between cells
      $v = $style->length_in_pt($v) / 2;
      $h = $style->length_in_pt($h) / 2;
      $style->margin = "$v $h";

      // The additional 1/2 width gets added to the table proper

    } else {

      // Drop the frame's actual border
      $style->border_left_width = $max_left / 2;
      $style->border_right_width = $max_right / 2;
      $style->border_top_width = $max_top / 2;
      $style->border_bottom_width = $max_bottom / 2;
      $style->margin = "none";
    }

    // Resolve the frame's width
    list($frame_min, $frame_max) = $frame->get_min_max_width();

    $width = $style->width;

    if ( is_percent($width) ) {
      $var = "percent";
      $val = (float)rtrim($width, "% ") / $colspan;

    } else if ( $width !== "auto" ) {
      $var = "absolute";
      $val = $style->length_in_pt($frame_min) / $colspan;
    }

    if (!$this->_columns_locked) {
      $min = 0;
      $max = 0;
      for ( $cs = 0; $cs < $colspan; $cs++ ) {
  
        // Resolve the frame's width(s) with other cells
        $col =& $this->get_column( $this->__col + $cs );
  
        // Note: $var is either 'percent' or 'absolute'.  We compare the
        // requested percentage or absolute values with the existing widths
        // and adjust accordingly.
        if ( isset($var) && $val > $col[$var] ) {
          $col[$var] = $val;
          $col["auto"] = false;
        }
  
        $min += $col["min-width"];
        $max += $col["max-width"];
      }
  
  
      if ( $frame_min > $min ) {
        // The frame needs more space.  Expand each sub-column
        $inc = ($frame_min - $min) / $colspan;
        for ($c = 0; $c < $colspan; $c++) {
          $col =& $this->get_column($this->__col + $c);
          $col["min-width"] += $inc;
        }
      }
  
      if ( $frame_max > $max ) {
        $inc = ($frame_max - $max) / $colspan;
        for ($c = 0; $c < $colspan; $c++) {
          $col =& $this->get_column($this->__col + $c);
          $col["max-width"] += $inc;
        }
      }
    }

    $this->__col += $colspan;
    if ( $this->__col > $this->_num_cols )
      $this->_num_cols = $this->__col;

  }
792999
protected function _set_content(){
    $frame = $this->_frame;
    $style = $frame->get_style();
  
    if ( $style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated" ) {
      $content = $this->_parse_content();
      $node = $frame->get_node()->ownerDocument->createTextNode($content);
      
      $new_style = $style->get_stylesheet()->create_style();
      $new_style->inherit($style);
      
      $new_frame = new Frame($node);
      $new_frame->set_style($new_style);
      
      Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf());
      $new_frame->get_decorator()->set_root($frame->get_root());
      $frame->append_child($new_frame);
    }
    
    if ( $style->counter_reset && ($reset = $style->counter_reset) !== "none" )
      $frame->reset_counter($reset);
    
    if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" )
      $frame->increment_counters($increment);
  }
917941
public function _displayTree($tree, $parent = NULL)
    {
        $outputList = TRUE;
        foreach ($tree as $name => $element) {
            if ($element->superclass() == $parent) {
                if ($outputList) echo "<ul>\n";
                echo '<li><a href="', str_repeat('../', $this->_depth), $element->asPath(), '">', $element->qualifiedName(), '</a>';
                $this->_displayTree($tree, $name);
                echo "</li>\n";
                $outputList = FALSE;
            }
        }
        if (!$outputList) echo "</ul>\n";
    }
725705
private function fillStyleAttributesWithMergedStyles()
    {
        foreach ($this->styleAttributesForNodes as $nodePath => $styleAttributesForNode) {
            $node = $this->visitedNodes[$nodePath];
            $currentStyleAttributes = $this->parseCssDeclarationsBlock($node->getAttribute('style'));
            $node->setAttribute(
                'style',
                $this->generateStyleStringFromDeclarationsArrays(
                    $currentStyleAttributes,
                    $styleAttributesForNode
                )
            );
        }
    }
1242558
private function makeNode(&$row, &$tree)
    {
        $hasChildren = $this->propertyAccessor->getValue($row, "[{$this->options['children_column']}]");
        $weight      = $this->propertyAccessor->getValue($row, "[{$this->options['hierarchy_column']}]");

        $row['hasChildren'] = (bool)($hasChildren > 0);
        $row['weight']      = $weight;
        $tree[$row['id']]   = $row;
    }
96302
public static function activeTree(NavigationTree $tree, Request $request) {
        self::activeNodes($request, $tree->getNodes());
    }
505989
function SetStyles($str)
	{
		$style = '';
		foreach (['B', 'I'] as $s) {
			if (strpos($str, $s) !== false) {
				$this->$s = true;
				$style .= $s;
			} else {
				$this->$s = false;
			}
		}
		$this->currentfontstyle = $style;
		$this->SetFont('', $style, 0, false);
	}
562948
protected function applyRule(ClassNode $node)
    {
        foreach ($node->getMethods() as $method) {
            $this->checkVisibility($method);
        }
    }
436331
public function apply(AbstractNode $node)
    {
        if (preg_match('(\\\\[^\\\\]+Controller$)', $node->getFullQualifiedName()) === 0) {
            return;
        }
        if ($node->getName() === 'AbstractController') {
            return;
        }

        /** @var \PHPMD\Node\ClassNode $node */
        foreach ($node->getMethods() as $method) {
            $this->applyPublicMethodsHaveActionSuffix($method);
        }
    }
851685
public function apply(AbstractNode $node)
    {
        $patterns = $this->collectPatterns($node);

        $this->applyPatterns($node, $patterns);

        /** @var \PHPMD\Node\AbstractTypeNode $node */
        foreach ($node->getMethods() as $method) {
            $this->applyPatterns(
                $method,
                $patterns
            );
        }
    }
924241
function(layerContext, node) {
        var style = {};
        this.runChildNodes(style, node);
        if(node.getAttribute("current") == "1") {
            style.current = true;
        }
        layerContext.styles.push(style);
    }
757923
function _applyStyles(context, attrs) {
    for (var attr in attrs) {
      if (attrs.hasOwnProperty(attr)) {
        context.style[attr] = attrs[attr];
      }
    }
  }
469039
public function editBlockConfiguration(Tree $tree, int $block_id): void
    {
        $num       = $this->getBlockSetting($block_id, 'num', self::DEFAULT_NUMBER);
        $infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE);

        $info_styles = [
            /* I18N: An option in a list-box */
            'list'     => I18N::translate('bullet list'),
            /* I18N: An option in a list-box */
            'array'    => I18N::translate('compact list'),
            /* I18N: An option in a list-box */
            'table'    => I18N::translate('table'),
            /* I18N: An option in a list-box */
            'tagcloud' => I18N::translate('tag cloud'),
        ];

        echo view('modules/top10_surnames/config', [
            'num'         => $num,
            'infoStyle'   => $infoStyle,
            'info_styles' => $info_styles,
        ]);
    }