1<?php
2/**
3 * Simple class to work with units (e.g. 'px', 'pt', 'cm'...)
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     LarsDW223
7 */
8
9/**
10 * Class helper_plugin_odt_units
11 */
12class ODTUnits {
13    // All static variables with fixed values
14    // Measure units as defined in "Extensible Stylesheet Language (XSL) Version 1.1"
15    protected static $xsl_units = array('cm', 'mm', 'in', 'pt', 'pc', 'px', 'em');
16    protected static $point_in_cm = 0.035277778;
17    protected static $inch_in_cm = 2.54;
18    protected static $inch_in_pt = 0.089605556;
19    protected static $pc_in_cm = 0.423333336;
20    protected static $pc_in_pt = 12;
21    protected static $twips_per_point = 20;
22
23    // Non static variables, can be changed
24    protected $px_per_em = 14;
25    protected $twips_per_pixel_x = 16;
26    protected $twips_per_pixel_y = 20;
27
28    /**
29     * Strips of the leading digits from $value. So left over will be the unit only.
30     *
31     * @param int $value The length value string, e.g. '1cm'.
32     * @return string The unit of $value, e.g. 'cm'
33     */
34    static public function stripDigits ($value) {
35        return ltrim ($value, '-.0123456789');
36    }
37
38    /**
39     * Gets only the digits from $value without the unit.
40     *
41     * @param string|int $value The length value string, e.g. '1cm'.
42     * @return string The digits of $value, e.g. '1'
43     */
44    static public function getDigits ($value) {
45        $digits = NULL;
46        $length = strlen ((string)$value);
47        for ($index = 0 ; $index < $length ; $index++ ) {
48            if ( is_numeric ($value [$index]) === false &&
49                 $value [$index] != '.' &&
50                 $value [$index] != '-' ) {
51                break;
52            }
53            $digits .= $value [$index];
54        }
55        return $digits;
56    }
57
58    /**
59     * Checks if $unit is a valid XSL unit.
60     *
61     * @param string $unit The unit string, e.g. 'cm'.
62     * @return boolean true if valid, false otherwise
63     */
64    static public function isValidXSLUnit($unit) {
65        return in_array($unit, self::$xsl_units);
66    }
67
68    /**
69     * Checks if length value string $value has a valid XSL unit.
70     *
71     * @param string|int $value The length value string, e.g. '1cm'.
72     * @return boolean true if valid, false otherwise
73     */
74    static public function hasValidXSLUnit($value) {
75        return in_array(self::stripDigits((string)$value), self::$xsl_units);
76    }
77
78    /**
79     * Sets the pixel per em unit used for px to em conversion.
80     *
81     * @param int $value The value to be set.
82     */
83    public function setPixelPerEm ($value) {
84        $this->px_per_em = $value;
85    }
86
87    /**
88     * Query the pixel per em unit.
89     *
90     * @return int The current value.
91     */
92    public function getPixelPerEm () {
93        return $this->px_per_em;
94    }
95
96    /**
97     * Sets the twips per pixel (X axis) used for px to pt conversion.
98     *
99     * @param int $value The value to be set.
100     */
101    public function setTwipsPerPixelX ($value) {
102        $this->twips_per_pixel_x = $value;
103    }
104
105    /**
106     * Sets the twips per pixel (Y axis) unit used for px to pt conversion.
107     *
108     * @param int $value The value to be set.
109     */
110    public function setTwipsPerPixelY ($value) {
111        $this->twips_per_pixel_y = $value;
112    }
113
114    /**
115     * Query the twips per pixel (X axis) setting.
116     *
117     * @return int The current value.
118     */
119    public function getTwipsPerPixelX () {
120        return $this->twips_per_pixel_x;
121    }
122
123    /**
124     * Query the twips per pixel (Y axis) setting.
125     *
126     * @return int The current value.
127     */
128    public function getTwipsPerPixelY () {
129        return $this->twips_per_pixel_y;
130    }
131
132    /**
133     * Convert pixel (X axis) to points according to the current settings.
134     *
135     * @param string|int $pixel String with pixel length value, e.g. '20px'
136     * @return string The current value.
137     */
138    public function pixelToPointsX ($pixel) {
139        $pixel = self::getDigits ((string)$pixel);
140        $value = $pixel * $this->twips_per_pixel_x / self::$twips_per_point;
141        return round ($value, 2).'pt';
142    }
143
144    /**
145     * Convert pixel (Y axis) to points according to the current settings.
146     *
147     * @param string|int $pixel String with pixel length value, e.g. '20px'
148     * @return string The current value.
149     */
150    public function pixelToPointsY ($pixel) {
151        $pixel = self::getDigits ((string)$pixel);
152        $value = $pixel * $this->twips_per_pixel_y / self::$twips_per_point;
153        return round ($value, 2).'pt';
154    }
155
156    /**
157     * Convert length value with valid XSL unit to points.
158     *
159     * @param string $value  String with length value, e.g. '20px', '20cm'...
160     * @param string $axis   Is the value to be converted a value on the X or Y axis? Default is 'y'.
161     *        Only relevant for conversion from 'px' or 'em'.
162     * @return string The current value.
163     */
164    public function toPoints ($value, $axis = 'y') {
165        $unit = self::stripDigits ($value);
166        if ( $unit == 'pt' ) {
167            return $value;
168        }
169
170        if ( self::isValidXSLUnit ($unit) === false  ) {
171            // Not a vlaid/supported unit. Return original value.
172            return $value;
173        }
174
175        $value = self::getDigits ($value);
176        switch ($unit) {
177            case 'cm':
178                $value = round (($value/self::$point_in_cm), 2).'pt';
179            break;
180            case 'mm':
181                $value = round (($value/(10 * self::$point_in_cm)), 2).'pt';
182            break;
183            case 'in':
184                $value = round (($value * self::$inch_in_pt), 2).'pt';
185            break;
186            case 'pc':
187                $value = round (($value * self::$pc_in_pt), 2).'pt';
188            break;
189            case 'px':
190                if ( $axis == 'x' || $axis == 'X' ) {
191                    $value = $this->pixelToPointsX ($value);
192                } else {
193                    $value = $this->pixelToPointsY ($value);
194                }
195            break;
196            case 'em':
197                $value = $this->pixelToPointsY ($value * $this->getPixelPerEm());
198            break;
199        }
200        return $value;
201    }
202
203    /**
204     * Convert length value with valid XSL unit to points.
205     *
206     * @param string $value  String with length value, e.g. '20px', '20pt'...
207     * @param string $axis   Is the value to be converted a value on the X or Y axis? Default is 'y'.
208     *        Only relevant for conversion from 'px' or 'em'.
209     * @return string The current value.
210     */
211    public function toCentimeters ($value, $axis = 'y') {
212        $unit = self::stripDigits ($value);
213        if ( $unit == 'cm' ) {
214            return $value;
215        }
216
217        if ( self::isValidXSLUnit ($unit) === false  ) {
218            // Not a vlaid/supported unit. Return original value.
219            return $value;
220        }
221
222        $value = self::toPoints ($value, $axis);
223        $value = substr($value, 0, -2);
224        $value = round (($value * self::$point_in_cm), 2).'cm';
225        return $value;
226    }
227
228    /**
229     * Convert length value with valid XSL unit to pixel.
230     *
231     * @param string $value  String with length value, e.g. '20pt'...
232     * @param string $axis   Is the value to be converted a value on the X or Y axis? Default is 'y'.
233     *        Only relevant for conversion from 'px' or 'em'.
234     * @return string The current value.
235     */
236    public function toPixel ($value, $axis = 'y') {
237        $unit = self::stripDigits ($value);
238        if ( $unit == 'px' ) {
239            return $value;
240        }
241
242        if ( self::isValidXSLUnit ($unit) === false  ) {
243            // Not a vlaid/supported unit. Return original value.
244            return $value;
245        }
246
247        $value = self::toPoints ($value, $axis);
248        $value = substr($value, 0, -2);
249        if ($axis == 'x') {
250            $value = round ((($value*self::$twips_per_point)/$this->twips_per_pixel_x), 2).'px';
251        } else {
252            $value = round ((($value*self::$twips_per_point)/$this->twips_per_pixel_y), 2).'px';
253        }
254        return $value;
255    }
256
257    public function getAbsoluteValue ($value, $base) {
258        $unit = self::stripDigits ($value);
259
260        $value = self::getDigits ($value);
261        switch ($unit) {
262            case '%':
263                $value = ($value * $base)/100;
264            break;
265            case 'em':
266                $value = $value * $base;
267            break;
268            default:
269                // Not an relative value. Just keep it.
270            break;
271        }
272        return $value;
273    }
274}
275