Completed
Push — master ( 2ccc3e...bf7d5b )
by Lars
03:43
created

putReplacedAmperstampBackToPreserveHtmlEntities()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
namespace voku\CssToInlineStyles;
3
4
use Symfony\Component\CssSelector\CssSelectorConverter;
5
use Symfony\Component\CssSelector\Exception\ExceptionInterface;
6
7
/**
8
 * CSS to Inline Styles class
9
 *
10
 * @author     Tijs Verkoyen <[email protected]>
11
 */
12
class CssToInlineStyles
13
{
14
15
  /**
16
   * regular expression: css media queries
17
   *
18
   * @var string
19
   */
20
  private static $cssMediaQueriesRegEx = '#@media\\s+(?:only\\s)?(?:[\\s{\\(]|screen|all)\\s?[^{]+{.*}\\s*}\\s*#misU';
21
22
  /**
23
   * regular expression: css charset
24
   *
25
   * @var string
26
   */
27
  private static $cssCharsetRegEx = '/@charset [\'"][^\'"]+[\'"];/i';
28
29
30
  /**
31
   * regular expression: conditional inline style tags
32
   *
33
   * @var string
34
   */
35
  private static $excludeConditionalInlineStylesBlockRegEx = '/<!--.*<style.*-->/isU';
36
37
  /**
38
   * regular expression: inline style tags
39
   *
40
   * @var string
41
   */
42
  private static $styleTagRegEx = '|<style(.*)>(.*)</style>|isU';
43
44
  /**
45
   * regular expression: css-comments
46
   *
47
   * @var string
48
   */
49
  private static $styleCommentRegEx = '/\\/\\*.*\\*\\//sU';
50
51
  /**
52
   * The CSS to use
53
   *
54
   * @var  string
55
   */
56
  private $css;
57
58
  /**
59
   * Should the generated HTML be cleaned
60
   *
61
   * @var  bool
62
   */
63
  private $cleanup = false;
64
65
  /**
66
   * The encoding to use.
67
   *
68
   * @var  string
69
   */
70
  private $encoding = 'UTF-8';
71
72
  /**
73
   * The HTML to process
74
   *
75
   * @var  string
76
   */
77
  private $html;
78
79
  /**
80
   * Use inline-styles block as CSS
81
   *
82
   * @var bool
83
   */
84
  private $useInlineStylesBlock = false;
85
86
  /**
87
   * Use link block reference as CSS
88
   *
89
   * @var bool
90
   */
91
  private $loadCSSFromHTML = false;
92
93
  /**
94
   * Strip original style tags
95
   *
96
   * @var bool
97
   */
98
  private $stripOriginalStyleTags = false;
99
100
  /**
101
   * Exclude conditional inline-style blocks
102
   *
103
   * @var bool
104
   */
105
  private $excludeConditionalInlineStylesBlock = true;
106
107
  /**
108
   * Exclude media queries from "$this->css" and keep media queries for inline-styles blocks
109
   *
110
   * @var bool
111
   */
112
  private $excludeMediaQueries = true;
113
114
  /**
115
   * Exclude media queries from "$this->css" and keep media queries for inline-styles blocks
116
   *
117
   * @var bool
118
   */
119
  private $excludeCssCharset = true;
120
121
  /**
122
   * Creates an instance, you could set the HTML and CSS here, or load it
123
   * later.
124
   *
125
   * @param  null|string $html The HTML to process.
126
   * @param  null|string $css The CSS to use.
127
   */
128 47
  public function __construct($html = null, $css = null)
129
  {
130 47
    if (null !== $html) {
131 2
      $this->setHTML($html);
132 2
    }
133
134 47
    if (null !== $css) {
135 2
      $this->setCSS($css);
136 2
    }
137 47
  }
138
139
  /**
140
   * Set HTML to process
141
   *
142
   * @param  string $html The HTML to process.
143
   */
144 45
  public function setHTML($html)
145
  {
146
    // strip style definitions, if we use css-class "cleanup" on a style-element
147 45
    $this->html = (string)preg_replace('/<style[^>]+class="cleanup"[^>]*>.*<\/style>/Usi', ' ', $html);
148 45
  }
149
150
  /**
151
   * Set CSS to use
152
   *
153
   * @param  string $css The CSS to use.
154
   */
155 43
  public function setCSS($css)
156
  {
157 43
    $this->css = (string)$css;
158 43
  }
159
160
  /**
161
   * Sort an array on the specificity element
162
   *
163
   * @return int
164
   *
165
   * @param Specificity[] $e1 The first element.
166
   * @param Specificity[] $e2 The second element.
167
   */
168 16
  private static function sortOnSpecificity($e1, $e2)
169
  {
170
    // Compare the specificity
171 16
    $value = $e1['specificity']->compareTo($e2['specificity']);
172
173
    // if the specificity is the same, use the order in which the element appeared
174 16
    if (0 === $value) {
175 11
      $value = $e1['order'] - $e2['order'];
176 11
    }
177
178 16
    return $value;
179
  }
180
181
  /**
182
   * Converts the loaded HTML into an HTML-string with inline styles based on the loaded CSS
183
   *
184
   * @param bool $outputXHTML [optional] Should we output valid XHTML?
185
   * @param int $libXMLOptions [optional] $libXMLOptions Since PHP 5.4.0 and Libxml 2.6.0,
186
   *                                                      you may also use the options parameter to specify additional
187
   *                                                      Libxml parameters. Recommend these options:
188
   *                                                      LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
189
   * @param bool $path [optional] Set the path to your external css-files.
190
   *
191
   * @return string
192
   *
193
   * @throws Exception
194
   */
195 45
  public function convert($outputXHTML = false, $libXMLOptions = 0, $path = false)
196
  {
197
    // redefine
198 45
    $outputXHTML = (bool)$outputXHTML;
199
200
    // validate
201 45
    if (!$this->html) {
202 1
      throw new Exception('No HTML provided.');
203
    }
204
205
    // use local variables
206 44
    $css = $this->css;
207
208
    // create new DOMDocument
209 44
    $document = $this->createDOMDocument($this->html, $libXMLOptions);
210
211
    // check if there is some link css reference
212 44
    if ($this->loadCSSFromHTML) {
213 1
      foreach ($document->getElementsByTagName('link') as $node) {
214
215
        /** @noinspection PhpUndefinedMethodInspection */
216 1
        $file = ($path ?: __DIR__) . '/' . $node->getAttribute('href');
217
218 1
        if (file_exists($file)) {
219 1
          $css .= file_get_contents($file);
220
221
          // converting to inline css because we don't need/want to load css files, so remove the link
222 1
          $node->parentNode->removeChild($node);
223 1
        }
224 1
      }
225 1
    }
226
227
    // should we use inline style-block
228 44
    if ($this->useInlineStylesBlock) {
229
230 26
      if (true === $this->excludeConditionalInlineStylesBlock) {
231 22
        $this->html = preg_replace(self::$excludeConditionalInlineStylesBlockRegEx, '', $this->html);
232 22
      }
233
234 26
      $css .= $this->getCssFromInlineHtmlStyleBlock($this->html);
235 26
    }
236
237
    // process css
238 44
    $cssRules = $this->processCSS($css);
239
240
    // create new XPath
241 44
    $xPath = $this->createXPath($document, $cssRules);
242
243
    // strip original style tags if we need to
244 44
    if ($this->stripOriginalStyleTags === true) {
245 13
      $this->stripOriginalStyleTags($xPath);
246 13
    }
247
248
    // cleanup the HTML if we need to
249 44
    if (true === $this->cleanup) {
250 3
      $this->cleanupHTML($xPath);
251 3
    }
252
253
    // should we output XHTML?
254 44
    if (true === $outputXHTML) {
255
      // set formatting
256 6
      $document->formatOutput = true;
257
258
      // get the HTML as XML
259 6
      $xml = $document->saveXML(null, LIBXML_NOEMPTYTAG);
260 6
      $xml = $this->putReplacedAmperstampBackToPreserveHtmlEntities($xml);
261
262
      // remove the XML-header
263 6
      return ltrim(preg_replace('/<\?xml.*\?>/', '', $xml));
264
    }
265
266
    // just regular HTML 4.01 as it should be used in newsletters
267 39
    $html =  $document->saveHTML();
268 39
    $html = $this->putReplacedAmperstampBackToPreserveHtmlEntities($html);
269
270 39
    return $html;
271
  }
272
273
  /**
274
   * get css from inline-html style-block
275
   *
276
   * @param string $html
277
   *
278
   * @return string
279
   */
280 28
  public function getCssFromInlineHtmlStyleBlock($html)
281
  {
282
    // init var
283 28
    $css = '';
284 28
    $matches = array();
285
286
    // match the style blocks
287 28
    preg_match_all(self::$styleTagRegEx, $html, $matches);
288
289
    // any style-blocks found?
290 28
    if (!empty($matches[2])) {
291
      // add
292 27
      foreach ($matches[2] as $match) {
293 27
        $css .= trim($match) . "\n";
294 27
      }
295 27
    }
296
297 28
    return $css;
298
  }
299
300
  /**
301
   * Process the loaded CSS
302
   *
303
   * @param $css
304
   *
305
   * @return array
306
   */
307 44
  private function processCSS($css)
308
  {
309
    //reset current set of rules
310 44
    $cssRules = array();
311
312
    // init vars
313 44
    $css = (string)$css;
314
315 44
    $css = $this->doCleanup($css);
316
317
    // rules are splitted by }
318 44
    $rules = (array)explode('}', $css);
319
320
    // init var
321 44
    $i = 1;
322
323
    // loop rules
324 44
    foreach ($rules as $rule) {
325
      // split into chunks
326 44
      $chunks = explode('{', $rule);
327
328
      // invalid rule?
329 44
      if (!isset($chunks[1])) {
330 44
        continue;
331
      }
332
333
      // set the selectors
334 33
      $selectors = trim($chunks[0]);
335
336
      // get cssProperties
337 33
      $cssProperties = trim($chunks[1]);
338
339
      // split multiple selectors
340 33
      $selectors = (array)explode(',', $selectors);
341
342
      // loop selectors
343 33
      foreach ($selectors as $selector) {
344
        // cleanup
345 33
        $selector = trim($selector);
346
347
        // build an array for each selector
348 33
        $ruleSet = array();
349
350
        // store selector
351 33
        $ruleSet['selector'] = $selector;
352
353
        // process the properties
354 33
        $ruleSet['properties'] = $this->processCSSProperties($cssProperties);
355
356
357
        // calculate specificity
358 33
        $ruleSet['specificity'] = Specificity::fromSelector($selector);
359
360
        // remember the order in which the rules appear
361 33
        $ruleSet['order'] = $i;
362
363
        // add into rules
364 33
        $cssRules[] = $ruleSet;
365
366
        // increment
367 33
        $i++;
368 33
      }
369 44
    }
370
371
    // sort based on specificity
372 44
    if (0 !== count($cssRules)) {
373 33
      usort($cssRules, array(__CLASS__, 'sortOnSpecificity'));
374 33
    }
375
376 44
    return $cssRules;
377
  }
378
379
  /**
380
   * @param string $css
381
   *
382
   * @return string
383
   */
384 44
  private function doCleanup($css)
385
  {
386
    // remove newlines & replace double quotes by single quotes
387 44
    $css = str_replace(
388 44
        array("\r", "\n", '"'),
389 44
        array('', '', '\''),
390
        $css
391 44
    );
392
393
    // remove comments
394 44
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
395
396
    // remove spaces
397 44
    $css = preg_replace('/\s\s+/', ' ', $css);
398
399
    // remove css charset
400 44
    if (true === $this->excludeCssCharset) {
401 44
      $css = $this->stripeCharsetInCss($css);
402 44
    }
403
404
    // remove css media queries
405 44
    if (true === $this->excludeMediaQueries) {
406 43
      $css = $this->stripeMediaQueries($css);
407 43
    }
408
409 44
    return (string)$css;
410
  }
411
412
  /**
413
   * remove css media queries from the string
414
   *
415
   * @param string $css
416
   *
417
   * @return string
418
   */
419 43
  private function stripeMediaQueries($css)
420
  {
421
    // remove comments previously to matching media queries
422 43
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
423
424 43
    return (string)preg_replace(self::$cssMediaQueriesRegEx, '', $css);
425
  }
426
427
  /**
428
   * remove charset from the string
429
   *
430
   * @param $css
431
   *
432
   * @return string
433
   */
434 44
  private function stripeCharsetInCss($css)
435
  {
436 44
    return (string)preg_replace(self::$cssCharsetRegEx, '', $css);
437
  }
438
439
  /**
440
   * Process the CSS-properties
441
   *
442
   * @return array
443
   *
444
   * @param  string $propertyString The CSS-properties.
445
   */
446 33
  private function processCSSProperties($propertyString)
447
  {
448
    // split into chunks
449 33
    $properties = $this->splitIntoProperties($propertyString);
450
451
    // init var
452 33
    $pairs = array();
453
454
    // loop properties
455 33
    foreach ($properties as $property) {
456
      // split into chunks
457 33
      $chunks = (array)explode(':', $property, 2);
458
459
      // validate
460 33
      if (!isset($chunks[1])) {
461 27
        continue;
462
      }
463
464
      // cleanup
465 32
      $chunks[0] = trim($chunks[0]);
466 32
      $chunks[1] = trim($chunks[1]);
467
468
      // add to pairs array
469
      if (
470 32
          !isset($pairs[$chunks[0]])
471 32
          ||
472 3
          !in_array($chunks[1], $pairs[$chunks[0]], true)
473 32
      ) {
474 32
        $pairs[$chunks[0]][] = $chunks[1];
475 32
      }
476 33
    }
477
478
    // sort the pairs
479 33
    ksort($pairs);
480
481
    // return
482 33
    return $pairs;
483
  }
484
485
  /**
486
   * Split a style string into an array of properties.
487
   * The returned array can contain empty strings.
488
   *
489
   * @param string $styles ex: 'color:blue;font-size:12px;'
490
   *
491
   * @return array an array of strings containing css property ex: array('color:blue','font-size:12px')
492
   */
493 33
  private function splitIntoProperties($styles)
494
  {
495 33
    $properties = (array)explode(';', $styles);
496 33
    $propertiesCount = count($properties);
497
498
    /** @noinspection ForeachInvariantsInspection */
499 33
    for ($i = 0; $i < $propertiesCount; $i++) {
500
      // If next property begins with base64,
501
      // Then the ';' was part of this property (and we should not have split on it).
502
      if (
503 33
          isset($properties[$i + 1])
504 33
          &&
505 26
          strpos($properties[$i + 1], 'base64,') !== false
506 33
      ) {
507 1
        $properties[$i] .= ';' . $properties[$i + 1];
508 1
        $properties[$i + 1] = '';
509 1
        ++$i;
510 1
      }
511 33
    }
512
513 33
    return $properties;
514
  }
515
516
  /**
517
   * create DOMDocument from HTML
518
   *
519
   * @param string $html
520
   * @param int $libXMLOptions
521
   *
522
   * @return \DOMDocument
523
   */
524 44
  private function createDOMDocument($html, $libXMLOptions = 0)
525
  {
526
    // create new DOMDocument
527 44
    $document = new \DOMDocument('1.0', $this->getEncoding());
528
529
    // DOMDocument settings
530 44
    $document->preserveWhiteSpace = false;
531 44
    $document->formatOutput = true;
532
533
    // set error level
534 44
    $internalErrors = libxml_use_internal_errors(true);
535
536 44
    $html = $this->replaceAmperstampToPreserveHtmlEntities($html);
537
538
    // UTF-8 hack: http://php.net/manual/en/domdocument.loadhtml.php#95251
539 44
    $html = trim($html);
540 44
    $xmlHackUsed = false;
541 44
    if (stripos('<?xml', $html) !== 0) {
542 44
      $xmlHackUsed = true;
543 44
      $html = '<?xml encoding="' . $this->getEncoding() . '" ?>' . $html;
544 44
    }
545
546
    // load HTML
547 44
    if ($libXMLOptions !== 0) {
548
      $document->loadHTML($html, $libXMLOptions);
549
    } else {
550 44
      $document->loadHTML($html);
551
    }
552
553
    // remove the "xml-encoding" hack
554 44
    if ($xmlHackUsed === true) {
555 44
      foreach ($document->childNodes as $child) {
556 44
        if ($child->nodeType == XML_PI_NODE) {
557 44
          $document->removeChild($child);
558 44
        }
559 44
      }
560 44
    }
561
562
    // set encoding
563 44
    $document->encoding = $this->getEncoding();
564
565
    // restore error level
566 44
    libxml_use_internal_errors($internalErrors);
567
568 44
    return $document;
569
  }
570
571
  /**
572
   * Get the encoding to use
573
   *
574
   * @return string
575
   */
576 44
  private function getEncoding()
577
  {
578 44
    return $this->encoding;
579
  }
580
581
  /**
582
   * create XPath
583
   *
584
   * @param \DOMDocument $document
585
   * @param array $cssRules
586
   *
587
   * @return \DOMXPath
588
   */
589 44
  private function createXPath(\DOMDocument $document, array $cssRules)
590
  {
591 44
    $xPath = new \DOMXPath($document);
592
593
    // any rules?
594 44
    if (0 !== count($cssRules)) {
595
      // loop rules
596 33
      foreach ($cssRules as $rule) {
597
598
        try {
599 33
          $converter = new CssSelectorConverter();
600 33
          $query = $converter->toXPath($rule['selector']);
601 33
        } catch (ExceptionInterface $e) {
602 4
          $query = null;
603
        }
604 33
        $converter = null;
605
606
        // validate query
607 33
        if (null === $query) {
608 4
          continue;
609
        }
610
611
        // search elements
612 31
        $elements = $xPath->query($query);
613
614
        // validate elements
615 31
        if (false === $elements) {
616
          continue;
617
        }
618
619
        // loop found elements
620 31
        foreach ($elements as $element) {
621
622
          /**
623
           * @var $element \DOMElement
624
           */
625
626
          // no styles stored?
627 31
          if (null === $element->attributes->getNamedItem('data-css-to-inline-styles-original-styles')) {
628
629
            // init var
630 31
            $originalStyle = '';
631
632 31
            if (null !== $element->attributes->getNamedItem('style')) {
633
              /** @noinspection PhpUndefinedFieldInspection */
634 8
              $originalStyle = $element->attributes->getNamedItem('style')->value;
635 8
            }
636
637
            // store original styles
638 31
            $element->setAttribute('data-css-to-inline-styles-original-styles', $originalStyle);
639
640
            // clear the styles
641 31
            $element->setAttribute('style', '');
642 31
          }
643
644 31
          $propertiesString = $this->createPropertyChunks($element, $rule['properties']);
645
646
          // set attribute
647 31
          if ('' != $propertiesString) {
648 31
            $element->setAttribute('style', $propertiesString);
649 31
          }
650 31
        }
651 33
      }
652
653
      // reapply original styles
654
      // search elements
655 33
      $elements = $xPath->query('//*[@data-css-to-inline-styles-original-styles]');
656
657
      // loop found elements
658 33
      foreach ($elements as $element) {
659
        // get the original styles
660
        /** @noinspection PhpUndefinedFieldInspection */
661 31
        $originalStyle = $element->attributes->getNamedItem('data-css-to-inline-styles-original-styles')->value;
662
663 31
        if ('' != $originalStyle) {
664 8
          $originalStyles = $this->splitIntoProperties($originalStyle);
665
666 8
          $originalProperties = $this->splitStyleIntoChunks($originalStyles);
667
668 8
          $propertiesString = $this->createPropertyChunks($element, $originalProperties);
669
670
          // set attribute
671 8
          if ('' != $propertiesString) {
672 8
            $element->setAttribute('style', $propertiesString);
673 8
          }
674 8
        }
675
676
        // remove placeholder
677 31
        $element->removeAttribute('data-css-to-inline-styles-original-styles');
678 33
      }
679 33
    }
680
681 44
    return $xPath;
682
  }
683
684
  /**
685
   * @param \DOMElement $element
686
   * @param array $ruleProperties
687
   *
688
   * @return array
689
   */
690 31
  private function createPropertyChunks(\DOMElement $element, array $ruleProperties)
691
  {
692
    // init var
693 31
    $properties = array();
694
695
    // get current styles
696 31
    $stylesAttribute = $element->attributes->getNamedItem('style');
697
698
    // any styles defined before?
699 31
    if (null !== $stylesAttribute) {
700
      // get value for the styles attribute
701
      /** @noinspection PhpUndefinedFieldInspection */
702 31
      $definedStyles = (string)$stylesAttribute->value;
703
704
      // split into properties
705 31
      $definedProperties = $this->splitIntoProperties($definedStyles);
706
707 31
      $properties = $this->splitStyleIntoChunks($definedProperties);
708 31
    }
709
710
    // add new properties into the list
711 31
    foreach ($ruleProperties as $key => $value) {
712
      // If one of the rules is already set and is !important, don't apply it,
713
      // except if the new rule is also important.
714
      if (
715 31
          !isset($properties[$key])
716 31
          ||
717 9
          false === stripos($properties[$key], '!important')
718 9
          ||
719 4
          false !== stripos(implode('', (array)$value), '!important')
720 31
      ) {
721 31
        $properties[$key] = $value;
722 31
      }
723 31
    }
724
725
    // build string
726 31
    $propertyChunks = array();
727
728
    // build chunks
729 31
    foreach ($properties as $key => $values) {
730 31
      foreach ((array)$values as $value) {
731 31
        $propertyChunks[] = $key . ': ' . $value . ';';
732 31
      }
733 31
    }
734
735 31
    return implode(' ', $propertyChunks);
736
  }
737
738
  /**
739
   * @param array $definedProperties
740
   *
741
   * @return array
742
   */
743 31
  private function splitStyleIntoChunks(array $definedProperties)
744
  {
745
    // init var
746 31
    $properties = array();
747
748
    // loop properties
749 31
    foreach ($definedProperties as $property) {
750
      // validate property
751
      if (
752
          !$property
753 31
          ||
754 16
          strpos($property, ':') === false
755 31
      ) {
756 31
        continue;
757
      }
758
759
      // split into chunks
760 16
      $chunks = (array)explode(':', trim($property), 2);
761
762
      // validate
763 16
      if (!isset($chunks[1])) {
764
        continue;
765
      }
766
767
      // loop chunks
768 16
      $properties[$chunks[0]] = trim($chunks[1]);
769 31
    }
770
771 31
    return $properties;
772
  }
773
774
  /**
775
   * Strip style tags into the generated HTML
776
   *
777
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
778
   *
779
   * @return string
780
   */
781 13
  private function stripOriginalStyleTags(\DOMXPath $xPath)
782
  {
783
    // get all style tags
784 13
    $nodes = $xPath->query('descendant-or-self::style');
785 13
    foreach ($nodes as $node) {
786 12
      if ($this->excludeMediaQueries === true) {
787
788
        // remove comments previously to matching media queries
789 11
        $node->nodeValue = preg_replace(self::$styleCommentRegEx, '', $node->nodeValue);
790
791
        // search for Media Queries
792 11
        preg_match_all(self::$cssMediaQueriesRegEx, $node->nodeValue, $mqs);
793
794
        // replace the nodeValue with just the Media Queries
795 11
        $node->nodeValue = implode("\n", $mqs[0]);
796
797 11
      } else {
798
        // remove the entire style tag
799 1
        $node->parentNode->removeChild($node);
800
      }
801 13
    }
802 13
  }
803
804
  /**
805
   * Remove id and class attributes.
806
   *
807
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
808
   *
809
   * @return string
810
   */
811 3
  private function cleanupHTML(\DOMXPath $xPath)
812
  {
813 3
    $nodes = $xPath->query('//@class | //@id');
814 3
    foreach ($nodes as $node) {
815 3
      $node->ownerElement->removeAttributeNode($node);
816 3
    }
817 3
  }
818
819
  /**
820
   * Should the IDs and classes be removed?
821
   *
822
   * @param  bool $on Should we enable cleanup?
823
   */
824 3
  public function setCleanup($on = true)
825
  {
826 3
    $this->cleanup = (bool)$on;
827 3
  }
828
829
  /**
830
   * Set the encoding to use with the DOMDocument
831
   *
832
   * @param  string $encoding The encoding to use.
833
   *
834
   * @deprecated Doesn't have any effect
835
   */
836
  public function setEncoding($encoding)
837
  {
838
    $this->encoding = (string)$encoding;
839
  }
840
841
  /**
842
   * Set use of inline styles block
843
   * If this is enabled the class will use the style-block in the HTML.
844
   *
845
   * @param  bool $on Should we process inline styles?
846
   */
847 26
  public function setUseInlineStylesBlock($on = true)
848
  {
849 26
    $this->useInlineStylesBlock = (bool)$on;
850 26
  }
851
852
  /**
853
   * Set use of inline link block
854
   * If this is enabled the class will use the links reference in the HTML.
855
   *
856
   * @return void
857
   *
858
   * @param  bool [optional] $on Should we process link styles?
859
   */
860 2
  public function setLoadCSSFromHTML($on = true)
861
  {
862 2
    $this->loadCSSFromHTML = (bool)$on;
863 2
  }
864
865
  /**
866
   * Set strip original style tags
867
   * If this is enabled the class will remove all style tags in the HTML.
868
   *
869
   * @param  bool $on Should we process inline styles?
870
   */
871 17
  public function setStripOriginalStyleTags($on = true)
872
  {
873 17
    $this->stripOriginalStyleTags = (bool)$on;
874 17
  }
875
876
  /**
877
   * Set exclude media queries
878
   *
879
   * If this is enabled the media queries will be removed before inlining the rules.
880
   *
881
   * WARNING: If you use inline styles block "<style>" the this option will keep the media queries.
882
   *
883
   * @param bool $on
884
   */
885 14
  public function setExcludeMediaQueries($on = true)
886
  {
887 14
    $this->excludeMediaQueries = (bool)$on;
888 14
  }
889
890
  /**
891
   * Set exclude charset
892
   *
893
   * @param bool $on
894
   */
895 1
  public function setExcludeCssCharset($on = true)
896
  {
897 1
    $this->excludeCssCharset = (bool)$on;
898 1
  }
899
900
  /**
901
   * Set exclude conditional inline-style blocks e.g.: <!--[if gte mso 9]><style>.foo { bar } </style><![endif]-->
902
   *
903
   * @param bool $on
904
   */
905 6
  public function setExcludeConditionalInlineStylesBlock($on = true)
906
  {
907 6
    $this->excludeConditionalInlineStylesBlock = (bool)$on;
908 6
  }
909
910
  /**
911
   * @param string $html
912
   * @return string
913
   */
914 44
  private function replaceAmperstampToPreserveHtmlEntities($html)
915
  {
916 44
    return str_replace(array('&',), array('!!!!AMP!!!!'), $html);
917
  }
918
919
  /**
920
   * @param string $html
921
   * @return string
922
   */
923 44
  private function putReplacedAmperstampBackToPreserveHtmlEntities($html)
924
  {
925 44
    return str_replace(array('!!!!AMP!!!!', '&#13;'), array('&', ''), $html);
926
  }
927
}
928