/** * Converts the loaded HTML into an HTML-string with inline styles based on the loaded CSS * * @return string * @param bool [optional] $outputXHTML Should we output valid XHTML? */ public function convert($outputXHTML = false) { // redefine $outputXHTML = (bool) $outputXHTML; // validate if ($this->html == null) { throw new Exception('No HTML provided.'); } // should we use inline style-block if ($this->useInlineStylesBlock) { // init var $matches = array(); // match the style blocks preg_match_all('|<style(.*)>(.*)</style>|isU', $this->html, $matches); // any style-blocks found? if (!empty($matches[2])) { // add foreach ($matches[2] as $match) { $this->css .= trim($match) . "\n"; } } } // process css $cssRules = $this->processCSS(); // create new DOMDocument $document = new \DOMDocument('1.0', $this->getEncoding()); // set error level $internalErrors = libxml_use_internal_errors(true); // load HTML $document->loadHTML($this->html); // Restore error level libxml_use_internal_errors($internalErrors); // create new XPath $xPath = new \DOMXPath($document); // any rules? if (!empty($cssRules)) { // loop rules foreach ($cssRules as $rule) { $selector = new Selector($rule['selector']); $query = $selector->toXPath(); if (is_null($query)) { continue; } // search elements $elements = $xPath->query($query); // validate elements if ($elements === false) { continue; } // loop found elements foreach ($elements as $element) { // no styles stored? if ($element->attributes->getNamedItem('data-css-to-inline-styles-original-styles') == null) { // init var $originalStyle = ''; if ($element->attributes->getNamedItem('style') !== null) { $originalStyle = $element->attributes->getNamedItem('style')->value; } // store original styles $element->setAttribute('data-css-to-inline-styles-original-styles', $originalStyle); // clear the styles $element->setAttribute('style', ''); } // init var $properties = array(); // get current styles $stylesAttribute = $element->attributes->getNamedItem('style'); // any styles defined before? if ($stylesAttribute !== null) { // get value for the styles attribute $definedStyles = (string) $stylesAttribute->value; // split into properties $definedProperties = $this->splitIntoProperties($definedStyles); // loop properties foreach ($definedProperties as $property) { // validate property if ($property == '') { continue; } // split into chunks $chunks = (array) explode(':', trim($property), 2); // validate if (!isset($chunks[1])) { continue; } // loop chunks $properties[$chunks[0]] = trim($chunks[1]); } } // add new properties into the list foreach ($rule['properties'] as $key => $value) { // If one of the rules is already set and is !important, don't apply it, // except if the new rule is also important. if (!isset($properties[$key]) || stristr($properties[$key], '!important') === false || stristr(implode('', $value), '!important') !== false) { $properties[$key] = $value; } } // build string $propertyChunks = array(); // build chunks foreach ($properties as $key => $values) { foreach ((array) $values as $value) { $propertyChunks[] = $key . ': ' . $value . ';'; } } // build properties string $propertiesString = implode(' ', $propertyChunks); // set attribute if ($propertiesString != '') { $element->setAttribute('style', $propertiesString); } } } // reapply original styles // search elements $elements = $xPath->query('//*[@data-css-to-inline-styles-original-styles]'); // loop found elements foreach ($elements as $element) { // get the original styles $originalStyle = $element->attributes->getNamedItem('data-css-to-inline-styles-original-styles')->value; if ($originalStyle != '') { $originalProperties = array(); $originalStyles = $this->splitIntoProperties($originalStyle); foreach ($originalStyles as $property) { // validate property if ($property == '') { continue; } // split into chunks $chunks = (array) explode(':', trim($property), 2); // validate if (!isset($chunks[1])) { continue; } // loop chunks $originalProperties[$chunks[0]] = trim($chunks[1]); } // get current styles $stylesAttribute = $element->attributes->getNamedItem('style'); $properties = array(); // any styles defined before? if ($stylesAttribute !== null) { // get value for the styles attribute $definedStyles = (string) $stylesAttribute->value; // split into properties $definedProperties = $this->splitIntoProperties($definedStyles); // loop properties foreach ($definedProperties as $property) { // validate property if ($property == '') { continue; } // split into chunks $chunks = (array) explode(':', trim($property), 2); // validate if (!isset($chunks[1])) { continue; } // loop chunks $properties[$chunks[0]] = trim($chunks[1]); } } // add new properties into the list foreach ($originalProperties as $key => $value) { $properties[$key] = $value; } // build string $propertyChunks = array(); // build chunks foreach ($properties as $key => $values) { foreach ((array) $values as $value) { $propertyChunks[] = $key . ': ' . $value . ';'; } } // build properties string $propertiesString = implode(' ', $propertyChunks); // set attribute if ($propertiesString != '') { $element->setAttribute('style', $propertiesString); } } // remove placeholder $element->removeAttribute('data-css-to-inline-styles-original-styles'); } } // strip original style tags if we need to if ($this->stripOriginalStyleTags) { $this->stripOriginalStyleTags($xPath); } // cleanup the HTML if we need to if ($this->cleanup) { $this->cleanupHTML($xPath); } // should we output XHTML? if ($outputXHTML) { // set formating $document->formatOutput = true; // get the HTML as XML $html = $document->saveXML(null, LIBXML_NOEMPTYTAG); // remove the XML-header $html = ltrim(preg_replace('/<\\?xml (.*)\\?>/', '', $html)); } else { // get the HTML $html = $document->saveHTML(); } // return return $html; }