xref: /webtrees/app/MediaFile.php (revision cdaafeee7d44ae3491555b0c6eaf2bceea8e9d6e)
18f5f5da8SGreg Roach<?php
28f5f5da8SGreg Roach/**
38f5f5da8SGreg Roach * webtrees: online genealogy
41062a142SGreg Roach * Copyright (C) 2018 webtrees development team
58f5f5da8SGreg Roach * This program is free software: you can redistribute it and/or modify
68f5f5da8SGreg Roach * it under the terms of the GNU General Public License as published by
78f5f5da8SGreg Roach * the Free Software Foundation, either version 3 of the License, or
88f5f5da8SGreg Roach * (at your option) any later version.
98f5f5da8SGreg Roach * This program is distributed in the hope that it will be useful,
108f5f5da8SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
118f5f5da8SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
128f5f5da8SGreg Roach * GNU General Public License for more details.
138f5f5da8SGreg Roach * You should have received a copy of the GNU General Public License
148f5f5da8SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
158f5f5da8SGreg Roach */
168f5f5da8SGreg Roachnamespace Fisharebest\Webtrees;
178f5f5da8SGreg Roach
188f5f5da8SGreg Roachuse League\Glide\Urls\UrlBuilderFactory;
192361dfe8SGreg Roachuse Throwable;
208f5f5da8SGreg Roach
218f5f5da8SGreg Roach/**
228f5f5da8SGreg Roach * A GEDCOM media file.  A media object can contain many media files,
238f5f5da8SGreg Roach * such as scans of both sides of a document, the transcript of an audio
248f5f5da8SGreg Roach * recording, etc.
258f5f5da8SGreg Roach */
26c1010edaSGreg Roachclass MediaFile
27c1010edaSGreg Roach{
285225fdfcSGreg Roach    const MIME_TYPES = [
295225fdfcSGreg Roach        'bmp'  => 'image/bmp',
305225fdfcSGreg Roach        'doc'  => 'application/msword',
315225fdfcSGreg Roach        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
325225fdfcSGreg Roach        'ged'  => 'text/x-gedcom',
335225fdfcSGreg Roach        'gif'  => 'image/gif',
345225fdfcSGreg Roach        'html' => 'text/html',
355225fdfcSGreg Roach        'htm'  => 'text/html',
365225fdfcSGreg Roach        'jpeg' => 'image/jpeg',
375225fdfcSGreg Roach        'jpg'  => 'image/jpeg',
385225fdfcSGreg Roach        'mov'  => 'video/quicktime',
395225fdfcSGreg Roach        'mp3'  => 'audio/mpeg',
405225fdfcSGreg Roach        'mp4'  => 'video/mp4',
415225fdfcSGreg Roach        'ogv'  => 'video/ogg',
425225fdfcSGreg Roach        'pdf'  => 'application/pdf',
435225fdfcSGreg Roach        'png'  => 'image/png',
445225fdfcSGreg Roach        'rar'  => 'application/x-rar-compressed',
455225fdfcSGreg Roach        'swf'  => 'application/x-shockwave-flash',
465225fdfcSGreg Roach        'svg'  => 'image/svg',
475225fdfcSGreg Roach        'tiff' => 'image/tiff',
485225fdfcSGreg Roach        'tif'  => 'image/tiff',
495225fdfcSGreg Roach        'xls'  => 'application/vnd-ms-excel',
505225fdfcSGreg Roach        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
515225fdfcSGreg Roach        'wmv'  => 'video/x-ms-wmv',
525225fdfcSGreg Roach        'zip'  => 'application/zip',
535225fdfcSGreg Roach    ];
545225fdfcSGreg Roach
558f5f5da8SGreg Roach    /** @var string The filename */
568f5f5da8SGreg Roach    private $multimedia_file_refn = '';
578f5f5da8SGreg Roach
588f5f5da8SGreg Roach    /** @var string The file extension; jpeg, txt, mp4, etc. */
598f5f5da8SGreg Roach    private $multimedia_format = '';
608f5f5da8SGreg Roach
618f5f5da8SGreg Roach    /** @var string The type of document; newspaper, microfiche, etc. */
628f5f5da8SGreg Roach    private $source_media_type = '';
638f5f5da8SGreg Roach    /** @var string The filename */
648f5f5da8SGreg Roach
658f5f5da8SGreg Roach    /** @var string The name of the document */
668f5f5da8SGreg Roach    private $descriptive_title = '';
678f5f5da8SGreg Roach
688f5f5da8SGreg Roach    /** @var Media $media The media object to which this file belongs */
698f5f5da8SGreg Roach    private $media;
708f5f5da8SGreg Roach
7164b90bf1SGreg Roach    /** @var string */
7264b90bf1SGreg Roach    private $fact_id;
7364b90bf1SGreg Roach
748f5f5da8SGreg Roach    /**
758f5f5da8SGreg Roach     * Create a MediaFile from raw GEDCOM data.
768f5f5da8SGreg Roach     *
778f5f5da8SGreg Roach     * @param string $gedcom
788f5f5da8SGreg Roach     * @param Media  $media
798f5f5da8SGreg Roach     */
80c1010edaSGreg Roach    public function __construct($gedcom, Media $media)
81c1010edaSGreg Roach    {
828f5f5da8SGreg Roach        $this->media   = $media;
8364b90bf1SGreg Roach        $this->fact_id = md5($gedcom);
848f5f5da8SGreg Roach
858f5f5da8SGreg Roach        if (preg_match('/^\d FILE (.+)/m', $gedcom, $match)) {
868f5f5da8SGreg Roach            $this->multimedia_file_refn = $match[1];
87423c6ccdSGreg Roach            $this->multimedia_format    = pathinfo($match[1], PATHINFO_EXTENSION);
888f5f5da8SGreg Roach        }
898f5f5da8SGreg Roach
908f5f5da8SGreg Roach        if (preg_match('/^\d FORM (.+)/m', $gedcom, $match)) {
918f5f5da8SGreg Roach            $this->multimedia_format = $match[1];
928f5f5da8SGreg Roach        }
938f5f5da8SGreg Roach
948f5f5da8SGreg Roach        if (preg_match('/^\d TYPE (.+)/m', $gedcom, $match)) {
958f5f5da8SGreg Roach            $this->source_media_type = $match[1];
968f5f5da8SGreg Roach        }
978f5f5da8SGreg Roach
988f5f5da8SGreg Roach        if (preg_match('/^\d TITL (.+)/m', $gedcom, $match)) {
998f5f5da8SGreg Roach            $this->descriptive_title = $match[1];
1008f5f5da8SGreg Roach        }
1018f5f5da8SGreg Roach    }
1028f5f5da8SGreg Roach
1038f5f5da8SGreg Roach    /**
10464b90bf1SGreg Roach     * Get the filename.
1058f5f5da8SGreg Roach     *
1068f5f5da8SGreg Roach     * @return string
1078f5f5da8SGreg Roach     */
108c1010edaSGreg Roach    public function filename(): string
109c1010edaSGreg Roach    {
1106e6a9947SGreg Roach        return $this->multimedia_file_refn;
1118f5f5da8SGreg Roach    }
1128f5f5da8SGreg Roach
1138f5f5da8SGreg Roach    /**
114d6641c58SGreg Roach     * Get the base part of the filename.
115d6641c58SGreg Roach     *
116d6641c58SGreg Roach     * @return string
117d6641c58SGreg Roach     */
118c1010edaSGreg Roach    public function basename(): string
119c1010edaSGreg Roach    {
120d6641c58SGreg Roach        return basename($this->multimedia_file_refn);
121d6641c58SGreg Roach    }
122d6641c58SGreg Roach
123d6641c58SGreg Roach    /**
124d6641c58SGreg Roach     * Get the folder part of the filename.
125d6641c58SGreg Roach     *
126d6641c58SGreg Roach     * @return string
127d6641c58SGreg Roach     */
128c1010edaSGreg Roach    public function dirname(): string
129c1010edaSGreg Roach    {
130d6641c58SGreg Roach        $dirname = dirname($this->multimedia_file_refn);
131d6641c58SGreg Roach
132d6641c58SGreg Roach        if ($dirname === '.') {
133d6641c58SGreg Roach            return '';
134d6641c58SGreg Roach        }
135b2ce94c6SRico Sonntag
136b2ce94c6SRico Sonntag        return $dirname;
137d6641c58SGreg Roach    }
138d6641c58SGreg Roach
139d6641c58SGreg Roach    /**
14064b90bf1SGreg Roach     * Get the format.
1418f5f5da8SGreg Roach     *
1428f5f5da8SGreg Roach     * @return string
1438f5f5da8SGreg Roach     */
144c1010edaSGreg Roach    public function format(): string
145c1010edaSGreg Roach    {
1468f5f5da8SGreg Roach        return $this->multimedia_format;
1478f5f5da8SGreg Roach    }
1488f5f5da8SGreg Roach
1498f5f5da8SGreg Roach    /**
15064b90bf1SGreg Roach     * Get the type.
1518f5f5da8SGreg Roach     *
1528f5f5da8SGreg Roach     * @return string
1538f5f5da8SGreg Roach     */
154c1010edaSGreg Roach    public function type(): string
155c1010edaSGreg Roach    {
1568f5f5da8SGreg Roach        return $this->source_media_type;
1578f5f5da8SGreg Roach    }
1588f5f5da8SGreg Roach
1598f5f5da8SGreg Roach    /**
16064b90bf1SGreg Roach     * Get the title.
1618f5f5da8SGreg Roach     *
1628f5f5da8SGreg Roach     * @return string
1638f5f5da8SGreg Roach     */
164c1010edaSGreg Roach    public function title(): string
165c1010edaSGreg Roach    {
1668f5f5da8SGreg Roach        return $this->descriptive_title;
1678f5f5da8SGreg Roach    }
1688f5f5da8SGreg Roach
1698f5f5da8SGreg Roach    /**
17064b90bf1SGreg Roach     * Get the fact ID.
17164b90bf1SGreg Roach     *
17264b90bf1SGreg Roach     * @return string
17364b90bf1SGreg Roach     */
174c1010edaSGreg Roach    public function factId(): string
175c1010edaSGreg Roach    {
17664b90bf1SGreg Roach        return $this->fact_id;
17764b90bf1SGreg Roach    }
17864b90bf1SGreg Roach
17964b90bf1SGreg Roach    /**
180d6641c58SGreg Roach     * @return bool
181d6641c58SGreg Roach     */
1828f53f488SRico Sonntag    public function isPendingAddition(): bool
183c1010edaSGreg Roach    {
184d6641c58SGreg Roach        foreach ($this->media->getFacts() as $fact) {
185d6641c58SGreg Roach            if ($fact->getFactId() === $this->fact_id) {
186d6641c58SGreg Roach                return $fact->isPendingAddition();
187d6641c58SGreg Roach            }
188d6641c58SGreg Roach        }
189d6641c58SGreg Roach
190d6641c58SGreg Roach        return false;
191d6641c58SGreg Roach    }
192d6641c58SGreg Roach
193d6641c58SGreg Roach    /**
194d6641c58SGreg Roach     * @return bool
195d6641c58SGreg Roach     */
1968f53f488SRico Sonntag    public function isPendingDeletion(): bool
197c1010edaSGreg Roach    {
198d6641c58SGreg Roach        foreach ($this->media->getFacts() as $fact) {
199d6641c58SGreg Roach            if ($fact->getFactId() === $this->fact_id) {
200d6641c58SGreg Roach                return $fact->isPendingDeletion();
201d6641c58SGreg Roach            }
202d6641c58SGreg Roach        }
203d6641c58SGreg Roach
204d6641c58SGreg Roach        return false;
205d6641c58SGreg Roach    }
206d6641c58SGreg Roach
207d6641c58SGreg Roach    /**
20864b90bf1SGreg Roach     * Display an image-thumbnail or a media-icon, and add markup for image viewers such as colorbox.
20964b90bf1SGreg Roach     *
21064b90bf1SGreg Roach     * @param int      $width      Pixels
21164b90bf1SGreg Roach     * @param int      $height     Pixels
21264b90bf1SGreg Roach     * @param string   $fit        "crop" or "contain"
21364b90bf1SGreg Roach     * @param string[] $attributes Additional HTML attributes
21464b90bf1SGreg Roach     *
21564b90bf1SGreg Roach     * @return string
21664b90bf1SGreg Roach     */
2178f53f488SRico Sonntag    public function displayImage($width, $height, $fit, $attributes = []): string
218c1010edaSGreg Roach    {
21964b90bf1SGreg Roach        if ($this->isExternal()) {
22064b90bf1SGreg Roach            $src    = $this->multimedia_file_refn;
22164b90bf1SGreg Roach            $srcset = [];
22264b90bf1SGreg Roach        } else {
22364b90bf1SGreg Roach            // Generate multiple images for displays with higher pixel densities.
22464b90bf1SGreg Roach            $src    = $this->imageUrl($width, $height, $fit);
22564b90bf1SGreg Roach            $srcset = [];
226c1010edaSGreg Roach            foreach ([
227c1010edaSGreg Roach                         2,
228c1010edaSGreg Roach                         3,
229c1010edaSGreg Roach                         4,
230c1010edaSGreg Roach                     ] as $x) {
23164b90bf1SGreg Roach                $srcset[] = $this->imageUrl($width * $x, $height * $x, $fit) . ' ' . $x . 'x';
23264b90bf1SGreg Roach            }
23364b90bf1SGreg Roach        }
23464b90bf1SGreg Roach
23564b90bf1SGreg Roach        $image = '<img ' . Html::attributes($attributes + [
23664b90bf1SGreg Roach                    'dir'    => 'auto',
23764b90bf1SGreg Roach                    'src'    => $src,
23864b90bf1SGreg Roach                    'srcset' => implode(',', $srcset),
2395be0d03dSGreg Roach                    'alt'    => htmlspecialchars_decode(strip_tags($this->media->getFullName())),
24064b90bf1SGreg Roach                ]) . '>';
24164b90bf1SGreg Roach
242e1d1700bSGreg Roach        if ($this->isImage()) {
24364b90bf1SGreg Roach            $attributes = Html::attributes([
24464b90bf1SGreg Roach                'class'      => 'gallery',
24564b90bf1SGreg Roach                'type'       => $this->mimeType(),
24660e3c46aSGreg Roach                'href'       => $this->imageUrl(0, 0, 'contain'),
2475be0d03dSGreg Roach                'data-title' => htmlspecialchars_decode(strip_tags($this->media->getFullName())),
24864b90bf1SGreg Roach            ]);
249e1d1700bSGreg Roach        } else {
250e1d1700bSGreg Roach            $attributes = Html::attributes([
251e1d1700bSGreg Roach                'type' => $this->mimeType(),
252e1d1700bSGreg Roach                'href' => $this->downloadUrl(),
253e1d1700bSGreg Roach            ]);
254e1d1700bSGreg Roach        }
25564b90bf1SGreg Roach
25664b90bf1SGreg Roach        return '<a ' . $attributes . '>' . $image . '</a>';
25764b90bf1SGreg Roach    }
25864b90bf1SGreg Roach
2594a9f750fSGreg Roach    /**
2604a9f750fSGreg Roach     * A list of image attributes
2614a9f750fSGreg Roach     *
2624a9f750fSGreg Roach     * @return string[]
2634a9f750fSGreg Roach     */
264c1010edaSGreg Roach    public function attributes(): array
265c1010edaSGreg Roach    {
2664a9f750fSGreg Roach        $attributes = [];
26764b90bf1SGreg Roach
2684a9f750fSGreg Roach        if (!$this->isExternal() || $this->fileExists()) {
2694a9f750fSGreg Roach            $file = $this->folder() . $this->multimedia_file_refn;
2704a9f750fSGreg Roach
2714a9f750fSGreg Roach            $attributes['__FILE_SIZE__'] = $this->fileSizeKB();
2724a9f750fSGreg Roach
2734a9f750fSGreg Roach            $imgsize = getimagesize($file);
2744a9f750fSGreg Roach            if (is_array($imgsize) && !empty($imgsize['0'])) {
2754a9f750fSGreg Roach                $attributes['__IMAGE_SIZE__'] = I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1']));
2764a9f750fSGreg Roach            }
2774a9f750fSGreg Roach        }
2784a9f750fSGreg Roach
2794a9f750fSGreg Roach        return $attributes;
2804a9f750fSGreg Roach    }
2814a9f750fSGreg Roach
2824a9f750fSGreg Roach    /**
2834a9f750fSGreg Roach     * check if the file exists on this server
2844a9f750fSGreg Roach     *
2854a9f750fSGreg Roach     * @return bool
2864a9f750fSGreg Roach     */
2878f53f488SRico Sonntag    public function fileExists(): bool
288c1010edaSGreg Roach    {
289d6641c58SGreg Roach        return !$this->isExternal() && file_exists($this->folder() . $this->multimedia_file_refn);
2904a9f750fSGreg Roach    }
2914a9f750fSGreg Roach
2924a9f750fSGreg Roach    /**
2934a9f750fSGreg Roach     * Is the media file actually a URL?
2944a9f750fSGreg Roach     */
295c1010edaSGreg Roach    public function isExternal(): bool
296c1010edaSGreg Roach    {
2974a9f750fSGreg Roach        return strpos($this->multimedia_file_refn, '://') !== false;
2984a9f750fSGreg Roach    }
2994a9f750fSGreg Roach
3004a9f750fSGreg Roach    /**
3014a9f750fSGreg Roach     * Is the media file an image?
3024a9f750fSGreg Roach     */
303c1010edaSGreg Roach    public function isImage(): bool
304c1010edaSGreg Roach    {
305c1010edaSGreg Roach        return in_array($this->extension(), [
306c1010edaSGreg Roach            'jpeg',
307c1010edaSGreg Roach            'jpg',
308c1010edaSGreg Roach            'gif',
309c1010edaSGreg Roach            'png',
310c1010edaSGreg Roach        ]);
3114a9f750fSGreg Roach    }
3124a9f750fSGreg Roach
3134a9f750fSGreg Roach    /**
31444379edcSGreg Roach     * Where is the file stored on disk?
31544379edcSGreg Roach     */
316c1010edaSGreg Roach    public function folder(): string
317c1010edaSGreg Roach    {
31844379edcSGreg Roach        return WT_DATA_DIR . $this->media->getTree()->getPreference('MEDIA_DIRECTORY');
31944379edcSGreg Roach    }
32044379edcSGreg Roach
32144379edcSGreg Roach    /**
3224a9f750fSGreg Roach     * A user-friendly view of the file size
3234a9f750fSGreg Roach     *
3244a9f750fSGreg Roach     * @return int
3254a9f750fSGreg Roach     */
326c1010edaSGreg Roach    private function fileSizeBytes(): int
327c1010edaSGreg Roach    {
3284a9f750fSGreg Roach        try {
3294a9f750fSGreg Roach            return filesize($this->folder() . $this->multimedia_file_refn);
3302361dfe8SGreg Roach        } catch (Throwable $ex) {
3314a9f750fSGreg Roach            DebugBar::addThrowable($ex);
3324a9f750fSGreg Roach
3334a9f750fSGreg Roach            return 0;
3344a9f750fSGreg Roach        }
3354a9f750fSGreg Roach    }
3364a9f750fSGreg Roach
3374a9f750fSGreg Roach    /**
3384a9f750fSGreg Roach     * get the media file size in KB
3394a9f750fSGreg Roach     *
3404a9f750fSGreg Roach     * @return string
3414a9f750fSGreg Roach     */
3428f53f488SRico Sonntag    public function fileSizeKB(): string
343c1010edaSGreg Roach    {
344b0b72ea4SGreg Roach        $size = $this->fileSizeBytes();
345*cdaafeeeSGreg Roach        $size = intdiv($size + 1023, 1024);
3464a9f750fSGreg Roach
347bbb76c12SGreg Roach        /* I18N: size of file in KB */
348bbb76c12SGreg Roach        return I18N::translate('%s KB', I18N::number($size));
3494a9f750fSGreg Roach    }
35064b90bf1SGreg Roach
35164b90bf1SGreg Roach    /**
3528f5f5da8SGreg Roach     * Get the filename on the server - for those (very few!) functions which actually
353bf7c4bf8SGreg Roach     * need the filename, such as the PDF reports.
3548f5f5da8SGreg Roach     *
3558f5f5da8SGreg Roach     * @return string
3568f5f5da8SGreg Roach     */
357c1010edaSGreg Roach    public function getServerFilename()
358c1010edaSGreg Roach    {
3598f5f5da8SGreg Roach        $MEDIA_DIRECTORY = $this->media->getTree()->getPreference('MEDIA_DIRECTORY');
3608f5f5da8SGreg Roach
36164b90bf1SGreg Roach        if ($this->isExternal() || !$this->multimedia_file_refn) {
3628f5f5da8SGreg Roach            // External image, or (in the case of corrupt GEDCOM data) no image at all
36364b90bf1SGreg Roach            return $this->multimedia_file_refn;
364b2ce94c6SRico Sonntag        }
365b2ce94c6SRico Sonntag
3668f5f5da8SGreg Roach        // Main image
36764b90bf1SGreg Roach        return WT_DATA_DIR . $MEDIA_DIRECTORY . $this->multimedia_file_refn;
3688f5f5da8SGreg Roach    }
3698f5f5da8SGreg Roach
3708f5f5da8SGreg Roach    /**
371e1d1700bSGreg Roach     * Generate a URL to download a non-image media file.
372e1d1700bSGreg Roach     *
373e1d1700bSGreg Roach     * @return string
374e1d1700bSGreg Roach     */
3758f53f488SRico Sonntag    public function downloadUrl(): string
376c1010edaSGreg Roach    {
377e1d1700bSGreg Roach        return route('media-download', [
378e1d1700bSGreg Roach            'xref'    => $this->media->getXref(),
379e1d1700bSGreg Roach            'ged'     => $this->media->getTree()->getName(),
380e1d1700bSGreg Roach            'fact_id' => $this->fact_id,
381e1d1700bSGreg Roach        ]);
382e1d1700bSGreg Roach    }
383e1d1700bSGreg Roach
384e1d1700bSGreg Roach    /**
3858f5f5da8SGreg Roach     * Generate a URL for an image.
3868f5f5da8SGreg Roach     *
3878f5f5da8SGreg Roach     * @param int    $width  Maximum width in pixels
3888f5f5da8SGreg Roach     * @param int    $height Maximum height in pixels
3898f5f5da8SGreg Roach     * @param string $fit    "crop" or "contain"
3908f5f5da8SGreg Roach     *
3918f5f5da8SGreg Roach     * @return string
3928f5f5da8SGreg Roach     */
3938f53f488SRico Sonntag    public function imageUrl($width, $height, $fit): string
394c1010edaSGreg Roach    {
3958f5f5da8SGreg Roach        // Sign the URL, to protect against mass-resize attacks.
3968f5f5da8SGreg Roach        $glide_key = Site::getPreference('glide-key');
3978f5f5da8SGreg Roach        if (empty($glide_key)) {
3988f5f5da8SGreg Roach            $glide_key = bin2hex(random_bytes(128));
3998f5f5da8SGreg Roach            Site::setPreference('glide-key', $glide_key);
4008f5f5da8SGreg Roach        }
4018f5f5da8SGreg Roach
40264b90bf1SGreg Roach        if (Auth::accessLevel($this->media->getTree()) > $this->media->getTree()->getPreference('SHOW_NO_WATERMARK')) {
4038f5f5da8SGreg Roach            $mark = 'watermark.png';
4048f5f5da8SGreg Roach        } else {
4058f5f5da8SGreg Roach            $mark = '';
4068f5f5da8SGreg Roach        }
4078f5f5da8SGreg Roach
4084a9f750fSGreg Roach        $url_builder = UrlBuilderFactory::create(WT_BASE_URL, $glide_key);
4094a9f750fSGreg Roach
4104a9f750fSGreg Roach        $url = $url_builder->getUrl('index.php', [
4114a9f750fSGreg Roach            'route'     => 'media-thumbnail',
4124a9f750fSGreg Roach            'xref'      => $this->media->getXref(),
41364b90bf1SGreg Roach            'ged'       => $this->media->getTree()->getName(),
4144a9f750fSGreg Roach            'fact_id'   => $this->fact_id,
4158f5f5da8SGreg Roach            'w'         => $width,
4168f5f5da8SGreg Roach            'h'         => $height,
4178f5f5da8SGreg Roach            'fit'       => $fit,
4188f5f5da8SGreg Roach            'mark'      => $mark,
4198f5f5da8SGreg Roach            'markh'     => '100h',
4208f5f5da8SGreg Roach            'markw'     => '100w',
4218f5f5da8SGreg Roach            'markalpha' => 25,
422c1010edaSGreg Roach            'or'        => 0,
423c1010edaSGreg Roach            // Intervention uses exif_read_data() which is very buggy.
4248f5f5da8SGreg Roach        ]);
4258f5f5da8SGreg Roach
4268f5f5da8SGreg Roach        return $url;
4278f5f5da8SGreg Roach    }
4288f5f5da8SGreg Roach
4298f5f5da8SGreg Roach    /**
4308f5f5da8SGreg Roach     * What file extension is used by this file?
4318f5f5da8SGreg Roach     *
4328f5f5da8SGreg Roach     * @return string
4338f5f5da8SGreg Roach     */
434c1010edaSGreg Roach    public function extension()
435c1010edaSGreg Roach    {
436a99c6938SGreg Roach        if (preg_match('/\.([a-zA-Z0-9]+)$/', $this->multimedia_file_refn, $match)) {
4378f5f5da8SGreg Roach            return strtolower($match[1]);
4388f5f5da8SGreg Roach        }
439b2ce94c6SRico Sonntag
440b2ce94c6SRico Sonntag        return '';
4418f5f5da8SGreg Roach    }
4428f5f5da8SGreg Roach
4438f5f5da8SGreg Roach    /**
4448f5f5da8SGreg Roach     * What is the mime-type of this object?
4458f5f5da8SGreg Roach     * For simplicity and efficiency, use the extension, rather than the contents.
4468f5f5da8SGreg Roach     *
4478f5f5da8SGreg Roach     * @return string
4488f5f5da8SGreg Roach     */
4498f53f488SRico Sonntag    public function mimeType(): string
450c1010edaSGreg Roach    {
4515225fdfcSGreg Roach        return self::MIME_TYPES[$this->extension()] ?? 'application/octet-stream';
4528f5f5da8SGreg Roach    }
4538f5f5da8SGreg Roach}
454