Source for file UnitConverter.php
Documentation is available at UnitConverter.php
* Class for converting between different unit-lengths as specified by
* Units information array. Units are grouped into measuring systems
* (English, Metric), and are assigned an integer representing
* the conversion factor between that unit and the smallest unit in
* the system. Numeric indexes are actually magical constants that
* encode conversion data from one system to the next, with a O(n^2)
* constraint on memory (this is generally not a problem, since
* the number of measuring systems is small.)
protected static $units =
array(
'px' =>
3, // This is as per CSS 2.1 and Firefox. Your mileage may vary
self::METRIC =>
array('pt', '0.352777778', 'mm'),
self::ENGLISH =>
array('mm', '2.83464567', 'pt'),
* Minimum bcmath precision for output.
* Bcmath precision for internal calculations.
* Whether or not BCMath is available
public function __construct($output_precision =
4, $internal_precision =
10, $force_no_bcmath =
false) {
* Converts a length object of one unit into another unit.
* @param HTMLPurifier_Length $length
* Instance of HTMLPurifier_Length to convert. You must validate()
* it before passing it here!
* About precision: This conversion function pays very special
* attention to the incoming precision of values and attempts
* to maintain a number of significant figure. Results are
* fairly accurate up to nine digits. Some caveats:
* - If a number is zero-padded as a result of this significant
* figure tracking, the zeroes will be eliminated.
* - If a number contains less than four sigfigs ($outputPrecision)
* and this causes some decimals to be excluded, those
* decimals will be added on.
public function convert($length, $to_unit) {
if (!$length->isValid()) return false;
$unit =
$length->getUnit();
if ($n ===
'0' ||
$unit ===
false) {
$state =
$dest_state =
false;
foreach (self::$units as $k =>
$x) {
if (isset
($x[$unit])) $state =
$k;
if (isset
($x[$to_unit])) $dest_state =
$k;
if (!$state ||
!$dest_state) return false;
// Some calculations about the initial precision of the number;
// this will be useful when we need to do final rounding.
// BCMath's internal precision deals only with decimals. Use
// our default if the initial number has no decimals, or increase
// it by how ever many decimals, thus, the number of guard digits
// will always be greater than or equal to internalPrecision.
for ($i =
0; $i <
2; $i++
) {
// Determine what unit IN THIS SYSTEM we need to convert to
if ($dest_state ===
$state) {
// Convert to the smallest unit, pending a system shift
$dest_unit =
self::$units[$state][$dest_state][0];
// Do the conversion if necessary
if ($dest_unit !==
$unit) {
$factor =
$this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp);
$n =
$this->mul($n, $factor, $cp);
// Output was zero, so bail out early. Shouldn't ever happen.
// It was a simple conversion, so bail out
if ($dest_state ===
$state) {
// Conversion failed! Apparently, the system we forwarded
// to didn't have this unit. This should never happen!
// Pre-condition: $i == 0
// Perform conversion to next system of units
$n =
$this->mul($n, self::$units[$state][$dest_state][1], $cp);
$unit =
self::$units[$state][$dest_state][2];
// One more loop around to convert the unit in the new system.
// Post-condition: $unit == $to_unit
if ($unit !==
$to_unit) return false;
//echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n";
$n =
$this->round($n, $sigfigs);
* Returns the number of significant figures in a string number.
* @param string $n Decimal number
* @return int number of sigfigs
$dp =
strpos($n, '.'); // decimal position
$sigfigs =
strlen(ltrim($n, '0.')); // eliminate extra decimal character
if ($dp !==
0) $sigfigs--
;
* Adds two numbers, using arbitrary precision when available.
private function add($s1, $s2, $scale) {
if ($this->bcmath) return bcadd($s1, $s2, $scale);
else return $this->scale($s1 +
$s2, $scale);
* Multiples two numbers, using arbitrary precision when available.
private function mul($s1, $s2, $scale) {
if ($this->bcmath) return bcmul($s1, $s2, $scale);
else return $this->scale($s1 *
$s2, $scale);
* Divides two numbers, using arbitrary precision when available.
private function div($s1, $s2, $scale) {
if ($this->bcmath) return bcdiv($s1, $s2, $scale);
else return $this->scale($s1 /
$s2, $scale);
* Rounds a number according to the number of sigfigs it should have,
* using arbitrary precision when available.
private function round($n, $sigfigs) {
$new_log = (int)
floor(log(abs($n), 10)); // Number of digits left of decimal - 1
$rp =
$sigfigs -
$new_log -
1; // Number of decimal places needed
$neg =
$n <
0 ?
'-' :
''; // Negative sign
$n =
bcdiv($n, '1', $rp);
// This algorithm partially depends on the standardized
// form of numbers that comes out of bcmath.
return $this->scale(round($n, $sigfigs -
$new_log -
1), $rp +
1);
* Scales a float to $scale digits right of decimal point, like BCMath.
private function scale($r, $scale) {
return sprintf('%.' .
$scale .
'f', (float)
$r);
Documentation generated on Thu, 19 Jun 2008 18:50:27 -0400 by phpDocumentor 1.4.2