1<?php
2
3namespace PhpOffice\PhpSpreadsheet\Shared;
4
5use GdImage;
6
7class Drawing
8{
9    /**
10     * Convert pixels to EMU.
11     *
12     * @param int $pValue Value in pixels
13     *
14     * @return int Value in EMU
15     */
16    public static function pixelsToEMU($pValue)
17    {
18        return round($pValue * 9525);
19    }
20
21    /**
22     * Convert EMU to pixels.
23     *
24     * @param int $pValue Value in EMU
25     *
26     * @return int Value in pixels
27     */
28    public static function EMUToPixels($pValue)
29    {
30        if ($pValue != 0) {
31            return round($pValue / 9525);
32        }
33
34        return 0;
35    }
36
37    /**
38     * Convert pixels to column width. Exact algorithm not known.
39     * By inspection of a real Excel file using Calibri 11, one finds 1000px ~ 142.85546875
40     * This gives a conversion factor of 7. Also, we assume that pixels and font size are proportional.
41     *
42     * @param int $pValue Value in pixels
43     * @param \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont Default font of the workbook
44     *
45     * @return int Value in cell dimension
46     */
47    public static function pixelsToCellDimension($pValue, \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont)
48    {
49        // Font name and size
50        $name = $pDefaultFont->getName();
51        $size = $pDefaultFont->getSize();
52
53        if (isset(Font::$defaultColumnWidths[$name][$size])) {
54            // Exact width can be determined
55            $colWidth = $pValue * Font::$defaultColumnWidths[$name][$size]['width'] / Font::$defaultColumnWidths[$name][$size]['px'];
56        } else {
57            // We don't have data for this particular font and size, use approximation by
58            // extrapolating from Calibri 11
59            $colWidth = $pValue * 11 * Font::$defaultColumnWidths['Calibri'][11]['width'] / Font::$defaultColumnWidths['Calibri'][11]['px'] / $size;
60        }
61
62        return $colWidth;
63    }
64
65    /**
66     * Convert column width from (intrinsic) Excel units to pixels.
67     *
68     * @param float $pValue Value in cell dimension
69     * @param \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont Default font of the workbook
70     *
71     * @return int Value in pixels
72     */
73    public static function cellDimensionToPixels($pValue, \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont)
74    {
75        // Font name and size
76        $name = $pDefaultFont->getName();
77        $size = $pDefaultFont->getSize();
78
79        if (isset(Font::$defaultColumnWidths[$name][$size])) {
80            // Exact width can be determined
81            $colWidth = $pValue * Font::$defaultColumnWidths[$name][$size]['px'] / Font::$defaultColumnWidths[$name][$size]['width'];
82        } else {
83            // We don't have data for this particular font and size, use approximation by
84            // extrapolating from Calibri 11
85            $colWidth = $pValue * $size * Font::$defaultColumnWidths['Calibri'][11]['px'] / Font::$defaultColumnWidths['Calibri'][11]['width'] / 11;
86        }
87
88        // Round pixels to closest integer
89        $colWidth = (int) round($colWidth);
90
91        return $colWidth;
92    }
93
94    /**
95     * Convert pixels to points.
96     *
97     * @param int $pValue Value in pixels
98     *
99     * @return float Value in points
100     */
101    public static function pixelsToPoints($pValue)
102    {
103        return $pValue * 0.75;
104    }
105
106    /**
107     * Convert points to pixels.
108     *
109     * @param int $pValue Value in points
110     *
111     * @return int Value in pixels
112     */
113    public static function pointsToPixels($pValue)
114    {
115        if ($pValue != 0) {
116            return (int) ceil($pValue / 0.75);
117        }
118
119        return 0;
120    }
121
122    /**
123     * Convert degrees to angle.
124     *
125     * @param int $pValue Degrees
126     *
127     * @return int Angle
128     */
129    public static function degreesToAngle($pValue)
130    {
131        return (int) round($pValue * 60000);
132    }
133
134    /**
135     * Convert angle to degrees.
136     *
137     * @param int $pValue Angle
138     *
139     * @return int Degrees
140     */
141    public static function angleToDegrees($pValue)
142    {
143        if ($pValue != 0) {
144            return round($pValue / 60000);
145        }
146
147        return 0;
148    }
149
150    /**
151     * Create a new image from file. By alexander at alexauto dot nl.
152     *
153     * @see http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
154     *
155     * @param string $p_sFile Path to Windows DIB (BMP) image
156     *
157     * @return GdImage|resource
158     */
159    public static function imagecreatefrombmp($p_sFile)
160    {
161        //    Load the image into a string
162        $file = fopen($p_sFile, 'rb');
163        $read = fread($file, 10);
164        while (!feof($file) && ($read != '')) {
165            $read .= fread($file, 1024);
166        }
167
168        $temp = unpack('H*', $read);
169        $hex = $temp[1];
170        $header = substr($hex, 0, 108);
171
172        //    Process the header
173        //    Structure: http://www.fastgraph.com/help/bmp_header_format.html
174        if (substr($header, 0, 4) == '424d') {
175            //    Cut it in parts of 2 bytes
176            $header_parts = str_split($header, 2);
177
178            //    Get the width        4 bytes
179            $width = hexdec($header_parts[19] . $header_parts[18]);
180
181            //    Get the height        4 bytes
182            $height = hexdec($header_parts[23] . $header_parts[22]);
183
184            //    Unset the header params
185            unset($header_parts);
186        }
187
188        //    Define starting X and Y
189        $x = 0;
190        $y = 1;
191
192        //    Create newimage
193        $image = imagecreatetruecolor($width, $height);
194
195        //    Grab the body from the image
196        $body = substr($hex, 108);
197
198        //    Calculate if padding at the end-line is needed
199        //    Divided by two to keep overview.
200        //    1 byte = 2 HEX-chars
201        $body_size = (strlen($body) / 2);
202        $header_size = ($width * $height);
203
204        //    Use end-line padding? Only when needed
205        $usePadding = ($body_size > ($header_size * 3) + 4);
206
207        //    Using a for-loop with index-calculation instaid of str_split to avoid large memory consumption
208        //    Calculate the next DWORD-position in the body
209        for ($i = 0; $i < $body_size; $i += 3) {
210            //    Calculate line-ending and padding
211            if ($x >= $width) {
212                // If padding needed, ignore image-padding
213                // Shift i to the ending of the current 32-bit-block
214                if ($usePadding) {
215                    $i += $width % 4;
216                }
217
218                //    Reset horizontal position
219                $x = 0;
220
221                //    Raise the height-position (bottom-up)
222                ++$y;
223
224                //    Reached the image-height? Break the for-loop
225                if ($y > $height) {
226                    break;
227                }
228            }
229
230            // Calculation of the RGB-pixel (defined as BGR in image-data)
231            // Define $i_pos as absolute position in the body
232            $i_pos = $i * 2;
233            $r = hexdec($body[$i_pos + 4] . $body[$i_pos + 5]);
234            $g = hexdec($body[$i_pos + 2] . $body[$i_pos + 3]);
235            $b = hexdec($body[$i_pos] . $body[$i_pos + 1]);
236
237            // Calculate and draw the pixel
238            $color = imagecolorallocate($image, $r, $g, $b);
239            imagesetpixel($image, $x, $height - $y, $color);
240
241            // Raise the horizontal position
242            ++$x;
243        }
244
245        // Unset the body / free the memory
246        unset($body);
247
248        //    Return image-object
249        return $image;
250    }
251}
252