diff options
Diffstat (limited to 'inc/3rdparty/htmlpurifier/HTMLPurifier/UnitConverter.php')
-rw-r--r-- | inc/3rdparty/htmlpurifier/HTMLPurifier/UnitConverter.php | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/UnitConverter.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/UnitConverter.php new file mode 100644 index 00000000..e663b327 --- /dev/null +++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/UnitConverter.php | |||
@@ -0,0 +1,307 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Class for converting between different unit-lengths as specified by | ||
5 | * CSS. | ||
6 | */ | ||
7 | class HTMLPurifier_UnitConverter | ||
8 | { | ||
9 | |||
10 | const ENGLISH = 1; | ||
11 | const METRIC = 2; | ||
12 | const DIGITAL = 3; | ||
13 | |||
14 | /** | ||
15 | * Units information array. Units are grouped into measuring systems | ||
16 | * (English, Metric), and are assigned an integer representing | ||
17 | * the conversion factor between that unit and the smallest unit in | ||
18 | * the system. Numeric indexes are actually magical constants that | ||
19 | * encode conversion data from one system to the next, with a O(n^2) | ||
20 | * constraint on memory (this is generally not a problem, since | ||
21 | * the number of measuring systems is small.) | ||
22 | */ | ||
23 | protected static $units = array( | ||
24 | self::ENGLISH => array( | ||
25 | 'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary | ||
26 | 'pt' => 4, | ||
27 | 'pc' => 48, | ||
28 | 'in' => 288, | ||
29 | self::METRIC => array('pt', '0.352777778', 'mm'), | ||
30 | ), | ||
31 | self::METRIC => array( | ||
32 | 'mm' => 1, | ||
33 | 'cm' => 10, | ||
34 | self::ENGLISH => array('mm', '2.83464567', 'pt'), | ||
35 | ), | ||
36 | ); | ||
37 | |||
38 | /** | ||
39 | * Minimum bcmath precision for output. | ||
40 | * @type int | ||
41 | */ | ||
42 | protected $outputPrecision; | ||
43 | |||
44 | /** | ||
45 | * Bcmath precision for internal calculations. | ||
46 | * @type int | ||
47 | */ | ||
48 | protected $internalPrecision; | ||
49 | |||
50 | /** | ||
51 | * Whether or not BCMath is available. | ||
52 | * @type bool | ||
53 | */ | ||
54 | private $bcmath; | ||
55 | |||
56 | public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) | ||
57 | { | ||
58 | $this->outputPrecision = $output_precision; | ||
59 | $this->internalPrecision = $internal_precision; | ||
60 | $this->bcmath = !$force_no_bcmath && function_exists('bcmul'); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * Converts a length object of one unit into another unit. | ||
65 | * @param HTMLPurifier_Length $length | ||
66 | * Instance of HTMLPurifier_Length to convert. You must validate() | ||
67 | * it before passing it here! | ||
68 | * @param string $to_unit | ||
69 | * Unit to convert to. | ||
70 | * @return HTMLPurifier_Length|bool | ||
71 | * @note | ||
72 | * About precision: This conversion function pays very special | ||
73 | * attention to the incoming precision of values and attempts | ||
74 | * to maintain a number of significant figure. Results are | ||
75 | * fairly accurate up to nine digits. Some caveats: | ||
76 | * - If a number is zero-padded as a result of this significant | ||
77 | * figure tracking, the zeroes will be eliminated. | ||
78 | * - If a number contains less than four sigfigs ($outputPrecision) | ||
79 | * and this causes some decimals to be excluded, those | ||
80 | * decimals will be added on. | ||
81 | */ | ||
82 | public function convert($length, $to_unit) | ||
83 | { | ||
84 | if (!$length->isValid()) { | ||
85 | return false; | ||
86 | } | ||
87 | |||
88 | $n = $length->getN(); | ||
89 | $unit = $length->getUnit(); | ||
90 | |||
91 | if ($n === '0' || $unit === false) { | ||
92 | return new HTMLPurifier_Length('0', false); | ||
93 | } | ||
94 | |||
95 | $state = $dest_state = false; | ||
96 | foreach (self::$units as $k => $x) { | ||
97 | if (isset($x[$unit])) { | ||
98 | $state = $k; | ||
99 | } | ||
100 | if (isset($x[$to_unit])) { | ||
101 | $dest_state = $k; | ||
102 | } | ||
103 | } | ||
104 | if (!$state || !$dest_state) { | ||
105 | return false; | ||
106 | } | ||
107 | |||
108 | // Some calculations about the initial precision of the number; | ||
109 | // this will be useful when we need to do final rounding. | ||
110 | $sigfigs = $this->getSigFigs($n); | ||
111 | if ($sigfigs < $this->outputPrecision) { | ||
112 | $sigfigs = $this->outputPrecision; | ||
113 | } | ||
114 | |||
115 | // BCMath's internal precision deals only with decimals. Use | ||
116 | // our default if the initial number has no decimals, or increase | ||
117 | // it by how ever many decimals, thus, the number of guard digits | ||
118 | // will always be greater than or equal to internalPrecision. | ||
119 | $log = (int)floor(log(abs($n), 10)); | ||
120 | $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision | ||
121 | |||
122 | for ($i = 0; $i < 2; $i++) { | ||
123 | |||
124 | // Determine what unit IN THIS SYSTEM we need to convert to | ||
125 | if ($dest_state === $state) { | ||
126 | // Simple conversion | ||
127 | $dest_unit = $to_unit; | ||
128 | } else { | ||
129 | // Convert to the smallest unit, pending a system shift | ||
130 | $dest_unit = self::$units[$state][$dest_state][0]; | ||
131 | } | ||
132 | |||
133 | // Do the conversion if necessary | ||
134 | if ($dest_unit !== $unit) { | ||
135 | $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp); | ||
136 | $n = $this->mul($n, $factor, $cp); | ||
137 | $unit = $dest_unit; | ||
138 | } | ||
139 | |||
140 | // Output was zero, so bail out early. Shouldn't ever happen. | ||
141 | if ($n === '') { | ||
142 | $n = '0'; | ||
143 | $unit = $to_unit; | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | // It was a simple conversion, so bail out | ||
148 | if ($dest_state === $state) { | ||
149 | break; | ||
150 | } | ||
151 | |||
152 | if ($i !== 0) { | ||
153 | // Conversion failed! Apparently, the system we forwarded | ||
154 | // to didn't have this unit. This should never happen! | ||
155 | return false; | ||
156 | } | ||
157 | |||
158 | // Pre-condition: $i == 0 | ||
159 | |||
160 | // Perform conversion to next system of units | ||
161 | $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp); | ||
162 | $unit = self::$units[$state][$dest_state][2]; | ||
163 | $state = $dest_state; | ||
164 | |||
165 | // One more loop around to convert the unit in the new system. | ||
166 | |||
167 | } | ||
168 | |||
169 | // Post-condition: $unit == $to_unit | ||
170 | if ($unit !== $to_unit) { | ||
171 | return false; | ||
172 | } | ||
173 | |||
174 | // Useful for debugging: | ||
175 | //echo "<pre>n"; | ||
176 | //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n"; | ||
177 | |||
178 | $n = $this->round($n, $sigfigs); | ||
179 | if (strpos($n, '.') !== false) { | ||
180 | $n = rtrim($n, '0'); | ||
181 | } | ||
182 | $n = rtrim($n, '.'); | ||
183 | |||
184 | return new HTMLPurifier_Length($n, $unit); | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * Returns the number of significant figures in a string number. | ||
189 | * @param string $n Decimal number | ||
190 | * @return int number of sigfigs | ||
191 | */ | ||
192 | public function getSigFigs($n) | ||
193 | { | ||
194 | $n = ltrim($n, '0+-'); | ||
195 | $dp = strpos($n, '.'); // decimal position | ||
196 | if ($dp === false) { | ||
197 | $sigfigs = strlen(rtrim($n, '0')); | ||
198 | } else { | ||
199 | $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character | ||
200 | if ($dp !== 0) { | ||
201 | $sigfigs--; | ||
202 | } | ||
203 | } | ||
204 | return $sigfigs; | ||
205 | } | ||
206 | |||
207 | /** | ||
208 | * Adds two numbers, using arbitrary precision when available. | ||
209 | * @param string $s1 | ||
210 | * @param string $s2 | ||
211 | * @param int $scale | ||
212 | * @return string | ||
213 | */ | ||
214 | private function add($s1, $s2, $scale) | ||
215 | { | ||
216 | if ($this->bcmath) { | ||
217 | return bcadd($s1, $s2, $scale); | ||
218 | } else { | ||
219 | return $this->scale((float)$s1 + (float)$s2, $scale); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * Multiples two numbers, using arbitrary precision when available. | ||
225 | * @param string $s1 | ||
226 | * @param string $s2 | ||
227 | * @param int $scale | ||
228 | * @return string | ||
229 | */ | ||
230 | private function mul($s1, $s2, $scale) | ||
231 | { | ||
232 | if ($this->bcmath) { | ||
233 | return bcmul($s1, $s2, $scale); | ||
234 | } else { | ||
235 | return $this->scale((float)$s1 * (float)$s2, $scale); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * Divides two numbers, using arbitrary precision when available. | ||
241 | * @param string $s1 | ||
242 | * @param string $s2 | ||
243 | * @param int $scale | ||
244 | * @return string | ||
245 | */ | ||
246 | private function div($s1, $s2, $scale) | ||
247 | { | ||
248 | if ($this->bcmath) { | ||
249 | return bcdiv($s1, $s2, $scale); | ||
250 | } else { | ||
251 | return $this->scale((float)$s1 / (float)$s2, $scale); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * Rounds a number according to the number of sigfigs it should have, | ||
257 | * using arbitrary precision when available. | ||
258 | * @param float $n | ||
259 | * @param int $sigfigs | ||
260 | * @return string | ||
261 | */ | ||
262 | private function round($n, $sigfigs) | ||
263 | { | ||
264 | $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1 | ||
265 | $rp = $sigfigs - $new_log - 1; // Number of decimal places needed | ||
266 | $neg = $n < 0 ? '-' : ''; // Negative sign | ||
267 | if ($this->bcmath) { | ||
268 | if ($rp >= 0) { | ||
269 | $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1); | ||
270 | $n = bcdiv($n, '1', $rp); | ||
271 | } else { | ||
272 | // This algorithm partially depends on the standardized | ||
273 | // form of numbers that comes out of bcmath. | ||
274 | $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0); | ||
275 | $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1); | ||
276 | } | ||
277 | return $n; | ||
278 | } else { | ||
279 | return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * Scales a float to $scale digits right of decimal point, like BCMath. | ||
285 | * @param float $r | ||
286 | * @param int $scale | ||
287 | * @return string | ||
288 | */ | ||
289 | private function scale($r, $scale) | ||
290 | { | ||
291 | if ($scale < 0) { | ||
292 | // The f sprintf type doesn't support negative numbers, so we | ||
293 | // need to cludge things manually. First get the string. | ||
294 | $r = sprintf('%.0f', (float)$r); | ||
295 | // Due to floating point precision loss, $r will more than likely | ||
296 | // look something like 4652999999999.9234. We grab one more digit | ||
297 | // than we need to precise from $r and then use that to round | ||
298 | // appropriately. | ||
299 | $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1); | ||
300 | // Now we return it, truncating the zero that was rounded off. | ||
301 | return substr($precise, 0, -1) . str_repeat('0', -$scale + 1); | ||
302 | } | ||
303 | return sprintf('%.' . $scale . 'f', (float)$r); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | // vim: et sw=4 sts=4 | ||