Completed
Pull Request — master (#95)
by
unknown
02:45
created

Pdf::fetchRemoteFile()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
c 0
b 0
f 0
rs 8.8571
cc 3
eloc 15
nc 3
nop 1
1
<?php
2
3
namespace Spatie\PdfToImage;
4
5
use Imagick;
6
use Spatie\PdfToImage\Exceptions\InvalidFormat;
7
use Spatie\PdfToImage\Exceptions\PdfDoesNotExist;
8
use Spatie\PdfToImage\Exceptions\PageDoesNotExist;
9
use Spatie\PdfToImage\Exceptions\InvalidLayerMethod;
10
use Spatie\PdfToImage\Exceptions\TempFileDoesNotExist;
11
use Spatie\PdfToImage\Exceptions\TempPathNotWritable;
12
use Spatie\PdfToImage\Exceptions\RemoteFileFetchFailed;
13
14
class Pdf
15
{
16
    protected $pdfFile;
17
18
    protected $resolution = 144;
19
20
    protected $outputFormat = 'jpg';
21
22
    protected $page = 1;
23
24
    public $imagick;
25
26
    protected $numberOfPages;
27
28
    protected $validOutputFormats = ['jpg', 'jpeg', 'png'];
29
30
    protected $layerMethod = Imagick::LAYERMETHOD_FLATTEN;
31
32
    protected $colorspace;
33
34
    protected $compressionQuality;
35
36
    protected $isRemoteFile = false;
37
38
    /**
39
     * @param string $pdfFile The path or url to the pdffile.
40
     *
41
     * @throws \Spatie\PdfToImage\Exceptions\PdfDoesNotExist
42
     */
43
    public function __construct($pdfFile)
44
    {
45
        if (! filter_var($pdfFile, FILTER_VALIDATE_URL) && ! file_exists($pdfFile)) {
46
            throw new PdfDoesNotExist();
47
        }
48
49
        if (filter_var($pdfFile, FILTER_VALIDATE_URL)) {
50
            $this->pdfFile = $this->fetchRemoteFile($pdfFile);
51
52
            $this->isRemoteFile = true;
53
        } else {
54
            $this->pdfFile = $pdfFile;
55
        }
56
57
        $this->imagick = new Imagick();
58
59
        $this->imagick->pingImage($this->pdfFile);
60
61
        $this->numberOfPages = $this->imagick->getNumberImages();
62
    }
63
64
    /**
65
     * Set the raster resolution.
66
     *
67
     * @param int $resolution
68
     *
69
     * @return $this
70
     */
71
    public function setResolution($resolution)
72
    {
73
        $this->resolution = $resolution;
74
75
        return $this;
76
    }
77
78
    /**
79
     * Set the output format.
80
     *
81
     * @param string $outputFormat
82
     *
83
     * @return $this
84
     *
85
     * @throws \Spatie\PdfToImage\Exceptions\InvalidFormat
86
     */
87
    public function setOutputFormat($outputFormat)
88
    {
89
        if (! $this->isValidOutputFormat($outputFormat)) {
90
            throw new InvalidFormat("Format {$outputFormat} is not supported");
91
        }
92
93
        $this->outputFormat = $outputFormat;
94
95
        return $this;
96
    }
97
98
    /**
99
     * Get the output format.
100
     *
101
     * @return string
102
     */
103
    public function getOutputFormat()
104
    {
105
        return $this->outputFormat;
106
    }
107
108
    /**
109
     * Sets the layer method for Imagick::mergeImageLayers()
110
     * If int, should correspond to a predefined LAYERMETHOD constant.
111
     * If null, Imagick::mergeImageLayers() will not be called.
112
     *
113
     * @param int|null
114
     *
115
     * @return $this
116
     *
117
     * @throws \Spatie\PdfToImage\Exceptions\InvalidLayerMethod
118
     *
119
     * @see https://secure.php.net/manual/en/imagick.constants.php
120
     * @see Pdf::getImageData()
121
     */
122
    public function setLayerMethod($layerMethod)
123
    {
124
        if (
125
            is_int($layerMethod) === false &&
126
            is_null($layerMethod) === false
127
        ) {
128
            throw new InvalidLayerMethod('LayerMethod must be an integer or null');
129
        }
130
131
        $this->layerMethod = $layerMethod;
132
133
        return $this;
134
    }
135
136
    /**
137
     * Determine if the given format is a valid output format.
138
     *
139
     * @param $outputFormat
140
     *
141
     * @return bool
142
     */
143
    public function isValidOutputFormat($outputFormat)
144
    {
145
        return in_array($outputFormat, $this->validOutputFormats);
146
    }
147
148
    /**
149
     * Set the page number that should be rendered.
150
     *
151
     * @param int $page
152
     *
153
     * @return $this
154
     *
155
     * @throws \Spatie\PdfToImage\Exceptions\PageDoesNotExist
156
     */
157
    public function setPage($page)
158
    {
159
        if ($page > $this->getNumberOfPages()) {
160
            throw new PageDoesNotExist("Page {$page} does not exist");
161
        }
162
163
        $this->page = $page;
164
165
        return $this;
166
    }
167
168
    /**
169
     * Get the number of pages in the pdf file.
170
     *
171
     * @return int
172
     */
173
    public function getNumberOfPages()
174
    {
175
        return $this->numberOfPages;
176
    }
177
178
    /**
179
     * Save the image to the given path.
180
     *
181
     * @param string $pathToImage
182
     * @param bool $clear
183
     *
184
     * @return bool
185
     */
186
    public function saveImage($pathToImage, $clear = true)
187
    {
188
        if (is_dir($pathToImage)) {
189
            $pathToImage = rtrim($pathToImage, '\/').DIRECTORY_SEPARATOR.$this->page.'.'.$this->outputFormat;
190
        }
191
192
        $imageData = $this->getImageData($pathToImage);
193
194
        $status = file_put_contents($pathToImage, $imageData) !== false;
195
        
196
        if ($clear) {
197
            $this->clear();
198
        }
199
200
        return $status;
201
    }
202
203
    /**
204
     * Save the file as images to the given directory.
205
     *
206
     * @param string $directory
207
     * @param string $prefix
208
     *
209
     * @return array $files the paths to the created images
210
     */
211
    public function saveAllPagesAsImages($directory, $prefix = '')
212
    {
213
        $numberOfPages = $this->getNumberOfPages();
214
215
        if ($numberOfPages === 0) {
216
            return [];
217
        }
218
219
        return array_map(function ($pageNumber) use ($directory, $prefix) {
220
            $this->setPage($pageNumber);
221
222
            $destination = "{$directory}/{$prefix}{$pageNumber}.{$this->outputFormat}";
223
224
            $this->saveImage($destination, false);
225
226
            return $destination;
227
        }, range(1, $numberOfPages));
228
229
        $this->clear();
0 ignored issues
show
Unused Code introduced by
$this->clear(); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
230
    }
231
232
    /**
233
     * Return raw image data.
234
     *
235
     * @param string $pathToImage
236
     *
237
     * @return \Imagick
238
     */
239
    public function getImageData($pathToImage)
240
    {
241
        /*
242
         * Reinitialize imagick because the target resolution must be set
243
         * before reading the actual image.
244
         */
245
        $this->imagick = new Imagick();
246
247
        $this->imagick->setResolution($this->resolution, $this->resolution);
248
249
        if ($this->colorspace !== null) {
250
            $this->imagick->setColorspace($this->colorspace);
251
        }
252
253
        if ($this->compressionQuality !== null) {
254
            $this->imagick->setCompressionQuality($this->compressionQuality);
255
        }
256
257
        $this->imagick->readImage(sprintf('%s[%s]', $this->pdfFile, $this->page - 1));
258
259
        if (is_int($this->layerMethod)) {
260
            $this->imagick = $this->imagick->mergeImageLayers($this->layerMethod);
261
        }
262
263
        $this->imagick->setFormat($this->determineOutputFormat($pathToImage));
264
265
        return $this->imagick;
266
    }
267
268
    /**
269
     * @param int $colorspace
270
     *
271
     * @return $this
272
     */
273
    public function setColorspace(int $colorspace)
274
    {
275
        $this->colorspace = $colorspace;
276
277
        return $this;
278
    }
279
280
    /**
281
     * @param int $compressionQuality
282
     *
283
     * @return $this
284
     */
285
    public function setCompressionQuality(int $compressionQuality)
286
    {
287
        $this->compressionQuality = $compressionQuality;
288
289
        return $this;
290
    }
291
292
    /**
293
     * Determine in which format the image must be rendered.
294
     *
295
     * @param $pathToImage
296
     *
297
     * @return string
298
     */
299
    protected function determineOutputFormat($pathToImage)
300
    {
301
        $outputFormat = pathinfo($pathToImage, PATHINFO_EXTENSION);
302
303
        if ($this->outputFormat != '') {
304
            $outputFormat = $this->outputFormat;
305
        }
306
307
        $outputFormat = strtolower($outputFormat);
308
309
        if (! $this->isValidOutputFormat($outputFormat)) {
310
            $outputFormat = 'jpg';
311
        }
312
313
        return $outputFormat;
314
    }
315
316
    /**
317
     * Fetch remote file and save temporary on image dir.
318
     *
319
     * @throws \Spatie\PdfToImage\Exceptions\TempPathNotWritable
320
     * @throws \Spatie\PdfToImage\Exceptions\RemoteFileFetchFailed
321
     *
322
     * @return string
323
     */
324
    protected function fetchRemoteFile($source)
325
    {
326
        $pathToTemp = tempnam(sys_get_temp_dir(), 'pdf');
327
328
        if (!is_writable($pathToTemp)) {
329
            throw new TempPathNotWritable();
330
        }
331
332
        $remote = curl_init($source);
333
334
        $local = fopen($pathToTemp, 'w');
335
        
336
        curl_setopt($remote, CURLOPT_FILE, $local);
337
        
338
        curl_setopt($remote, CURLOPT_TIMEOUT, 60);
339
340
        curl_setopt($remote, CURLOPT_FOLLOWLOCATION, true);
341
342
        curl_exec($remote);
343
344
        if (curl_error($remote)) {
345
            throw new RemoteFileFetchFailed("Remote file fetch failed. Error ".curl_error($remote));
346
        }
347
        
348
        curl_close($remote);
349
        
350
        fclose($local);
351
352
        return $pathToTemp;
353
    }
354
355
    /**
356
     * Delete Temporary pdf file.
357
     *
358
     * @throws \Spatie\PdfToImage\Exceptions\TempFileDoesNotExist
359
     *
360
     * @return bool
361
     */
362
    protected function deleteTempFile()
363
    {
364
        $tempPath = $this->pdfFile;
365
366
        if (!file_exists($tempPath)) {
367
            throw new TempFileDoesNotExist("Temporary file {$tempPath} does not exist");
368
        }
369
370
        return unlink($tempPath);
371
    }
372
373
    /**
374
     * Remove temp file and clear Imagick object
375
     *
376
     * @return bool
377
     */
378
    protected function clear()
379
    {
380
        if ($this->isRemoteFile) {
381
            $this->deleteTempFile();
382
        }
383
384
        return $this->imagick->clear();
385
    }
386
}
387