self::ENGINE_EMOGRIFIER, 'cleanup' => true, 'responsive' => false, // If classes/ids should not be remove, only relevant for cleanup=>true 'useInlineStylesBlock' => true, 'debug' => false, // only cssToInline 'xhtmlOutput' => false, // only cssToInline 'removeCss' => true, // only cssToInline 'correctUtf8' => false // only cssToInline ]; public $config = []; /** * Inits with auto merged config. */ public function __construct($config = []) { $defaults = (array)Configure::read('InlineCss') + $this->_defaults; $this->config = $config + $defaults; if (!method_exists($this, '_process' . ucfirst($this->config['engine']))) { throw new InternalErrorException('Engine does not exist: ' . $this->config['engine']); } } /** * Processes HTML and CSS. * * @return string Result */ public function process($html, $css = null) { if (($html = trim($html)) === '') { return $html; } $method = '_process' . ucfirst($this->config['engine']); return $this->{$method}($html, $css); } /** * @return string Result */ protected function _processEmogrifier($html, $css) { if (class_exists('\Pelago\Emogrifier')) { $Emogrifier = new \Pelago\Emogrifier($html, $css); } else { App::import('Vendor', 'Tools.Emogrifier', ['file' => 'Emogrifier/Emogrifier.php']); $Emogrifier = new Emogrifier($html, $css); } //$Emogrifier->preserveEncoding = true; $result = $Emogrifier->emogrify(); if ($this->config['cleanup']) { // Remove comments and whitespace $result = preg_replace('//', '', $result); //$result = preg_replace( '/\s\s+/', '\s', $result); // Result classes and ids if (!$this->config['responsive']) { $result = preg_replace('/\bclass="[^"]*"/', '', $result); $result = preg_replace('/\bid="[^"]*"/', '', $result); } } return $result; } /** * Process css blocks to inline css * Also works for html snippets (without ) * * @return string HTML output */ protected function _processCssToInline($html, $css) { App::import('Vendor', 'Tools.CssToInlineStyles', ['file' => 'CssToInlineStyles' . DS . 'CssToInlineStyles.php']); //fix issue with being added $separator = '~~~~~~~~~~~~~~~~~~~~'; if (strpos($html, 'config['cleanup']) { $CssToInlineStyles->setCleanup(); } if ($this->config['useInlineStylesBlock']) { $CssToInlineStyles->setUseInlineStylesBlock(); } if ($this->config['removeCss']) { $CssToInlineStyles->setStripOriginalStyleTags(); } if ($this->config['correctUtf8']) { $CssToInlineStyles->setCorrectUtf8(); } if ($this->config['debug']) { CakeLog::write('css', $html); } $html = $CssToInlineStyles->convert($this->config['xhtmlOutput']); if ($this->config['removeCss']) { //$html = preg_replace('/\(.*)\<\/style\>/i', '', $html); $html = $this->stripOnly($html, ['style', 'script'], true); //CakeLog::write('css', $html); } if (!empty($incomplete)) { $html = substr($html, strpos($html, $separator) + 20); $html = substr($html, 0, strpos($html, $separator)); $html = trim($html); } return $html; } /** * Some reverse function of strip_tags with blacklisting instead of whitelisting * //maybe move to Tools.Utility/String/Text? * * @return string cleanedStr */ public function stripOnly($str, $tags, $stripContent = false) { $content = ''; if (!is_array($tags)) { $tags = (strpos($str, '>') !== false ? explode('>', str_replace('<', '', $tags)) : [$tags]); if (end($tags) === '') { array_pop($tags); } } foreach ($tags as $tag) { if ($stripContent) { $content = '(.+]*>|)'; } $str = preg_replace('#]*>' . $content . '#is', '', $str); } return $str; } /** * _extractAndRemoveCss - extracts any CSS from the rendered view and * removes it from the $html * * @return string */ protected function _extractAndRemoveCss($html) { $css = null; $DOM = new DOMDocument(); $DOM->loadHTML($html); // DOM removal queue $removeDoms = []; // catch style sheet content $links = $DOM->getElementsByTagName('link'); foreach ($links as $link) { if ($link->hasAttribute('href') && preg_match("/\.css$/i", $link->getAttribute('href'))) { // find the css file and load contents if ($link->hasAttribute('media')) { // FOR NOW continue; foreach ($this->mediaTypes as $cssLinkMedia) { if (strstr($link->getAttribute('media'), $cssLinkMedia)) { $css .= $this->_findAndLoadCssFile($link->getAttribute('href')) . "\n\n"; $removeDoms[] = $link; } } } else { $css .= $this->_findAndLoadCssFile($link->getAttribute('href')) . "\n\n"; $removeDoms[] = $link; } } } // Catch embeded