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