1<?php
2
3namespace PhpOffice\PhpSpreadsheet\Worksheet;
4
5use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
6use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
7use PhpOffice\PhpSpreadsheet\IComparable;
8
9class BaseDrawing implements IComparable
10{
11    /**
12     * Image counter.
13     *
14     * @var int
15     */
16    private static $imageCounter = 0;
17
18    /**
19     * Image index.
20     *
21     * @var int
22     */
23    private $imageIndex = 0;
24
25    /**
26     * Name.
27     *
28     * @var string
29     */
30    protected $name;
31
32    /**
33     * Description.
34     *
35     * @var string
36     */
37    protected $description;
38
39    /**
40     * Worksheet.
41     *
42     * @var Worksheet
43     */
44    protected $worksheet;
45
46    /**
47     * Coordinates.
48     *
49     * @var string
50     */
51    protected $coordinates;
52
53    /**
54     * Offset X.
55     *
56     * @var int
57     */
58    protected $offsetX;
59
60    /**
61     * Offset Y.
62     *
63     * @var int
64     */
65    protected $offsetY;
66
67    /**
68     * Width.
69     *
70     * @var int
71     */
72    protected $width;
73
74    /**
75     * Height.
76     *
77     * @var int
78     */
79    protected $height;
80
81    /**
82     * Proportional resize.
83     *
84     * @var bool
85     */
86    protected $resizeProportional;
87
88    /**
89     * Rotation.
90     *
91     * @var int
92     */
93    protected $rotation;
94
95    /**
96     * Shadow.
97     *
98     * @var Drawing\Shadow
99     */
100    protected $shadow;
101
102    /**
103     * Image hyperlink.
104     *
105     * @var null|Hyperlink
106     */
107    private $hyperlink;
108
109    /**
110     * Create a new BaseDrawing.
111     */
112    public function __construct()
113    {
114        // Initialise values
115        $this->name = '';
116        $this->description = '';
117        $this->worksheet = null;
118        $this->coordinates = 'A1';
119        $this->offsetX = 0;
120        $this->offsetY = 0;
121        $this->width = 0;
122        $this->height = 0;
123        $this->resizeProportional = true;
124        $this->rotation = 0;
125        $this->shadow = new Drawing\Shadow();
126
127        // Set image index
128        ++self::$imageCounter;
129        $this->imageIndex = self::$imageCounter;
130    }
131
132    /**
133     * Get image index.
134     *
135     * @return int
136     */
137    public function getImageIndex()
138    {
139        return $this->imageIndex;
140    }
141
142    /**
143     * Get Name.
144     *
145     * @return string
146     */
147    public function getName()
148    {
149        return $this->name;
150    }
151
152    /**
153     * Set Name.
154     *
155     * @param string $pValue
156     *
157     * @return BaseDrawing
158     */
159    public function setName($pValue)
160    {
161        $this->name = $pValue;
162
163        return $this;
164    }
165
166    /**
167     * Get Description.
168     *
169     * @return string
170     */
171    public function getDescription()
172    {
173        return $this->description;
174    }
175
176    /**
177     * Set Description.
178     *
179     * @param string $description
180     *
181     * @return BaseDrawing
182     */
183    public function setDescription($description)
184    {
185        $this->description = $description;
186
187        return $this;
188    }
189
190    /**
191     * Get Worksheet.
192     *
193     * @return Worksheet
194     */
195    public function getWorksheet()
196    {
197        return $this->worksheet;
198    }
199
200    /**
201     * Set Worksheet.
202     *
203     * @param Worksheet $pValue
204     * @param bool $pOverrideOld If a Worksheet has already been assigned, overwrite it and remove image from old Worksheet?
205     *
206     * @throws PhpSpreadsheetException
207     *
208     * @return BaseDrawing
209     */
210    public function setWorksheet(Worksheet $pValue = null, $pOverrideOld = false)
211    {
212        if ($this->worksheet === null) {
213            // Add drawing to \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
214            $this->worksheet = $pValue;
215            $this->worksheet->getCell($this->coordinates);
216            $this->worksheet->getDrawingCollection()->append($this);
217        } else {
218            if ($pOverrideOld) {
219                // Remove drawing from old \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
220                $iterator = $this->worksheet->getDrawingCollection()->getIterator();
221
222                while ($iterator->valid()) {
223                    if ($iterator->current()->getHashCode() == $this->getHashCode()) {
224                        $this->worksheet->getDrawingCollection()->offsetUnset($iterator->key());
225                        $this->worksheet = null;
226
227                        break;
228                    }
229                }
230
231                // Set new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
232                $this->setWorksheet($pValue);
233            } else {
234                throw new PhpSpreadsheetException('A Worksheet has already been assigned. Drawings can only exist on one \\PhpOffice\\PhpSpreadsheet\\Worksheet.');
235            }
236        }
237
238        return $this;
239    }
240
241    /**
242     * Get Coordinates.
243     *
244     * @return string
245     */
246    public function getCoordinates()
247    {
248        return $this->coordinates;
249    }
250
251    /**
252     * Set Coordinates.
253     *
254     * @param string $pValue eg: 'A1'
255     *
256     * @return BaseDrawing
257     */
258    public function setCoordinates($pValue)
259    {
260        $this->coordinates = $pValue;
261
262        return $this;
263    }
264
265    /**
266     * Get OffsetX.
267     *
268     * @return int
269     */
270    public function getOffsetX()
271    {
272        return $this->offsetX;
273    }
274
275    /**
276     * Set OffsetX.
277     *
278     * @param int $pValue
279     *
280     * @return BaseDrawing
281     */
282    public function setOffsetX($pValue)
283    {
284        $this->offsetX = $pValue;
285
286        return $this;
287    }
288
289    /**
290     * Get OffsetY.
291     *
292     * @return int
293     */
294    public function getOffsetY()
295    {
296        return $this->offsetY;
297    }
298
299    /**
300     * Set OffsetY.
301     *
302     * @param int $pValue
303     *
304     * @return BaseDrawing
305     */
306    public function setOffsetY($pValue)
307    {
308        $this->offsetY = $pValue;
309
310        return $this;
311    }
312
313    /**
314     * Get Width.
315     *
316     * @return int
317     */
318    public function getWidth()
319    {
320        return $this->width;
321    }
322
323    /**
324     * Set Width.
325     *
326     * @param int $pValue
327     *
328     * @return BaseDrawing
329     */
330    public function setWidth($pValue)
331    {
332        // Resize proportional?
333        if ($this->resizeProportional && $pValue != 0) {
334            $ratio = $this->height / ($this->width != 0 ? $this->width : 1);
335            $this->height = round($ratio * $pValue);
336        }
337
338        // Set width
339        $this->width = $pValue;
340
341        return $this;
342    }
343
344    /**
345     * Get Height.
346     *
347     * @return int
348     */
349    public function getHeight()
350    {
351        return $this->height;
352    }
353
354    /**
355     * Set Height.
356     *
357     * @param int $pValue
358     *
359     * @return BaseDrawing
360     */
361    public function setHeight($pValue)
362    {
363        // Resize proportional?
364        if ($this->resizeProportional && $pValue != 0) {
365            $ratio = $this->width / ($this->height != 0 ? $this->height : 1);
366            $this->width = round($ratio * $pValue);
367        }
368
369        // Set height
370        $this->height = $pValue;
371
372        return $this;
373    }
374
375    /**
376     * Set width and height with proportional resize.
377     *
378     * Example:
379     * <code>
380     * $objDrawing->setResizeProportional(true);
381     * $objDrawing->setWidthAndHeight(160,120);
382     * </code>
383     *
384     * @author Vincent@luo MSN:kele_100@hotmail.com
385     *
386     * @param int $width
387     * @param int $height
388     *
389     * @return BaseDrawing
390     */
391    public function setWidthAndHeight($width, $height)
392    {
393        $xratio = $width / ($this->width != 0 ? $this->width : 1);
394        $yratio = $height / ($this->height != 0 ? $this->height : 1);
395        if ($this->resizeProportional && !($width == 0 || $height == 0)) {
396            if (($xratio * $this->height) < $height) {
397                $this->height = ceil($xratio * $this->height);
398                $this->width = $width;
399            } else {
400                $this->width = ceil($yratio * $this->width);
401                $this->height = $height;
402            }
403        } else {
404            $this->width = $width;
405            $this->height = $height;
406        }
407
408        return $this;
409    }
410
411    /**
412     * Get ResizeProportional.
413     *
414     * @return bool
415     */
416    public function getResizeProportional()
417    {
418        return $this->resizeProportional;
419    }
420
421    /**
422     * Set ResizeProportional.
423     *
424     * @param bool $pValue
425     *
426     * @return BaseDrawing
427     */
428    public function setResizeProportional($pValue)
429    {
430        $this->resizeProportional = $pValue;
431
432        return $this;
433    }
434
435    /**
436     * Get Rotation.
437     *
438     * @return int
439     */
440    public function getRotation()
441    {
442        return $this->rotation;
443    }
444
445    /**
446     * Set Rotation.
447     *
448     * @param int $pValue
449     *
450     * @return BaseDrawing
451     */
452    public function setRotation($pValue)
453    {
454        $this->rotation = $pValue;
455
456        return $this;
457    }
458
459    /**
460     * Get Shadow.
461     *
462     * @return Drawing\Shadow
463     */
464    public function getShadow()
465    {
466        return $this->shadow;
467    }
468
469    /**
470     * Set Shadow.
471     *
472     * @param Drawing\Shadow $pValue
473     *
474     * @return BaseDrawing
475     */
476    public function setShadow(Drawing\Shadow $pValue = null)
477    {
478        $this->shadow = $pValue;
479
480        return $this;
481    }
482
483    /**
484     * Get hash code.
485     *
486     * @return string Hash code
487     */
488    public function getHashCode()
489    {
490        return md5(
491            $this->name .
492            $this->description .
493            $this->worksheet->getHashCode() .
494            $this->coordinates .
495            $this->offsetX .
496            $this->offsetY .
497            $this->width .
498            $this->height .
499            $this->rotation .
500            $this->shadow->getHashCode() .
501            __CLASS__
502        );
503    }
504
505    /**
506     * Implement PHP __clone to create a deep clone, not just a shallow copy.
507     */
508    public function __clone()
509    {
510        $vars = get_object_vars($this);
511        foreach ($vars as $key => $value) {
512            if ($key == 'worksheet') {
513                $this->worksheet = null;
514            } elseif (is_object($value)) {
515                $this->$key = clone $value;
516            } else {
517                $this->$key = $value;
518            }
519        }
520    }
521
522    /**
523     * @param null|Hyperlink $pHyperlink
524     */
525    public function setHyperlink(Hyperlink $pHyperlink = null)
526    {
527        $this->hyperlink = $pHyperlink;
528    }
529
530    /**
531     * @return null|Hyperlink
532     */
533    public function getHyperlink()
534    {
535        return $this->hyperlink;
536    }
537}
538