• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPOffice / PhpSpreadsheet / 18013951085

25 Sep 2025 04:20PM UTC coverage: 95.867% (+0.3%) from 95.602%
18013951085

push

github

web-flow
Merge pull request #4663 from oleibman/tweakcoveralls

Tweak Coveralls

45116 of 47061 relevant lines covered (95.87%)

373.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.63
/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
1
<?php
2

3
namespace PhpOffice\PhpSpreadsheet\Worksheet;
4

5
use GdImage;
6
use PhpOffice\PhpSpreadsheet\Exception;
7
use PhpOffice\PhpSpreadsheet\Shared\File;
8

9
class MemoryDrawing extends BaseDrawing
10
{
11
    // Rendering functions
12
    const RENDERING_DEFAULT = 'imagepng';
13
    const RENDERING_PNG = 'imagepng';
14
    const RENDERING_GIF = 'imagegif';
15
    const RENDERING_JPEG = 'imagejpeg';
16

17
    // MIME types
18
    const MIMETYPE_DEFAULT = 'image/png';
19
    const MIMETYPE_PNG = 'image/png';
20
    const MIMETYPE_GIF = 'image/gif';
21
    const MIMETYPE_JPEG = 'image/jpeg';
22

23
    const SUPPORTED_MIME_TYPES = [
24
        self::MIMETYPE_GIF,
25
        self::MIMETYPE_JPEG,
26
        self::MIMETYPE_PNG,
27
    ];
28

29
    /**
30
     * Image resource.
31
     */
32
    private null|GdImage $imageResource = null;
33

34
    /**
35
     * Rendering function.
36
     *
37
     * @var callable-string
38
     */
39
    private string $renderingFunction;
40

41
    /**
42
     * Mime type.
43
     */
44
    private string $mimeType;
45

46
    /**
47
     * Unique name.
48
     */
49
    private string $uniqueName;
50

51
    /**
52
     * Create a new MemoryDrawing.
53
     */
54
    public function __construct()
26✔
55
    {
56
        // Initialise values
57
        $this->renderingFunction = self::RENDERING_DEFAULT;
26✔
58
        $this->mimeType = self::MIMETYPE_DEFAULT;
26✔
59
        $this->uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
26✔
60

61
        // Initialize parent
62
        parent::__construct();
26✔
63
    }
64

65
    public function __destruct()
7✔
66
    {
67
        $this->imageResource = null;
7✔
68
        $this->worksheet = null;
7✔
69
    }
70

71
    public function __clone()
1✔
72
    {
73
        parent::__clone();
1✔
74
        $this->cloneResource();
1✔
75
    }
76

77
    private function cloneResource(): void
1✔
78
    {
79
        if (!$this->imageResource) {
1✔
80
            return;
×
81
        }
82

83
        $width = (int) imagesx($this->imageResource);
1✔
84
        $height = (int) imagesy($this->imageResource);
1✔
85

86
        if (imageistruecolor($this->imageResource)) {
1✔
87
            $clone = imagecreatetruecolor($width, $height);
1✔
88
            if (!$clone) {
1✔
89
                throw new Exception('Could not clone image resource');
×
90
            }
91

92
            imagealphablending($clone, false);
1✔
93
            imagesavealpha($clone, true);
1✔
94
        } else {
95
            $clone = imagecreate($width, $height);
×
96
            if (!$clone) {
×
97
                throw new Exception('Could not clone image resource');
×
98
            }
99

100
            // If the image has transparency...
101
            $transparent = imagecolortransparent($this->imageResource);
×
102
            if ($transparent >= 0) {
×
103
                // Starting with Php8.0, next function throws rather than return false
104
                $rgb = imagecolorsforindex($this->imageResource, $transparent);
×
105

106
                imagesavealpha($clone, true);
×
107
                $color = imagecolorallocatealpha($clone, $rgb['red'], $rgb['green'], $rgb['blue'], $rgb['alpha']);
×
108
                if ($color === false) {
×
109
                    throw new Exception('Could not get image alpha color');
×
110
                }
111

112
                imagefill($clone, 0, 0, $color);
×
113
            }
114
        }
115

116
        //Create the Clone!!
117
        imagecopy($clone, $this->imageResource, 0, 0, 0, 0, $width, $height);
1✔
118

119
        $this->imageResource = $clone;
1✔
120
    }
121

122
    /**
123
     * @param resource $imageStream Stream data to be converted to a Memory Drawing
124
     *
125
     * @throws Exception
126
     */
127
    public static function fromStream($imageStream): self
1✔
128
    {
129
        $streamValue = stream_get_contents($imageStream);
1✔
130

131
        return self::fromString($streamValue);
1✔
132
    }
133

134
    /**
135
     * @param string $imageString String data to be converted to a Memory Drawing
136
     *
137
     * @throws Exception
138
     */
139
    public static function fromString(string $imageString): self
5✔
140
    {
141
        $gdImage = @imagecreatefromstring($imageString);
5✔
142
        if ($gdImage === false) {
5✔
143
            throw new Exception('Value cannot be converted to an image');
1✔
144
        }
145

146
        $mimeType = self::identifyMimeType($imageString);
4✔
147
        if (imageistruecolor($gdImage) || imagecolortransparent($gdImage) >= 0) {
4✔
148
            imagesavealpha($gdImage, true);
4✔
149
        }
150
        $renderingFunction = self::identifyRenderingFunction($mimeType);
4✔
151

152
        $drawing = new self();
4✔
153
        $drawing->setImageResource($gdImage);
4✔
154
        $drawing->setRenderingFunction($renderingFunction);
4✔
155
        $drawing->setMimeType($mimeType);
4✔
156

157
        return $drawing;
4✔
158
    }
159

160
    /** @return callable-string */
161
    private static function identifyRenderingFunction(string $mimeType): string
4✔
162
    {
163
        return match ($mimeType) {
4✔
164
            self::MIMETYPE_PNG => self::RENDERING_PNG,
2✔
165
            self::MIMETYPE_JPEG => self::RENDERING_JPEG,
2✔
166
            self::MIMETYPE_GIF => self::RENDERING_GIF,
×
167
            default => self::RENDERING_DEFAULT,
4✔
168
        };
4✔
169
    }
170

171
    /**
172
     * @throws Exception
173
     */
174
    private static function identifyMimeType(string $imageString): string
4✔
175
    {
176
        $temporaryFileName = File::temporaryFilename();
4✔
177
        file_put_contents($temporaryFileName, $imageString);
4✔
178

179
        $mimeType = self::identifyMimeTypeUsingExif($temporaryFileName);
4✔
180
        if ($mimeType !== null) {
4✔
181
            unlink($temporaryFileName);
4✔
182

183
            return $mimeType;
4✔
184
        }
185

186
        $mimeType = self::identifyMimeTypeUsingGd($temporaryFileName);
×
187
        if ($mimeType !== null) {
×
188
            unlink($temporaryFileName);
×
189

190
            return $mimeType;
×
191
        }
192

193
        unlink($temporaryFileName);
×
194

195
        return self::MIMETYPE_DEFAULT;
×
196
    }
197

198
    private static function identifyMimeTypeUsingExif(string $temporaryFileName): ?string
4✔
199
    {
200
        if (function_exists('exif_imagetype')) {
4✔
201
            $imageType = @exif_imagetype($temporaryFileName);
4✔
202
            $mimeType = ($imageType) ? image_type_to_mime_type($imageType) : null;
4✔
203

204
            return self::supportedMimeTypes($mimeType);
4✔
205
        }
206

207
        return null;
×
208
    }
209

210
    private static function identifyMimeTypeUsingGd(string $temporaryFileName): ?string
×
211
    {
212
        if (function_exists('getimagesize')) {
×
213
            $imageSize = @getimagesize($temporaryFileName);
×
214
            if (is_array($imageSize)) {
×
215
                $mimeType = $imageSize['mime'];
×
216

217
                return self::supportedMimeTypes($mimeType);
×
218
            }
219
        }
220

221
        return null;
×
222
    }
223

224
    private static function supportedMimeTypes(?string $mimeType = null): ?string
4✔
225
    {
226
        if (in_array($mimeType, self::SUPPORTED_MIME_TYPES, true)) {
4✔
227
            return $mimeType;
4✔
228
        }
229

230
        return null;
×
231
    }
232

233
    /**
234
     * Get image resource.
235
     */
236
    public function getImageResource(): ?GdImage
21✔
237
    {
238
        return $this->imageResource;
21✔
239
    }
240

241
    /**
242
     * Set image resource.
243
     *
244
     * @return $this
245
     */
246
    public function setImageResource(?GdImage $value): static
26✔
247
    {
248
        $this->imageResource = $value;
26✔
249

250
        if ($this->imageResource !== null) {
26✔
251
            // Get width/height
252
            $this->width = (int) imagesx($this->imageResource);
26✔
253
            $this->height = (int) imagesy($this->imageResource);
26✔
254
        }
255

256
        return $this;
26✔
257
    }
258

259
    /**
260
     * Get rendering function.
261
     *
262
     * @return callable-string
263
     */
264
    public function getRenderingFunction(): string
14✔
265
    {
266
        return $this->renderingFunction;
14✔
267
    }
268

269
    /**
270
     * Set rendering function.
271
     *
272
     * @param callable-string $value see self::RENDERING_*
273
     *
274
     * @return $this
275
     */
276
    public function setRenderingFunction(string $value): static
23✔
277
    {
278
        $this->renderingFunction = $value;
23✔
279

280
        return $this;
23✔
281
    }
282

283
    /**
284
     * Get mime type.
285
     */
286
    public function getMimeType(): string
17✔
287
    {
288
        return $this->mimeType;
17✔
289
    }
290

291
    /**
292
     * Set mime type.
293
     *
294
     * @param string $value see self::MIMETYPE_*
295
     *
296
     * @return $this
297
     */
298
    public function setMimeType(string $value): static
23✔
299
    {
300
        $this->mimeType = $value;
23✔
301

302
        return $this;
23✔
303
    }
304

305
    /**
306
     * Get indexed filename (using image index).
307
     */
308
    public function getIndexedFilename(): string
11✔
309
    {
310
        $extension = strtolower($this->getMimeType());
11✔
311
        $extension = explode('/', $extension);
11✔
312
        $extension = $extension[1];
11✔
313

314
        return $this->uniqueName . $this->getImageIndex() . '.' . $extension;
11✔
315
    }
316

317
    /**
318
     * Get hash code.
319
     *
320
     * @return string Hash code
321
     */
322
    public function getHashCode(): string
11✔
323
    {
324
        return md5(
11✔
325
            $this->renderingFunction
11✔
326
            . $this->mimeType
11✔
327
            . $this->uniqueName
11✔
328
            . parent::getHashCode()
11✔
329
            . __CLASS__
11✔
330
        );
11✔
331
    }
332
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc