HTMLPurifier/UnitConverter.php Quellcode

UnitConverter.php
gehe zur Dokumentation dieser Datei
1 <?php
2 
8 {
9 
10  const ENGLISH = 1;
11  const METRIC = 2;
12  const DIGITAL = 3;
13 
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 
42  protected $outputPrecision;
43 
48  protected $internalPrecision;
49 
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 
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 
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 
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 
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 
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 
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 
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




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.