@@ -7,65 +7,65 @@ |
||
7 | 7 | |
8 | 8 | class Processor |
9 | 9 | { |
10 | - /** |
|
11 | - * Get the rules from a given CSS-string |
|
12 | - * |
|
13 | - * @param string $css |
|
14 | - * @param Rule[] $existingRules |
|
15 | - * |
|
16 | - * @return Rule[] |
|
17 | - */ |
|
18 | - public function getRules($css, $existingRules = array()) |
|
19 | - { |
|
20 | - $css = $this->doCleanup($css); |
|
21 | - $rulesProcessor = new RuleProcessor(); |
|
22 | - $rules = $rulesProcessor->splitIntoSeparateRules($css); |
|
10 | + /** |
|
11 | + * Get the rules from a given CSS-string |
|
12 | + * |
|
13 | + * @param string $css |
|
14 | + * @param Rule[] $existingRules |
|
15 | + * |
|
16 | + * @return Rule[] |
|
17 | + */ |
|
18 | + public function getRules($css, $existingRules = array()) |
|
19 | + { |
|
20 | + $css = $this->doCleanup($css); |
|
21 | + $rulesProcessor = new RuleProcessor(); |
|
22 | + $rules = $rulesProcessor->splitIntoSeparateRules($css); |
|
23 | 23 | |
24 | - return $rulesProcessor->convertArrayToObjects($rules, $existingRules); |
|
25 | - } |
|
24 | + return $rulesProcessor->convertArrayToObjects($rules, $existingRules); |
|
25 | + } |
|
26 | 26 | |
27 | - /** |
|
28 | - * Get the CSS from the style-tags in the given HTML-string |
|
29 | - * |
|
30 | - * @param string $html |
|
31 | - * |
|
32 | - * @return string |
|
33 | - */ |
|
34 | - public function getCssFromStyleTags($html) |
|
35 | - { |
|
36 | - $css = ''; |
|
37 | - $matches = array(); |
|
38 | - $htmlNoComments = preg_replace('|<!--.*?-->|s', '', $html); |
|
39 | - preg_match_all('|<style(?:\s.*)?>(.*)</style>|isU', $htmlNoComments, $matches); |
|
27 | + /** |
|
28 | + * Get the CSS from the style-tags in the given HTML-string |
|
29 | + * |
|
30 | + * @param string $html |
|
31 | + * |
|
32 | + * @return string |
|
33 | + */ |
|
34 | + public function getCssFromStyleTags($html) |
|
35 | + { |
|
36 | + $css = ''; |
|
37 | + $matches = array(); |
|
38 | + $htmlNoComments = preg_replace('|<!--.*?-->|s', '', $html); |
|
39 | + preg_match_all('|<style(?:\s.*)?>(.*)</style>|isU', $htmlNoComments, $matches); |
|
40 | 40 | |
41 | - if (!empty($matches[1])) { |
|
42 | - foreach ($matches[1] as $match) { |
|
43 | - $css .= trim($match) . "\n"; |
|
44 | - } |
|
45 | - } |
|
41 | + if (!empty($matches[1])) { |
|
42 | + foreach ($matches[1] as $match) { |
|
43 | + $css .= trim($match) . "\n"; |
|
44 | + } |
|
45 | + } |
|
46 | 46 | |
47 | - return $css; |
|
48 | - } |
|
47 | + return $css; |
|
48 | + } |
|
49 | 49 | |
50 | - /** |
|
51 | - * @param string $css |
|
52 | - * |
|
53 | - * @return string |
|
54 | - */ |
|
55 | - private function doCleanup($css) |
|
56 | - { |
|
57 | - // remove charset |
|
58 | - $css = preg_replace('/@charset "[^"]++";/', '', $css); |
|
59 | - // remove media queries |
|
60 | - $css = preg_replace('/@media [^{]*+{([^{}]++|{[^{}]*+})*+}/', '', $css); |
|
50 | + /** |
|
51 | + * @param string $css |
|
52 | + * |
|
53 | + * @return string |
|
54 | + */ |
|
55 | + private function doCleanup($css) |
|
56 | + { |
|
57 | + // remove charset |
|
58 | + $css = preg_replace('/@charset "[^"]++";/', '', $css); |
|
59 | + // remove media queries |
|
60 | + $css = preg_replace('/@media [^{]*+{([^{}]++|{[^{}]*+})*+}/', '', $css); |
|
61 | 61 | |
62 | - $css = str_replace(array("\r", "\n"), '', $css); |
|
63 | - $css = str_replace(array("\t"), ' ', $css); |
|
64 | - $css = str_replace('"', '\'', $css); |
|
65 | - $css = preg_replace('|/\*.*?\*/|', '', $css); |
|
66 | - $css = preg_replace('/\s\s++/', ' ', $css); |
|
67 | - $css = trim($css); |
|
62 | + $css = str_replace(array("\r", "\n"), '', $css); |
|
63 | + $css = str_replace(array("\t"), ' ', $css); |
|
64 | + $css = str_replace('"', '\'', $css); |
|
65 | + $css = preg_replace('|/\*.*?\*/|', '', $css); |
|
66 | + $css = preg_replace('/\s\s++/', ' ', $css); |
|
67 | + $css = trim($css); |
|
68 | 68 | |
69 | - return $css; |
|
70 | - } |
|
69 | + return $css; |
|
70 | + } |
|
71 | 71 | } |
@@ -38,9 +38,9 @@ |
||
38 | 38 | $htmlNoComments = preg_replace('|<!--.*?-->|s', '', $html); |
39 | 39 | preg_match_all('|<style(?:\s.*)?>(.*)</style>|isU', $htmlNoComments, $matches); |
40 | 40 | |
41 | - if (!empty($matches[1])) { |
|
41 | + if ( ! empty($matches[1])) { |
|
42 | 42 | foreach ($matches[1] as $match) { |
43 | - $css .= trim($match) . "\n"; |
|
43 | + $css .= trim($match)."\n"; |
|
44 | 44 | } |
45 | 45 | } |
46 | 46 |
@@ -7,79 +7,79 @@ |
||
7 | 7 | |
8 | 8 | final class Rule |
9 | 9 | { |
10 | - /** |
|
11 | - * @var string |
|
12 | - */ |
|
13 | - private $selector; |
|
10 | + /** |
|
11 | + * @var string |
|
12 | + */ |
|
13 | + private $selector; |
|
14 | 14 | |
15 | - /** |
|
16 | - * @var Property[] |
|
17 | - */ |
|
18 | - private $properties; |
|
15 | + /** |
|
16 | + * @var Property[] |
|
17 | + */ |
|
18 | + private $properties; |
|
19 | 19 | |
20 | - /** |
|
21 | - * @var Specificity |
|
22 | - */ |
|
23 | - private $specificity; |
|
20 | + /** |
|
21 | + * @var Specificity |
|
22 | + */ |
|
23 | + private $specificity; |
|
24 | 24 | |
25 | - /** |
|
26 | - * @var integer |
|
27 | - */ |
|
28 | - private $order; |
|
25 | + /** |
|
26 | + * @var integer |
|
27 | + */ |
|
28 | + private $order; |
|
29 | 29 | |
30 | - /** |
|
31 | - * Rule constructor. |
|
32 | - * |
|
33 | - * @param string $selector |
|
34 | - * @param Property[] $properties |
|
35 | - * @param Specificity $specificity |
|
36 | - * @param int $order |
|
37 | - */ |
|
38 | - public function __construct($selector, array $properties, Specificity $specificity, $order) |
|
39 | - { |
|
40 | - $this->selector = $selector; |
|
41 | - $this->properties = $properties; |
|
42 | - $this->specificity = $specificity; |
|
43 | - $this->order = $order; |
|
44 | - } |
|
30 | + /** |
|
31 | + * Rule constructor. |
|
32 | + * |
|
33 | + * @param string $selector |
|
34 | + * @param Property[] $properties |
|
35 | + * @param Specificity $specificity |
|
36 | + * @param int $order |
|
37 | + */ |
|
38 | + public function __construct($selector, array $properties, Specificity $specificity, $order) |
|
39 | + { |
|
40 | + $this->selector = $selector; |
|
41 | + $this->properties = $properties; |
|
42 | + $this->specificity = $specificity; |
|
43 | + $this->order = $order; |
|
44 | + } |
|
45 | 45 | |
46 | - /** |
|
47 | - * Get selector |
|
48 | - * |
|
49 | - * @return string |
|
50 | - */ |
|
51 | - public function getSelector() |
|
52 | - { |
|
53 | - return $this->selector; |
|
54 | - } |
|
46 | + /** |
|
47 | + * Get selector |
|
48 | + * |
|
49 | + * @return string |
|
50 | + */ |
|
51 | + public function getSelector() |
|
52 | + { |
|
53 | + return $this->selector; |
|
54 | + } |
|
55 | 55 | |
56 | - /** |
|
57 | - * Get properties |
|
58 | - * |
|
59 | - * @return Property[] |
|
60 | - */ |
|
61 | - public function getProperties() |
|
62 | - { |
|
63 | - return $this->properties; |
|
64 | - } |
|
56 | + /** |
|
57 | + * Get properties |
|
58 | + * |
|
59 | + * @return Property[] |
|
60 | + */ |
|
61 | + public function getProperties() |
|
62 | + { |
|
63 | + return $this->properties; |
|
64 | + } |
|
65 | 65 | |
66 | - /** |
|
67 | - * Get specificity |
|
68 | - * |
|
69 | - * @return Specificity |
|
70 | - */ |
|
71 | - public function getSpecificity() |
|
72 | - { |
|
73 | - return $this->specificity; |
|
74 | - } |
|
66 | + /** |
|
67 | + * Get specificity |
|
68 | + * |
|
69 | + * @return Specificity |
|
70 | + */ |
|
71 | + public function getSpecificity() |
|
72 | + { |
|
73 | + return $this->specificity; |
|
74 | + } |
|
75 | 75 | |
76 | - /** |
|
77 | - * Get order |
|
78 | - * |
|
79 | - * @return int |
|
80 | - */ |
|
81 | - public function getOrder() |
|
82 | - { |
|
83 | - return $this->order; |
|
84 | - } |
|
76 | + /** |
|
77 | + * Get order |
|
78 | + * |
|
79 | + * @return int |
|
80 | + */ |
|
81 | + public function getOrder() |
|
82 | + { |
|
83 | + return $this->order; |
|
84 | + } |
|
85 | 85 | } |
@@ -7,89 +7,89 @@ discard block |
||
7 | 7 | |
8 | 8 | class Processor |
9 | 9 | { |
10 | - /** |
|
11 | - * Splits a string into separate rules |
|
12 | - * |
|
13 | - * @param string $rulesString |
|
14 | - * |
|
15 | - * @return string[] |
|
16 | - */ |
|
17 | - public function splitIntoSeparateRules($rulesString) |
|
18 | - { |
|
19 | - $rulesString = $this->cleanup($rulesString); |
|
20 | - |
|
21 | - return (array) explode('}', $rulesString); |
|
22 | - } |
|
23 | - |
|
24 | - /** |
|
25 | - * @param string $string |
|
26 | - * |
|
27 | - * @return string |
|
28 | - */ |
|
29 | - private function cleanup($string) |
|
30 | - { |
|
31 | - $string = str_replace(array("\r", "\n"), '', $string); |
|
32 | - $string = str_replace(array("\t"), ' ', $string); |
|
33 | - $string = str_replace('"', '\'', $string); |
|
34 | - $string = preg_replace('|/\*.*?\*/|', '', $string); |
|
35 | - $string = preg_replace('/\s\s+/', ' ', $string); |
|
36 | - |
|
37 | - $string = trim($string); |
|
38 | - $string = rtrim($string, '}'); |
|
39 | - |
|
40 | - return $string; |
|
41 | - } |
|
42 | - |
|
43 | - /** |
|
44 | - * Converts a rule-string into an object |
|
45 | - * |
|
46 | - * @param string $rule |
|
47 | - * @param int $originalOrder |
|
48 | - * |
|
49 | - * @return Rule[] |
|
50 | - */ |
|
51 | - public function convertToObjects($rule, $originalOrder) |
|
52 | - { |
|
53 | - $rule = $this->cleanup($rule); |
|
54 | - |
|
55 | - $chunks = explode('{', $rule); |
|
56 | - if (!isset($chunks[1])) { |
|
57 | - return array(); |
|
58 | - } |
|
59 | - $propertiesProcessor = new PropertyProcessor(); |
|
60 | - $rules = array(); |
|
61 | - $selectors = (array) explode(',', trim($chunks[0])); |
|
62 | - $properties = $propertiesProcessor->splitIntoSeparateProperties($chunks[1]); |
|
63 | - |
|
64 | - foreach ($selectors as $selector) { |
|
65 | - $selector = trim($selector); |
|
66 | - $specificity = $this->calculateSpecificityBasedOnASelector($selector); |
|
67 | - |
|
68 | - $rules[] = new Rule( |
|
69 | - $selector, |
|
70 | - $propertiesProcessor->convertArrayToObjects($properties, $specificity), |
|
71 | - $specificity, |
|
72 | - $originalOrder |
|
73 | - ); |
|
74 | - } |
|
75 | - |
|
76 | - return $rules; |
|
77 | - } |
|
78 | - |
|
79 | - /** |
|
80 | - * Calculates the specificity based on a CSS Selector string, |
|
81 | - * Based on the patterns from premailer/css_parser by Alex Dunae |
|
82 | - * |
|
83 | - * @see https://github.com/premailer/css_parser/blob/master/lib/css_parser/regexps.rb |
|
84 | - * |
|
85 | - * @param string $selector |
|
86 | - * |
|
87 | - * @return Specificity |
|
88 | - */ |
|
89 | - public function calculateSpecificityBasedOnASelector($selector) |
|
90 | - { |
|
91 | - $idSelectorsPattern = " \#"; |
|
92 | - $classAttributesPseudoClassesSelectorsPattern = " (\.[\w]+) # classes |
|
10 | + /** |
|
11 | + * Splits a string into separate rules |
|
12 | + * |
|
13 | + * @param string $rulesString |
|
14 | + * |
|
15 | + * @return string[] |
|
16 | + */ |
|
17 | + public function splitIntoSeparateRules($rulesString) |
|
18 | + { |
|
19 | + $rulesString = $this->cleanup($rulesString); |
|
20 | + |
|
21 | + return (array) explode('}', $rulesString); |
|
22 | + } |
|
23 | + |
|
24 | + /** |
|
25 | + * @param string $string |
|
26 | + * |
|
27 | + * @return string |
|
28 | + */ |
|
29 | + private function cleanup($string) |
|
30 | + { |
|
31 | + $string = str_replace(array("\r", "\n"), '', $string); |
|
32 | + $string = str_replace(array("\t"), ' ', $string); |
|
33 | + $string = str_replace('"', '\'', $string); |
|
34 | + $string = preg_replace('|/\*.*?\*/|', '', $string); |
|
35 | + $string = preg_replace('/\s\s+/', ' ', $string); |
|
36 | + |
|
37 | + $string = trim($string); |
|
38 | + $string = rtrim($string, '}'); |
|
39 | + |
|
40 | + return $string; |
|
41 | + } |
|
42 | + |
|
43 | + /** |
|
44 | + * Converts a rule-string into an object |
|
45 | + * |
|
46 | + * @param string $rule |
|
47 | + * @param int $originalOrder |
|
48 | + * |
|
49 | + * @return Rule[] |
|
50 | + */ |
|
51 | + public function convertToObjects($rule, $originalOrder) |
|
52 | + { |
|
53 | + $rule = $this->cleanup($rule); |
|
54 | + |
|
55 | + $chunks = explode('{', $rule); |
|
56 | + if (!isset($chunks[1])) { |
|
57 | + return array(); |
|
58 | + } |
|
59 | + $propertiesProcessor = new PropertyProcessor(); |
|
60 | + $rules = array(); |
|
61 | + $selectors = (array) explode(',', trim($chunks[0])); |
|
62 | + $properties = $propertiesProcessor->splitIntoSeparateProperties($chunks[1]); |
|
63 | + |
|
64 | + foreach ($selectors as $selector) { |
|
65 | + $selector = trim($selector); |
|
66 | + $specificity = $this->calculateSpecificityBasedOnASelector($selector); |
|
67 | + |
|
68 | + $rules[] = new Rule( |
|
69 | + $selector, |
|
70 | + $propertiesProcessor->convertArrayToObjects($properties, $specificity), |
|
71 | + $specificity, |
|
72 | + $originalOrder |
|
73 | + ); |
|
74 | + } |
|
75 | + |
|
76 | + return $rules; |
|
77 | + } |
|
78 | + |
|
79 | + /** |
|
80 | + * Calculates the specificity based on a CSS Selector string, |
|
81 | + * Based on the patterns from premailer/css_parser by Alex Dunae |
|
82 | + * |
|
83 | + * @see https://github.com/premailer/css_parser/blob/master/lib/css_parser/regexps.rb |
|
84 | + * |
|
85 | + * @param string $selector |
|
86 | + * |
|
87 | + * @return Specificity |
|
88 | + */ |
|
89 | + public function calculateSpecificityBasedOnASelector($selector) |
|
90 | + { |
|
91 | + $idSelectorsPattern = " \#"; |
|
92 | + $classAttributesPseudoClassesSelectorsPattern = " (\.[\w]+) # classes |
|
93 | 93 | | |
94 | 94 | \[(\w+) # attributes |
95 | 95 | | |
@@ -106,7 +106,7 @@ discard block |
||
106 | 106 | |empty|contains |
107 | 107 | ))"; |
108 | 108 | |
109 | - $typePseudoElementsSelectorPattern = " ((^|[\s\+\>\~]+)[\w]+ # elements |
|
109 | + $typePseudoElementsSelectorPattern = " ((^|[\s\+\>\~]+)[\w]+ # elements |
|
110 | 110 | | |
111 | 111 | \:{1,2}( # pseudo-elements |
112 | 112 | after|before |
@@ -115,49 +115,49 @@ discard block |
||
115 | 115 | ) |
116 | 116 | )"; |
117 | 117 | |
118 | - return new Specificity( |
|
119 | - preg_match_all("/{$idSelectorsPattern}/ix", $selector, $matches), |
|
120 | - preg_match_all("/{$classAttributesPseudoClassesSelectorsPattern}/ix", $selector, $matches), |
|
121 | - preg_match_all("/{$typePseudoElementsSelectorPattern}/ix", $selector, $matches) |
|
122 | - ); |
|
123 | - } |
|
124 | - |
|
125 | - /** |
|
126 | - * @param string[] $rules |
|
127 | - * @param Rule[] $objects |
|
128 | - * |
|
129 | - * @return Rule[] |
|
130 | - */ |
|
131 | - public function convertArrayToObjects(array $rules, array $objects = array()) |
|
132 | - { |
|
133 | - $order = 1; |
|
134 | - foreach ($rules as $rule) { |
|
135 | - $objects = array_merge($objects, $this->convertToObjects($rule, $order)); |
|
136 | - $order++; |
|
137 | - } |
|
138 | - |
|
139 | - return $objects; |
|
140 | - } |
|
141 | - |
|
142 | - /** |
|
143 | - * Sorts an array on the specificity element in an ascending way |
|
144 | - * Lower specificity will be sorted to the beginning of the array |
|
145 | - * |
|
146 | - * @param Rule $e1 The first element. |
|
147 | - * @param Rule $e2 The second element. |
|
148 | - * |
|
149 | - * @return int |
|
150 | - */ |
|
151 | - public static function sortOnSpecificity(Rule $e1, Rule $e2) |
|
152 | - { |
|
153 | - $e1Specificity = $e1->getSpecificity(); |
|
154 | - $value = $e1Specificity->compareTo($e2->getSpecificity()); |
|
155 | - |
|
156 | - // if the specificity is the same, use the order in which the element appeared |
|
157 | - if ($value === 0) { |
|
158 | - $value = $e1->getOrder() - $e2->getOrder(); |
|
159 | - } |
|
160 | - |
|
161 | - return $value; |
|
162 | - } |
|
118 | + return new Specificity( |
|
119 | + preg_match_all("/{$idSelectorsPattern}/ix", $selector, $matches), |
|
120 | + preg_match_all("/{$classAttributesPseudoClassesSelectorsPattern}/ix", $selector, $matches), |
|
121 | + preg_match_all("/{$typePseudoElementsSelectorPattern}/ix", $selector, $matches) |
|
122 | + ); |
|
123 | + } |
|
124 | + |
|
125 | + /** |
|
126 | + * @param string[] $rules |
|
127 | + * @param Rule[] $objects |
|
128 | + * |
|
129 | + * @return Rule[] |
|
130 | + */ |
|
131 | + public function convertArrayToObjects(array $rules, array $objects = array()) |
|
132 | + { |
|
133 | + $order = 1; |
|
134 | + foreach ($rules as $rule) { |
|
135 | + $objects = array_merge($objects, $this->convertToObjects($rule, $order)); |
|
136 | + $order++; |
|
137 | + } |
|
138 | + |
|
139 | + return $objects; |
|
140 | + } |
|
141 | + |
|
142 | + /** |
|
143 | + * Sorts an array on the specificity element in an ascending way |
|
144 | + * Lower specificity will be sorted to the beginning of the array |
|
145 | + * |
|
146 | + * @param Rule $e1 The first element. |
|
147 | + * @param Rule $e2 The second element. |
|
148 | + * |
|
149 | + * @return int |
|
150 | + */ |
|
151 | + public static function sortOnSpecificity(Rule $e1, Rule $e2) |
|
152 | + { |
|
153 | + $e1Specificity = $e1->getSpecificity(); |
|
154 | + $value = $e1Specificity->compareTo($e2->getSpecificity()); |
|
155 | + |
|
156 | + // if the specificity is the same, use the order in which the element appeared |
|
157 | + if ($value === 0) { |
|
158 | + $value = $e1->getOrder() - $e2->getOrder(); |
|
159 | + } |
|
160 | + |
|
161 | + return $value; |
|
162 | + } |
|
163 | 163 | } |
@@ -53,7 +53,7 @@ |
||
53 | 53 | $rule = $this->cleanup($rule); |
54 | 54 | |
55 | 55 | $chunks = explode('{', $rule); |
56 | - if (!isset($chunks[1])) { |
|
56 | + if ( ! isset($chunks[1])) { |
|
57 | 57 | return array(); |
58 | 58 | } |
59 | 59 | $propertiesProcessor = new PropertyProcessor(); |
@@ -6,85 +6,85 @@ |
||
6 | 6 | |
7 | 7 | final class Property |
8 | 8 | { |
9 | - /** |
|
10 | - * @var string |
|
11 | - */ |
|
12 | - private $name; |
|
9 | + /** |
|
10 | + * @var string |
|
11 | + */ |
|
12 | + private $name; |
|
13 | 13 | |
14 | - /** |
|
15 | - * @var string |
|
16 | - */ |
|
17 | - private $value; |
|
14 | + /** |
|
15 | + * @var string |
|
16 | + */ |
|
17 | + private $value; |
|
18 | 18 | |
19 | - /** |
|
20 | - * @var Specificity |
|
21 | - */ |
|
22 | - private $originalSpecificity; |
|
19 | + /** |
|
20 | + * @var Specificity |
|
21 | + */ |
|
22 | + private $originalSpecificity; |
|
23 | 23 | |
24 | - /** |
|
25 | - * Property constructor. |
|
26 | - * @param string $name |
|
27 | - * @param string $value |
|
28 | - * @param Specificity|null $specificity |
|
29 | - */ |
|
30 | - public function __construct($name, $value, Specificity $specificity = null) |
|
31 | - { |
|
32 | - $this->name = $name; |
|
33 | - $this->value = $value; |
|
34 | - $this->originalSpecificity = $specificity; |
|
35 | - } |
|
24 | + /** |
|
25 | + * Property constructor. |
|
26 | + * @param string $name |
|
27 | + * @param string $value |
|
28 | + * @param Specificity|null $specificity |
|
29 | + */ |
|
30 | + public function __construct($name, $value, Specificity $specificity = null) |
|
31 | + { |
|
32 | + $this->name = $name; |
|
33 | + $this->value = $value; |
|
34 | + $this->originalSpecificity = $specificity; |
|
35 | + } |
|
36 | 36 | |
37 | - /** |
|
38 | - * Get name |
|
39 | - * |
|
40 | - * @return string |
|
41 | - */ |
|
42 | - public function getName() |
|
43 | - { |
|
44 | - return $this->name; |
|
45 | - } |
|
37 | + /** |
|
38 | + * Get name |
|
39 | + * |
|
40 | + * @return string |
|
41 | + */ |
|
42 | + public function getName() |
|
43 | + { |
|
44 | + return $this->name; |
|
45 | + } |
|
46 | 46 | |
47 | - /** |
|
48 | - * Get value |
|
49 | - * |
|
50 | - * @return string |
|
51 | - */ |
|
52 | - public function getValue() |
|
53 | - { |
|
54 | - return $this->value; |
|
55 | - } |
|
47 | + /** |
|
48 | + * Get value |
|
49 | + * |
|
50 | + * @return string |
|
51 | + */ |
|
52 | + public function getValue() |
|
53 | + { |
|
54 | + return $this->value; |
|
55 | + } |
|
56 | 56 | |
57 | - /** |
|
58 | - * Get originalSpecificity |
|
59 | - * |
|
60 | - * @return Specificity |
|
61 | - */ |
|
62 | - public function getOriginalSpecificity() |
|
63 | - { |
|
64 | - return $this->originalSpecificity; |
|
65 | - } |
|
57 | + /** |
|
58 | + * Get originalSpecificity |
|
59 | + * |
|
60 | + * @return Specificity |
|
61 | + */ |
|
62 | + public function getOriginalSpecificity() |
|
63 | + { |
|
64 | + return $this->originalSpecificity; |
|
65 | + } |
|
66 | 66 | |
67 | - /** |
|
68 | - * Is this property important? |
|
69 | - * |
|
70 | - * @return bool |
|
71 | - */ |
|
72 | - public function isImportant() |
|
73 | - { |
|
74 | - return (stripos($this->value, '!important') !== false); |
|
75 | - } |
|
67 | + /** |
|
68 | + * Is this property important? |
|
69 | + * |
|
70 | + * @return bool |
|
71 | + */ |
|
72 | + public function isImportant() |
|
73 | + { |
|
74 | + return (stripos($this->value, '!important') !== false); |
|
75 | + } |
|
76 | 76 | |
77 | - /** |
|
78 | - * Get the textual representation of the property |
|
79 | - * |
|
80 | - * @return string |
|
81 | - */ |
|
82 | - public function toString() |
|
83 | - { |
|
84 | - return sprintf( |
|
85 | - '%1$s: %2$s;', |
|
86 | - $this->name, |
|
87 | - $this->value |
|
88 | - ); |
|
89 | - } |
|
77 | + /** |
|
78 | + * Get the textual representation of the property |
|
79 | + * |
|
80 | + * @return string |
|
81 | + */ |
|
82 | + public function toString() |
|
83 | + { |
|
84 | + return sprintf( |
|
85 | + '%1$s: %2$s;', |
|
86 | + $this->name, |
|
87 | + $this->value |
|
88 | + ); |
|
89 | + } |
|
90 | 90 | } |
@@ -6,122 +6,122 @@ |
||
6 | 6 | |
7 | 7 | class Processor |
8 | 8 | { |
9 | - /** |
|
10 | - * Split a string into separate properties |
|
11 | - * |
|
12 | - * @param string $propertiesString |
|
13 | - * |
|
14 | - * @return string[] |
|
15 | - */ |
|
16 | - public function splitIntoSeparateProperties($propertiesString) |
|
17 | - { |
|
18 | - $propertiesString = $this->cleanup($propertiesString); |
|
19 | - |
|
20 | - $properties = (array) explode(';', $propertiesString); |
|
21 | - $keysToRemove = array(); |
|
22 | - $numberOfProperties = count($properties); |
|
23 | - |
|
24 | - for ($i = 0; $i < $numberOfProperties; $i++) { |
|
25 | - $properties[$i] = trim($properties[$i]); |
|
26 | - |
|
27 | - // if the new property begins with base64 it is part of the current property |
|
28 | - if (isset($properties[$i + 1]) && strpos(trim($properties[$i + 1]), 'base64,') === 0) { |
|
29 | - $properties[$i] .= ';' . trim($properties[$i + 1]); |
|
30 | - $keysToRemove[] = $i + 1; |
|
31 | - } |
|
32 | - } |
|
33 | - |
|
34 | - if (!empty($keysToRemove)) { |
|
35 | - foreach ($keysToRemove as $key) { |
|
36 | - unset($properties[$key]); |
|
37 | - } |
|
38 | - } |
|
39 | - |
|
40 | - return array_values($properties); |
|
41 | - } |
|
42 | - |
|
43 | - /** |
|
44 | - * @param string $string |
|
45 | - * |
|
46 | - * @return string |
|
47 | - */ |
|
48 | - private function cleanup($string) |
|
49 | - { |
|
50 | - $string = str_replace(array("\r", "\n"), '', $string); |
|
51 | - $string = str_replace(array("\t"), ' ', $string); |
|
52 | - $string = str_replace('"', '\'', $string); |
|
53 | - $string = preg_replace('|/\*.*?\*/|', '', $string); |
|
54 | - $string = preg_replace('/\s\s+/', ' ', $string); |
|
55 | - |
|
56 | - $string = trim($string); |
|
57 | - $string = rtrim($string, ';'); |
|
58 | - |
|
59 | - return $string; |
|
60 | - } |
|
61 | - |
|
62 | - /** |
|
63 | - * Converts a property-string into an object |
|
64 | - * |
|
65 | - * @param string $property |
|
66 | - * |
|
67 | - * @return Property|null |
|
68 | - */ |
|
69 | - public function convertToObject($property, Specificity $specificity = null) |
|
70 | - { |
|
71 | - if (strpos($property, ':') === false) { |
|
72 | - return null; |
|
73 | - } |
|
74 | - |
|
75 | - list($name, $value) = explode(':', $property, 2); |
|
76 | - |
|
77 | - $name = trim($name); |
|
78 | - $value = trim($value); |
|
79 | - |
|
80 | - if ($value === '') { |
|
81 | - return null; |
|
82 | - } |
|
83 | - |
|
84 | - return new Property($name, $value, $specificity); |
|
85 | - } |
|
86 | - |
|
87 | - /** |
|
88 | - * Converts an array of property-strings into objects |
|
89 | - * |
|
90 | - * @param string[] $properties |
|
91 | - * |
|
92 | - * @return Property[] |
|
93 | - */ |
|
94 | - public function convertArrayToObjects(array $properties, Specificity $specificity = null) |
|
95 | - { |
|
96 | - $objects = array(); |
|
97 | - |
|
98 | - foreach ($properties as $property) { |
|
99 | - $object = $this->convertToObject($property, $specificity); |
|
100 | - if ($object === null) { |
|
101 | - continue; |
|
102 | - } |
|
103 | - |
|
104 | - $objects[] = $object; |
|
105 | - } |
|
106 | - |
|
107 | - return $objects; |
|
108 | - } |
|
109 | - |
|
110 | - /** |
|
111 | - * Build the property-string for multiple properties |
|
112 | - * |
|
113 | - * @param Property[] $properties |
|
114 | - * |
|
115 | - * @return string |
|
116 | - */ |
|
117 | - public function buildPropertiesString(array $properties) |
|
118 | - { |
|
119 | - $chunks = array(); |
|
120 | - |
|
121 | - foreach ($properties as $property) { |
|
122 | - $chunks[] = $property->toString(); |
|
123 | - } |
|
124 | - |
|
125 | - return implode(' ', $chunks); |
|
126 | - } |
|
9 | + /** |
|
10 | + * Split a string into separate properties |
|
11 | + * |
|
12 | + * @param string $propertiesString |
|
13 | + * |
|
14 | + * @return string[] |
|
15 | + */ |
|
16 | + public function splitIntoSeparateProperties($propertiesString) |
|
17 | + { |
|
18 | + $propertiesString = $this->cleanup($propertiesString); |
|
19 | + |
|
20 | + $properties = (array) explode(';', $propertiesString); |
|
21 | + $keysToRemove = array(); |
|
22 | + $numberOfProperties = count($properties); |
|
23 | + |
|
24 | + for ($i = 0; $i < $numberOfProperties; $i++) { |
|
25 | + $properties[$i] = trim($properties[$i]); |
|
26 | + |
|
27 | + // if the new property begins with base64 it is part of the current property |
|
28 | + if (isset($properties[$i + 1]) && strpos(trim($properties[$i + 1]), 'base64,') === 0) { |
|
29 | + $properties[$i] .= ';' . trim($properties[$i + 1]); |
|
30 | + $keysToRemove[] = $i + 1; |
|
31 | + } |
|
32 | + } |
|
33 | + |
|
34 | + if (!empty($keysToRemove)) { |
|
35 | + foreach ($keysToRemove as $key) { |
|
36 | + unset($properties[$key]); |
|
37 | + } |
|
38 | + } |
|
39 | + |
|
40 | + return array_values($properties); |
|
41 | + } |
|
42 | + |
|
43 | + /** |
|
44 | + * @param string $string |
|
45 | + * |
|
46 | + * @return string |
|
47 | + */ |
|
48 | + private function cleanup($string) |
|
49 | + { |
|
50 | + $string = str_replace(array("\r", "\n"), '', $string); |
|
51 | + $string = str_replace(array("\t"), ' ', $string); |
|
52 | + $string = str_replace('"', '\'', $string); |
|
53 | + $string = preg_replace('|/\*.*?\*/|', '', $string); |
|
54 | + $string = preg_replace('/\s\s+/', ' ', $string); |
|
55 | + |
|
56 | + $string = trim($string); |
|
57 | + $string = rtrim($string, ';'); |
|
58 | + |
|
59 | + return $string; |
|
60 | + } |
|
61 | + |
|
62 | + /** |
|
63 | + * Converts a property-string into an object |
|
64 | + * |
|
65 | + * @param string $property |
|
66 | + * |
|
67 | + * @return Property|null |
|
68 | + */ |
|
69 | + public function convertToObject($property, Specificity $specificity = null) |
|
70 | + { |
|
71 | + if (strpos($property, ':') === false) { |
|
72 | + return null; |
|
73 | + } |
|
74 | + |
|
75 | + list($name, $value) = explode(':', $property, 2); |
|
76 | + |
|
77 | + $name = trim($name); |
|
78 | + $value = trim($value); |
|
79 | + |
|
80 | + if ($value === '') { |
|
81 | + return null; |
|
82 | + } |
|
83 | + |
|
84 | + return new Property($name, $value, $specificity); |
|
85 | + } |
|
86 | + |
|
87 | + /** |
|
88 | + * Converts an array of property-strings into objects |
|
89 | + * |
|
90 | + * @param string[] $properties |
|
91 | + * |
|
92 | + * @return Property[] |
|
93 | + */ |
|
94 | + public function convertArrayToObjects(array $properties, Specificity $specificity = null) |
|
95 | + { |
|
96 | + $objects = array(); |
|
97 | + |
|
98 | + foreach ($properties as $property) { |
|
99 | + $object = $this->convertToObject($property, $specificity); |
|
100 | + if ($object === null) { |
|
101 | + continue; |
|
102 | + } |
|
103 | + |
|
104 | + $objects[] = $object; |
|
105 | + } |
|
106 | + |
|
107 | + return $objects; |
|
108 | + } |
|
109 | + |
|
110 | + /** |
|
111 | + * Build the property-string for multiple properties |
|
112 | + * |
|
113 | + * @param Property[] $properties |
|
114 | + * |
|
115 | + * @return string |
|
116 | + */ |
|
117 | + public function buildPropertiesString(array $properties) |
|
118 | + { |
|
119 | + $chunks = array(); |
|
120 | + |
|
121 | + foreach ($properties as $property) { |
|
122 | + $chunks[] = $property->toString(); |
|
123 | + } |
|
124 | + |
|
125 | + return implode(' ', $chunks); |
|
126 | + } |
|
127 | 127 | } |
@@ -26,12 +26,12 @@ |
||
26 | 26 | |
27 | 27 | // if the new property begins with base64 it is part of the current property |
28 | 28 | if (isset($properties[$i + 1]) && strpos(trim($properties[$i + 1]), 'base64,') === 0) { |
29 | - $properties[$i] .= ';' . trim($properties[$i + 1]); |
|
29 | + $properties[$i] .= ';'.trim($properties[$i + 1]); |
|
30 | 30 | $keysToRemove[] = $i + 1; |
31 | 31 | } |
32 | 32 | } |
33 | 33 | |
34 | - if (!empty($keysToRemove)) { |
|
34 | + if ( ! empty($keysToRemove)) { |
|
35 | 35 | foreach ($keysToRemove as $key) { |
36 | 36 | unset($properties[$key]); |
37 | 37 | } |
@@ -11,230 +11,230 @@ |
||
11 | 11 | |
12 | 12 | class CssToInlineStyles |
13 | 13 | { |
14 | - private $cssConverter; |
|
15 | - |
|
16 | - public function __construct() |
|
17 | - { |
|
18 | - if (class_exists('Symfony\Component\CssSelector\CssSelectorConverter')) { |
|
19 | - $this->cssConverter = new CssSelectorConverter(); |
|
20 | - } |
|
21 | - } |
|
22 | - |
|
23 | - /** |
|
24 | - * Will inline the $css into the given $html |
|
25 | - * |
|
26 | - * Remark: if the html contains <style>-tags those will be used, the rules |
|
27 | - * in $css will be appended. |
|
28 | - * |
|
29 | - * @param string $html |
|
30 | - * @param string $css |
|
31 | - * |
|
32 | - * @return string |
|
33 | - */ |
|
34 | - public function convert($html, $css = null) |
|
35 | - { |
|
36 | - $document = $this->createDomDocumentFromHtml($html); |
|
37 | - $processor = new Processor(); |
|
38 | - |
|
39 | - // get all styles from the style-tags |
|
40 | - $rules = $processor->getRules( |
|
41 | - $processor->getCssFromStyleTags($html) |
|
42 | - ); |
|
43 | - |
|
44 | - if ($css !== null) { |
|
45 | - $rules = $processor->getRules($css, $rules); |
|
46 | - } |
|
47 | - |
|
48 | - $document = $this->inline($document, $rules); |
|
49 | - |
|
50 | - return $this->getHtmlFromDocument($document); |
|
51 | - } |
|
52 | - |
|
53 | - /** |
|
54 | - * Inline the given properties on an given DOMElement |
|
55 | - * |
|
56 | - * @param \DOMElement $element |
|
57 | - * @param Css\Property\Property[] $properties |
|
58 | - * |
|
59 | - * @return \DOMElement |
|
60 | - */ |
|
61 | - public function inlineCssOnElement(\DOMElement $element, array $properties) |
|
62 | - { |
|
63 | - if (empty($properties)) { |
|
64 | - return $element; |
|
65 | - } |
|
66 | - |
|
67 | - $cssProperties = array(); |
|
68 | - $inlineProperties = array(); |
|
69 | - |
|
70 | - foreach ($this->getInlineStyles($element) as $property) { |
|
71 | - $inlineProperties[$property->getName()] = $property; |
|
72 | - } |
|
73 | - |
|
74 | - foreach ($properties as $property) { |
|
75 | - if (!isset($inlineProperties[$property->getName()])) { |
|
76 | - $cssProperties[$property->getName()] = $property; |
|
77 | - } |
|
78 | - } |
|
79 | - |
|
80 | - $rules = array(); |
|
81 | - foreach (array_merge($cssProperties, $inlineProperties) as $property) { |
|
82 | - $rules[] = $property->toString(); |
|
83 | - } |
|
84 | - $element->setAttribute('style', implode(' ', $rules)); |
|
85 | - |
|
86 | - return $element; |
|
87 | - } |
|
88 | - |
|
89 | - /** |
|
90 | - * Get the current inline styles for a given DOMElement |
|
91 | - * |
|
92 | - * @param \DOMElement $element |
|
93 | - * |
|
94 | - * @return Css\Property\Property[] |
|
95 | - */ |
|
96 | - public function getInlineStyles(\DOMElement $element) |
|
97 | - { |
|
98 | - $processor = new PropertyProcessor(); |
|
99 | - |
|
100 | - return $processor->convertArrayToObjects( |
|
101 | - $processor->splitIntoSeparateProperties( |
|
102 | - $element->getAttribute('style') |
|
103 | - ) |
|
104 | - ); |
|
105 | - } |
|
106 | - |
|
107 | - /** |
|
108 | - * @param string $html |
|
109 | - * |
|
110 | - * @return \DOMDocument |
|
111 | - */ |
|
112 | - protected function createDomDocumentFromHtml($html) |
|
113 | - { |
|
114 | - $document = new \DOMDocument('1.0', 'UTF-8'); |
|
115 | - $internalErrors = libxml_use_internal_errors(true); |
|
116 | - $document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); |
|
117 | - libxml_use_internal_errors($internalErrors); |
|
118 | - $document->formatOutput = true; |
|
119 | - |
|
120 | - return $document; |
|
121 | - } |
|
122 | - |
|
123 | - /** |
|
124 | - * @param \DOMDocument $document |
|
125 | - * |
|
126 | - * @return string |
|
127 | - */ |
|
128 | - protected function getHtmlFromDocument(\DOMDocument $document) |
|
129 | - { |
|
130 | - // retrieve the document element |
|
131 | - // we do it this way to preserve the utf-8 encoding |
|
132 | - $htmlElement = $document->documentElement; |
|
133 | - $html = $document->saveHTML($htmlElement); |
|
134 | - $html = trim($html); |
|
135 | - |
|
136 | - // retrieve the doctype |
|
137 | - $document->removeChild($htmlElement); |
|
138 | - $doctype = $document->saveHTML(); |
|
139 | - $doctype = trim($doctype); |
|
140 | - |
|
141 | - // if it is the html5 doctype convert it to lowercase |
|
142 | - if ($doctype === '<!DOCTYPE html>') { |
|
143 | - $doctype = strtolower($doctype); |
|
144 | - } |
|
145 | - |
|
146 | - return $doctype."\n".$html; |
|
147 | - } |
|
148 | - |
|
149 | - /** |
|
150 | - * @param \DOMDocument $document |
|
151 | - * @param Css\Rule\Rule[] $rules |
|
152 | - * |
|
153 | - * @return \DOMDocument |
|
154 | - */ |
|
155 | - protected function inline(\DOMDocument $document, array $rules) |
|
156 | - { |
|
157 | - if (empty($rules)) { |
|
158 | - return $document; |
|
159 | - } |
|
160 | - |
|
161 | - $propertyStorage = new \SplObjectStorage(); |
|
162 | - |
|
163 | - $xPath = new \DOMXPath($document); |
|
164 | - |
|
165 | - usort($rules, array(RuleProcessor::class, 'sortOnSpecificity')); |
|
166 | - |
|
167 | - foreach ($rules as $rule) { |
|
168 | - try { |
|
169 | - if (null !== $this->cssConverter) { |
|
170 | - $expression = $this->cssConverter->toXPath($rule->getSelector()); |
|
171 | - } else { |
|
172 | - // Compatibility layer for Symfony 2.7 and older |
|
173 | - $expression = CssSelector::toXPath($rule->getSelector()); |
|
174 | - } |
|
175 | - } catch (ExceptionInterface $e) { |
|
176 | - continue; |
|
177 | - } |
|
178 | - |
|
179 | - $elements = $xPath->query($expression); |
|
180 | - |
|
181 | - if ($elements === false) { |
|
182 | - continue; |
|
183 | - } |
|
184 | - |
|
185 | - foreach ($elements as $element) { |
|
186 | - $propertyStorage[$element] = $this->calculatePropertiesToBeApplied( |
|
187 | - $rule->getProperties(), |
|
188 | - $propertyStorage->contains($element) ? $propertyStorage[$element] : array() |
|
189 | - ); |
|
190 | - } |
|
191 | - } |
|
192 | - |
|
193 | - foreach ($propertyStorage as $element) { |
|
194 | - $this->inlineCssOnElement($element, $propertyStorage[$element]); |
|
195 | - } |
|
196 | - |
|
197 | - return $document; |
|
198 | - } |
|
199 | - |
|
200 | - /** |
|
201 | - * Merge the CSS rules to determine the applied properties. |
|
202 | - * |
|
203 | - * @param Css\Property\Property[] $properties |
|
204 | - * @param Css\Property\Property[] $cssProperties existing applied properties indexed by name |
|
205 | - * |
|
206 | - * @return Css\Property\Property[] updated properties, indexed by name |
|
207 | - */ |
|
208 | - private function calculatePropertiesToBeApplied(array $properties, array $cssProperties) |
|
209 | - { |
|
210 | - if (empty($properties)) { |
|
211 | - return $cssProperties; |
|
212 | - } |
|
213 | - |
|
214 | - foreach ($properties as $property) { |
|
215 | - if (isset($cssProperties[$property->getName()])) { |
|
216 | - $existingProperty = $cssProperties[$property->getName()]; |
|
217 | - |
|
218 | - //skip check to overrule if existing property is important and current is not |
|
219 | - if ($existingProperty->isImportant() && !$property->isImportant()) { |
|
220 | - continue; |
|
221 | - } |
|
222 | - |
|
223 | - //overrule if current property is important and existing is not, else check specificity |
|
224 | - $overrule = !$existingProperty->isImportant() && $property->isImportant(); |
|
225 | - if (!$overrule) { |
|
226 | - $overrule = $existingProperty->getOriginalSpecificity()->compareTo($property->getOriginalSpecificity()) <= 0; |
|
227 | - } |
|
228 | - |
|
229 | - if ($overrule) { |
|
230 | - unset($cssProperties[$property->getName()]); |
|
231 | - $cssProperties[$property->getName()] = $property; |
|
232 | - } |
|
233 | - } else { |
|
234 | - $cssProperties[$property->getName()] = $property; |
|
235 | - } |
|
236 | - } |
|
237 | - |
|
238 | - return $cssProperties; |
|
239 | - } |
|
14 | + private $cssConverter; |
|
15 | + |
|
16 | + public function __construct() |
|
17 | + { |
|
18 | + if (class_exists('Symfony\Component\CssSelector\CssSelectorConverter')) { |
|
19 | + $this->cssConverter = new CssSelectorConverter(); |
|
20 | + } |
|
21 | + } |
|
22 | + |
|
23 | + /** |
|
24 | + * Will inline the $css into the given $html |
|
25 | + * |
|
26 | + * Remark: if the html contains <style>-tags those will be used, the rules |
|
27 | + * in $css will be appended. |
|
28 | + * |
|
29 | + * @param string $html |
|
30 | + * @param string $css |
|
31 | + * |
|
32 | + * @return string |
|
33 | + */ |
|
34 | + public function convert($html, $css = null) |
|
35 | + { |
|
36 | + $document = $this->createDomDocumentFromHtml($html); |
|
37 | + $processor = new Processor(); |
|
38 | + |
|
39 | + // get all styles from the style-tags |
|
40 | + $rules = $processor->getRules( |
|
41 | + $processor->getCssFromStyleTags($html) |
|
42 | + ); |
|
43 | + |
|
44 | + if ($css !== null) { |
|
45 | + $rules = $processor->getRules($css, $rules); |
|
46 | + } |
|
47 | + |
|
48 | + $document = $this->inline($document, $rules); |
|
49 | + |
|
50 | + return $this->getHtmlFromDocument($document); |
|
51 | + } |
|
52 | + |
|
53 | + /** |
|
54 | + * Inline the given properties on an given DOMElement |
|
55 | + * |
|
56 | + * @param \DOMElement $element |
|
57 | + * @param Css\Property\Property[] $properties |
|
58 | + * |
|
59 | + * @return \DOMElement |
|
60 | + */ |
|
61 | + public function inlineCssOnElement(\DOMElement $element, array $properties) |
|
62 | + { |
|
63 | + if (empty($properties)) { |
|
64 | + return $element; |
|
65 | + } |
|
66 | + |
|
67 | + $cssProperties = array(); |
|
68 | + $inlineProperties = array(); |
|
69 | + |
|
70 | + foreach ($this->getInlineStyles($element) as $property) { |
|
71 | + $inlineProperties[$property->getName()] = $property; |
|
72 | + } |
|
73 | + |
|
74 | + foreach ($properties as $property) { |
|
75 | + if (!isset($inlineProperties[$property->getName()])) { |
|
76 | + $cssProperties[$property->getName()] = $property; |
|
77 | + } |
|
78 | + } |
|
79 | + |
|
80 | + $rules = array(); |
|
81 | + foreach (array_merge($cssProperties, $inlineProperties) as $property) { |
|
82 | + $rules[] = $property->toString(); |
|
83 | + } |
|
84 | + $element->setAttribute('style', implode(' ', $rules)); |
|
85 | + |
|
86 | + return $element; |
|
87 | + } |
|
88 | + |
|
89 | + /** |
|
90 | + * Get the current inline styles for a given DOMElement |
|
91 | + * |
|
92 | + * @param \DOMElement $element |
|
93 | + * |
|
94 | + * @return Css\Property\Property[] |
|
95 | + */ |
|
96 | + public function getInlineStyles(\DOMElement $element) |
|
97 | + { |
|
98 | + $processor = new PropertyProcessor(); |
|
99 | + |
|
100 | + return $processor->convertArrayToObjects( |
|
101 | + $processor->splitIntoSeparateProperties( |
|
102 | + $element->getAttribute('style') |
|
103 | + ) |
|
104 | + ); |
|
105 | + } |
|
106 | + |
|
107 | + /** |
|
108 | + * @param string $html |
|
109 | + * |
|
110 | + * @return \DOMDocument |
|
111 | + */ |
|
112 | + protected function createDomDocumentFromHtml($html) |
|
113 | + { |
|
114 | + $document = new \DOMDocument('1.0', 'UTF-8'); |
|
115 | + $internalErrors = libxml_use_internal_errors(true); |
|
116 | + $document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); |
|
117 | + libxml_use_internal_errors($internalErrors); |
|
118 | + $document->formatOutput = true; |
|
119 | + |
|
120 | + return $document; |
|
121 | + } |
|
122 | + |
|
123 | + /** |
|
124 | + * @param \DOMDocument $document |
|
125 | + * |
|
126 | + * @return string |
|
127 | + */ |
|
128 | + protected function getHtmlFromDocument(\DOMDocument $document) |
|
129 | + { |
|
130 | + // retrieve the document element |
|
131 | + // we do it this way to preserve the utf-8 encoding |
|
132 | + $htmlElement = $document->documentElement; |
|
133 | + $html = $document->saveHTML($htmlElement); |
|
134 | + $html = trim($html); |
|
135 | + |
|
136 | + // retrieve the doctype |
|
137 | + $document->removeChild($htmlElement); |
|
138 | + $doctype = $document->saveHTML(); |
|
139 | + $doctype = trim($doctype); |
|
140 | + |
|
141 | + // if it is the html5 doctype convert it to lowercase |
|
142 | + if ($doctype === '<!DOCTYPE html>') { |
|
143 | + $doctype = strtolower($doctype); |
|
144 | + } |
|
145 | + |
|
146 | + return $doctype."\n".$html; |
|
147 | + } |
|
148 | + |
|
149 | + /** |
|
150 | + * @param \DOMDocument $document |
|
151 | + * @param Css\Rule\Rule[] $rules |
|
152 | + * |
|
153 | + * @return \DOMDocument |
|
154 | + */ |
|
155 | + protected function inline(\DOMDocument $document, array $rules) |
|
156 | + { |
|
157 | + if (empty($rules)) { |
|
158 | + return $document; |
|
159 | + } |
|
160 | + |
|
161 | + $propertyStorage = new \SplObjectStorage(); |
|
162 | + |
|
163 | + $xPath = new \DOMXPath($document); |
|
164 | + |
|
165 | + usort($rules, array(RuleProcessor::class, 'sortOnSpecificity')); |
|
166 | + |
|
167 | + foreach ($rules as $rule) { |
|
168 | + try { |
|
169 | + if (null !== $this->cssConverter) { |
|
170 | + $expression = $this->cssConverter->toXPath($rule->getSelector()); |
|
171 | + } else { |
|
172 | + // Compatibility layer for Symfony 2.7 and older |
|
173 | + $expression = CssSelector::toXPath($rule->getSelector()); |
|
174 | + } |
|
175 | + } catch (ExceptionInterface $e) { |
|
176 | + continue; |
|
177 | + } |
|
178 | + |
|
179 | + $elements = $xPath->query($expression); |
|
180 | + |
|
181 | + if ($elements === false) { |
|
182 | + continue; |
|
183 | + } |
|
184 | + |
|
185 | + foreach ($elements as $element) { |
|
186 | + $propertyStorage[$element] = $this->calculatePropertiesToBeApplied( |
|
187 | + $rule->getProperties(), |
|
188 | + $propertyStorage->contains($element) ? $propertyStorage[$element] : array() |
|
189 | + ); |
|
190 | + } |
|
191 | + } |
|
192 | + |
|
193 | + foreach ($propertyStorage as $element) { |
|
194 | + $this->inlineCssOnElement($element, $propertyStorage[$element]); |
|
195 | + } |
|
196 | + |
|
197 | + return $document; |
|
198 | + } |
|
199 | + |
|
200 | + /** |
|
201 | + * Merge the CSS rules to determine the applied properties. |
|
202 | + * |
|
203 | + * @param Css\Property\Property[] $properties |
|
204 | + * @param Css\Property\Property[] $cssProperties existing applied properties indexed by name |
|
205 | + * |
|
206 | + * @return Css\Property\Property[] updated properties, indexed by name |
|
207 | + */ |
|
208 | + private function calculatePropertiesToBeApplied(array $properties, array $cssProperties) |
|
209 | + { |
|
210 | + if (empty($properties)) { |
|
211 | + return $cssProperties; |
|
212 | + } |
|
213 | + |
|
214 | + foreach ($properties as $property) { |
|
215 | + if (isset($cssProperties[$property->getName()])) { |
|
216 | + $existingProperty = $cssProperties[$property->getName()]; |
|
217 | + |
|
218 | + //skip check to overrule if existing property is important and current is not |
|
219 | + if ($existingProperty->isImportant() && !$property->isImportant()) { |
|
220 | + continue; |
|
221 | + } |
|
222 | + |
|
223 | + //overrule if current property is important and existing is not, else check specificity |
|
224 | + $overrule = !$existingProperty->isImportant() && $property->isImportant(); |
|
225 | + if (!$overrule) { |
|
226 | + $overrule = $existingProperty->getOriginalSpecificity()->compareTo($property->getOriginalSpecificity()) <= 0; |
|
227 | + } |
|
228 | + |
|
229 | + if ($overrule) { |
|
230 | + unset($cssProperties[$property->getName()]); |
|
231 | + $cssProperties[$property->getName()] = $property; |
|
232 | + } |
|
233 | + } else { |
|
234 | + $cssProperties[$property->getName()] = $property; |
|
235 | + } |
|
236 | + } |
|
237 | + |
|
238 | + return $cssProperties; |
|
239 | + } |
|
240 | 240 | } |
@@ -72,7 +72,7 @@ discard block |
||
72 | 72 | } |
73 | 73 | |
74 | 74 | foreach ($properties as $property) { |
75 | - if (!isset($inlineProperties[$property->getName()])) { |
|
75 | + if ( ! isset($inlineProperties[$property->getName()])) { |
|
76 | 76 | $cssProperties[$property->getName()] = $property; |
77 | 77 | } |
78 | 78 | } |
@@ -216,13 +216,13 @@ discard block |
||
216 | 216 | $existingProperty = $cssProperties[$property->getName()]; |
217 | 217 | |
218 | 218 | //skip check to overrule if existing property is important and current is not |
219 | - if ($existingProperty->isImportant() && !$property->isImportant()) { |
|
219 | + if ($existingProperty->isImportant() && ! $property->isImportant()) { |
|
220 | 220 | continue; |
221 | 221 | } |
222 | 222 | |
223 | 223 | //overrule if current property is important and existing is not, else check specificity |
224 | - $overrule = !$existingProperty->isImportant() && $property->isImportant(); |
|
225 | - if (!$overrule) { |
|
224 | + $overrule = ! $existingProperty->isImportant() && $property->isImportant(); |
|
225 | + if ( ! $overrule) { |
|
226 | 226 | $overrule = $existingProperty->getOriginalSpecificity()->compareTo($property->getOriginalSpecificity()) <= 0; |
227 | 227 | } |
228 | 228 |
@@ -24,14 +24,14 @@ |
||
24 | 24 | public $data; |
25 | 25 | |
26 | 26 | public function __construct(File $font) { |
27 | - $this->font = $font; |
|
27 | + $this->font = $font; |
|
28 | 28 | } |
29 | 29 | |
30 | 30 | public function encode() { |
31 | - return $this->font->pack($this->def, $this->data); |
|
31 | + return $this->font->pack($this->def, $this->data); |
|
32 | 32 | } |
33 | 33 | |
34 | 34 | public function parse() { |
35 | - $this->data = $this->font->unpack($this->def); |
|
35 | + $this->data = $this->font->unpack($this->def); |
|
36 | 36 | } |
37 | 37 | } |
38 | 38 | \ No newline at end of file |
@@ -20,94 +20,94 @@ |
||
20 | 20 | */ |
21 | 21 | class Header extends \FontLib\Header { |
22 | 22 | protected $def = array( |
23 | - "format" => self::uint32, |
|
24 | - "numTables" => self::uint16, |
|
25 | - "searchRange" => self::uint16, |
|
26 | - "entrySelector" => self::uint16, |
|
27 | - "rangeShift" => self::uint16, |
|
23 | + "format" => self::uint32, |
|
24 | + "numTables" => self::uint16, |
|
25 | + "searchRange" => self::uint16, |
|
26 | + "entrySelector" => self::uint16, |
|
27 | + "rangeShift" => self::uint16, |
|
28 | 28 | ); |
29 | 29 | |
30 | 30 | public function parse() { |
31 | - $font = $this->font; |
|
32 | - |
|
33 | - $this->data = $font->unpack(array( |
|
34 | - "EOTSize" => self::uint32, |
|
35 | - "FontDataSize" => self::uint32, |
|
36 | - "Version" => self::uint32, |
|
37 | - "Flags" => self::uint32, |
|
38 | - "FontPANOSE" => array(self::uint8, 10), |
|
39 | - "Charset" => self::uint8, |
|
40 | - "Italic" => self::uint8, |
|
41 | - "Weight" => self::uint32, |
|
42 | - "fsType" => self::uint16, |
|
43 | - "MagicNumber" => self::uint16, |
|
44 | - "UnicodeRange1" => self::uint32, |
|
45 | - "UnicodeRange2" => self::uint32, |
|
46 | - "UnicodeRange3" => self::uint32, |
|
47 | - "UnicodeRange4" => self::uint32, |
|
48 | - "CodePageRange1" => self::uint32, |
|
49 | - "CodePageRange2" => self::uint32, |
|
50 | - "CheckSumAdjustment" => self::uint32, |
|
51 | - "Reserved1" => self::uint32, |
|
52 | - "Reserved2" => self::uint32, |
|
53 | - "Reserved3" => self::uint32, |
|
54 | - "Reserved4" => self::uint32, |
|
55 | - )); |
|
56 | - |
|
57 | - $this->data["Padding1"] = $font->readUInt16(); |
|
58 | - $this->readString("FamilyName"); |
|
59 | - |
|
60 | - $this->data["Padding2"] = $font->readUInt16(); |
|
61 | - $this->readString("StyleName"); |
|
62 | - |
|
63 | - $this->data["Padding3"] = $font->readUInt16(); |
|
64 | - $this->readString("VersionName"); |
|
65 | - |
|
66 | - $this->data["Padding4"] = $font->readUInt16(); |
|
67 | - $this->readString("FullName"); |
|
68 | - |
|
69 | - switch ($this->data["Version"]) { |
|
70 | - default: |
|
71 | - throw new Exception("Unknown EOT version " . $this->data["Version"]); |
|
72 | - |
|
73 | - case 0x00010000: |
|
74 | - // Nothing to do more |
|
75 | - break; |
|
76 | - |
|
77 | - case 0x00020001: |
|
78 | - $this->data["Padding5"] = $font->readUInt16(); |
|
79 | - $this->readString("RootString"); |
|
80 | - break; |
|
81 | - |
|
82 | - case 0x00020002: |
|
83 | - $this->data["Padding5"] = $font->readUInt16(); |
|
84 | - $this->readString("RootString"); |
|
85 | - |
|
86 | - $this->data["RootStringCheckSum"] = $font->readUInt32(); |
|
87 | - $this->data["EUDCCodePage"] = $font->readUInt32(); |
|
88 | - |
|
89 | - $this->data["Padding6"] = $font->readUInt16(); |
|
90 | - $this->readString("Signature"); |
|
91 | - |
|
92 | - $this->data["EUDCFlags"] = $font->readUInt32(); |
|
93 | - $this->data["EUDCFontSize"] = $font->readUInt32(); |
|
94 | - break; |
|
95 | - } |
|
96 | - |
|
97 | - if (!empty($this->data["RootString"])) { |
|
98 | - $this->data["RootString"] = explode("\0", $this->data["RootString"]); |
|
99 | - } |
|
31 | + $font = $this->font; |
|
32 | + |
|
33 | + $this->data = $font->unpack(array( |
|
34 | + "EOTSize" => self::uint32, |
|
35 | + "FontDataSize" => self::uint32, |
|
36 | + "Version" => self::uint32, |
|
37 | + "Flags" => self::uint32, |
|
38 | + "FontPANOSE" => array(self::uint8, 10), |
|
39 | + "Charset" => self::uint8, |
|
40 | + "Italic" => self::uint8, |
|
41 | + "Weight" => self::uint32, |
|
42 | + "fsType" => self::uint16, |
|
43 | + "MagicNumber" => self::uint16, |
|
44 | + "UnicodeRange1" => self::uint32, |
|
45 | + "UnicodeRange2" => self::uint32, |
|
46 | + "UnicodeRange3" => self::uint32, |
|
47 | + "UnicodeRange4" => self::uint32, |
|
48 | + "CodePageRange1" => self::uint32, |
|
49 | + "CodePageRange2" => self::uint32, |
|
50 | + "CheckSumAdjustment" => self::uint32, |
|
51 | + "Reserved1" => self::uint32, |
|
52 | + "Reserved2" => self::uint32, |
|
53 | + "Reserved3" => self::uint32, |
|
54 | + "Reserved4" => self::uint32, |
|
55 | + )); |
|
56 | + |
|
57 | + $this->data["Padding1"] = $font->readUInt16(); |
|
58 | + $this->readString("FamilyName"); |
|
59 | + |
|
60 | + $this->data["Padding2"] = $font->readUInt16(); |
|
61 | + $this->readString("StyleName"); |
|
62 | + |
|
63 | + $this->data["Padding3"] = $font->readUInt16(); |
|
64 | + $this->readString("VersionName"); |
|
65 | + |
|
66 | + $this->data["Padding4"] = $font->readUInt16(); |
|
67 | + $this->readString("FullName"); |
|
68 | + |
|
69 | + switch ($this->data["Version"]) { |
|
70 | + default: |
|
71 | + throw new Exception("Unknown EOT version " . $this->data["Version"]); |
|
72 | + |
|
73 | + case 0x00010000: |
|
74 | + // Nothing to do more |
|
75 | + break; |
|
76 | + |
|
77 | + case 0x00020001: |
|
78 | + $this->data["Padding5"] = $font->readUInt16(); |
|
79 | + $this->readString("RootString"); |
|
80 | + break; |
|
81 | + |
|
82 | + case 0x00020002: |
|
83 | + $this->data["Padding5"] = $font->readUInt16(); |
|
84 | + $this->readString("RootString"); |
|
85 | + |
|
86 | + $this->data["RootStringCheckSum"] = $font->readUInt32(); |
|
87 | + $this->data["EUDCCodePage"] = $font->readUInt32(); |
|
88 | + |
|
89 | + $this->data["Padding6"] = $font->readUInt16(); |
|
90 | + $this->readString("Signature"); |
|
91 | + |
|
92 | + $this->data["EUDCFlags"] = $font->readUInt32(); |
|
93 | + $this->data["EUDCFontSize"] = $font->readUInt32(); |
|
94 | + break; |
|
95 | + } |
|
96 | + |
|
97 | + if (!empty($this->data["RootString"])) { |
|
98 | + $this->data["RootString"] = explode("\0", $this->data["RootString"]); |
|
99 | + } |
|
100 | 100 | } |
101 | 101 | |
102 | 102 | private function readString($name) { |
103 | - $font = $this->font; |
|
104 | - $size = $font->readUInt16(); |
|
103 | + $font = $this->font; |
|
104 | + $size = $font->readUInt16(); |
|
105 | 105 | |
106 | - $this->data["{$name}Size"] = $size; |
|
107 | - $this->data[$name] = Font::UTF16ToUTF8($font->read($size)); |
|
106 | + $this->data["{$name}Size"] = $size; |
|
107 | + $this->data[$name] = Font::UTF16ToUTF8($font->read($size)); |
|
108 | 108 | } |
109 | 109 | |
110 | 110 | public function encode() { |
111 | - //return $this->font->pack($this->def, $this->data); |
|
111 | + //return $this->font->pack($this->def, $this->data); |
|
112 | 112 | } |
113 | 113 | } |
114 | 114 | \ No newline at end of file |
@@ -68,7 +68,7 @@ discard block |
||
68 | 68 | |
69 | 69 | switch ($this->data["Version"]) { |
70 | 70 | default: |
71 | - throw new Exception("Unknown EOT version " . $this->data["Version"]); |
|
71 | + throw new Exception("Unknown EOT version ".$this->data["Version"]); |
|
72 | 72 | |
73 | 73 | case 0x00010000: |
74 | 74 | // Nothing to do more |
@@ -94,7 +94,7 @@ discard block |
||
94 | 94 | break; |
95 | 95 | } |
96 | 96 | |
97 | - if (!empty($this->data["RootString"])) { |
|
97 | + if ( ! empty($this->data["RootString"])) { |
|
98 | 98 | $this->data["RootString"] = explode("\0", $this->data["RootString"]); |
99 | 99 | } |
100 | 100 | } |
@@ -28,37 +28,37 @@ discard block |
||
28 | 28 | public $header; |
29 | 29 | |
30 | 30 | function parseHeader() { |
31 | - if (!empty($this->header)) { |
|
32 | - return; |
|
33 | - } |
|
31 | + if (!empty($this->header)) { |
|
32 | + return; |
|
33 | + } |
|
34 | 34 | |
35 | - $this->header = new Header($this); |
|
36 | - $this->header->parse(); |
|
35 | + $this->header = new Header($this); |
|
36 | + $this->header->parse(); |
|
37 | 37 | } |
38 | 38 | |
39 | 39 | function parse() { |
40 | - $this->parseHeader(); |
|
40 | + $this->parseHeader(); |
|
41 | 41 | |
42 | - $flags = $this->header->data["Flags"]; |
|
42 | + $flags = $this->header->data["Flags"]; |
|
43 | 43 | |
44 | - if ($flags & self::TTEMBED_TTCOMPRESSED) { |
|
45 | - $mtx_version = $this->readUInt8(); |
|
46 | - $mtx_copy_limit = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8(); |
|
47 | - $mtx_offset_1 = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8(); |
|
48 | - $mtx_offset_2 = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8(); |
|
49 | - /* |
|
44 | + if ($flags & self::TTEMBED_TTCOMPRESSED) { |
|
45 | + $mtx_version = $this->readUInt8(); |
|
46 | + $mtx_copy_limit = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8(); |
|
47 | + $mtx_offset_1 = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8(); |
|
48 | + $mtx_offset_2 = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8(); |
|
49 | + /* |
|
50 | 50 | var_dump("$mtx_version $mtx_copy_limit $mtx_offset_1 $mtx_offset_2"); |
51 | 51 | |
52 | 52 | $pos = $this->pos(); |
53 | 53 | $size = $mtx_offset_1 - $pos; |
54 | 54 | var_dump("pos: $pos"); |
55 | 55 | var_dump("size: $size");*/ |
56 | - } |
|
56 | + } |
|
57 | 57 | |
58 | - if ($flags & self::TTEMBED_XORENCRYPTDATA) { |
|
59 | - // Process XOR |
|
60 | - } |
|
61 | - // TODO Read font data ... |
|
58 | + if ($flags & self::TTEMBED_XORENCRYPTDATA) { |
|
59 | + // Process XOR |
|
60 | + } |
|
61 | + // TODO Read font data ... |
|
62 | 62 | } |
63 | 63 | |
64 | 64 | /** |
@@ -69,20 +69,20 @@ discard block |
||
69 | 69 | * @return string |
70 | 70 | */ |
71 | 71 | public function read($n) { |
72 | - if ($n < 1) { |
|
73 | - return ""; |
|
74 | - } |
|
75 | - |
|
76 | - $string = (string) fread($this->f, $n); |
|
77 | - $chunks = mb_str_split($string, 2, '8bit'); |
|
78 | - $chunks = array_map("strrev", $chunks); |
|
79 | - return implode("", $chunks); |
|
72 | + if ($n < 1) { |
|
73 | + return ""; |
|
74 | + } |
|
75 | + |
|
76 | + $string = (string) fread($this->f, $n); |
|
77 | + $chunks = mb_str_split($string, 2, '8bit'); |
|
78 | + $chunks = array_map("strrev", $chunks); |
|
79 | + return implode("", $chunks); |
|
80 | 80 | } |
81 | 81 | |
82 | 82 | public function readUInt32() { |
83 | - $uint32 = parent::readUInt32(); |
|
83 | + $uint32 = parent::readUInt32(); |
|
84 | 84 | |
85 | - return $uint32 >> 16 & 0x0000FFFF | $uint32 << 16 & 0xFFFF0000; |
|
85 | + return $uint32 >> 16 & 0x0000FFFF | $uint32 << 16 & 0xFFFF0000; |
|
86 | 86 | } |
87 | 87 | |
88 | 88 | /** |
@@ -91,7 +91,7 @@ discard block |
||
91 | 91 | * @return string|null |
92 | 92 | */ |
93 | 93 | function getFontCopyright() { |
94 | - return null; |
|
94 | + return null; |
|
95 | 95 | } |
96 | 96 | |
97 | 97 | /** |
@@ -100,7 +100,7 @@ discard block |
||
100 | 100 | * @return string|null |
101 | 101 | */ |
102 | 102 | function getFontName() { |
103 | - return $this->header->data["FamilyName"]; |
|
103 | + return $this->header->data["FamilyName"]; |
|
104 | 104 | } |
105 | 105 | |
106 | 106 | /** |
@@ -109,7 +109,7 @@ discard block |
||
109 | 109 | * @return string|null |
110 | 110 | */ |
111 | 111 | function getFontSubfamily() { |
112 | - return $this->header->data["StyleName"]; |
|
112 | + return $this->header->data["StyleName"]; |
|
113 | 113 | } |
114 | 114 | |
115 | 115 | /** |
@@ -118,7 +118,7 @@ discard block |
||
118 | 118 | * @return string|null |
119 | 119 | */ |
120 | 120 | function getFontSubfamilyID() { |
121 | - return $this->header->data["StyleName"]; |
|
121 | + return $this->header->data["StyleName"]; |
|
122 | 122 | } |
123 | 123 | |
124 | 124 | /** |
@@ -127,7 +127,7 @@ discard block |
||
127 | 127 | * @return string|null |
128 | 128 | */ |
129 | 129 | function getFontFullName() { |
130 | - return $this->header->data["FullName"]; |
|
130 | + return $this->header->data["FullName"]; |
|
131 | 131 | } |
132 | 132 | |
133 | 133 | /** |
@@ -136,7 +136,7 @@ discard block |
||
136 | 136 | * @return string|null |
137 | 137 | */ |
138 | 138 | function getFontVersion() { |
139 | - return $this->header->data["VersionName"]; |
|
139 | + return $this->header->data["VersionName"]; |
|
140 | 140 | } |
141 | 141 | |
142 | 142 | /** |
@@ -145,7 +145,7 @@ discard block |
||
145 | 145 | * @return string|null |
146 | 146 | */ |
147 | 147 | function getFontWeight() { |
148 | - return $this->header->data["Weight"]; |
|
148 | + return $this->header->data["Weight"]; |
|
149 | 149 | } |
150 | 150 | |
151 | 151 | /** |
@@ -154,6 +154,6 @@ discard block |
||
154 | 154 | * @return string|null |
155 | 155 | */ |
156 | 156 | function getFontPostscriptName() { |
157 | - return null; |
|
157 | + return null; |
|
158 | 158 | } |
159 | 159 | } |
@@ -28,7 +28,7 @@ |
||
28 | 28 | public $header; |
29 | 29 | |
30 | 30 | function parseHeader() { |
31 | - if (!empty($this->header)) { |
|
31 | + if ( ! empty($this->header)) { |
|
32 | 32 | return; |
33 | 33 | } |
34 | 34 |