xref: /webtrees/app/MediaFile.php (revision f4afa648fcb1eff2558e414d35a921dbfbf02078)
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 */
16e7f56f2aSGreg Roachdeclare(strict_types=1);
17e7f56f2aSGreg Roach
188f5f5da8SGreg Roachnamespace Fisharebest\Webtrees;
198f5f5da8SGreg Roach
208f5f5da8SGreg Roachuse League\Glide\Urls\UrlBuilderFactory;
212361dfe8SGreg Roachuse Throwable;
228f5f5da8SGreg Roach
238f5f5da8SGreg Roach/**
248f5f5da8SGreg Roach * A GEDCOM media file.  A media object can contain many media files,
258f5f5da8SGreg Roach * such as scans of both sides of a document, the transcript of an audio
268f5f5da8SGreg Roach * recording, etc.
278f5f5da8SGreg Roach */
28c1010edaSGreg Roachclass MediaFile
29c1010edaSGreg Roach{
305225fdfcSGreg Roach    const MIME_TYPES = [
315225fdfcSGreg Roach        'bmp'  => 'image/bmp',
325225fdfcSGreg Roach        'doc'  => 'application/msword',
335225fdfcSGreg Roach        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
345225fdfcSGreg Roach        'ged'  => 'text/x-gedcom',
355225fdfcSGreg Roach        'gif'  => 'image/gif',
365225fdfcSGreg Roach        'html' => 'text/html',
375225fdfcSGreg Roach        'htm'  => 'text/html',
385225fdfcSGreg Roach        'jpeg' => 'image/jpeg',
395225fdfcSGreg Roach        'jpg'  => 'image/jpeg',
405225fdfcSGreg Roach        'mov'  => 'video/quicktime',
415225fdfcSGreg Roach        'mp3'  => 'audio/mpeg',
425225fdfcSGreg Roach        'mp4'  => 'video/mp4',
435225fdfcSGreg Roach        'ogv'  => 'video/ogg',
445225fdfcSGreg Roach        'pdf'  => 'application/pdf',
455225fdfcSGreg Roach        'png'  => 'image/png',
465225fdfcSGreg Roach        'rar'  => 'application/x-rar-compressed',
475225fdfcSGreg Roach        'swf'  => 'application/x-shockwave-flash',
485225fdfcSGreg Roach        'svg'  => 'image/svg',
495225fdfcSGreg Roach        'tiff' => 'image/tiff',
505225fdfcSGreg Roach        'tif'  => 'image/tiff',
515225fdfcSGreg Roach        'xls'  => 'application/vnd-ms-excel',
525225fdfcSGreg Roach        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
535225fdfcSGreg Roach        'wmv'  => 'video/x-ms-wmv',
545225fdfcSGreg Roach        'zip'  => 'application/zip',
555225fdfcSGreg Roach    ];
565225fdfcSGreg Roach
578f5f5da8SGreg Roach    /** @var string The filename */
588f5f5da8SGreg Roach    private $multimedia_file_refn = '';
598f5f5da8SGreg Roach
608f5f5da8SGreg Roach    /** @var string The file extension; jpeg, txt, mp4, etc. */
618f5f5da8SGreg Roach    private $multimedia_format = '';
628f5f5da8SGreg Roach
638f5f5da8SGreg Roach    /** @var string The type of document; newspaper, microfiche, etc. */
648f5f5da8SGreg Roach    private $source_media_type = '';
658f5f5da8SGreg Roach    /** @var string The filename */
668f5f5da8SGreg Roach
678f5f5da8SGreg Roach    /** @var string The name of the document */
688f5f5da8SGreg Roach    private $descriptive_title = '';
698f5f5da8SGreg Roach
708f5f5da8SGreg Roach    /** @var Media $media The media object to which this file belongs */
718f5f5da8SGreg Roach    private $media;
728f5f5da8SGreg Roach
7364b90bf1SGreg Roach    /** @var string */
7464b90bf1SGreg Roach    private $fact_id;
7564b90bf1SGreg Roach
768f5f5da8SGreg Roach    /**
778f5f5da8SGreg Roach     * Create a MediaFile from raw GEDCOM data.
788f5f5da8SGreg Roach     *
798f5f5da8SGreg Roach     * @param string $gedcom
808f5f5da8SGreg Roach     * @param Media  $media
818f5f5da8SGreg Roach     */
82c1010edaSGreg Roach    public function __construct($gedcom, Media $media)
83c1010edaSGreg Roach    {
848f5f5da8SGreg Roach        $this->media   = $media;
8564b90bf1SGreg Roach        $this->fact_id = md5($gedcom);
868f5f5da8SGreg Roach
878f5f5da8SGreg Roach        if (preg_match('/^\d FILE (.+)/m', $gedcom, $match)) {
888f5f5da8SGreg Roach            $this->multimedia_file_refn = $match[1];
89423c6ccdSGreg Roach            $this->multimedia_format    = pathinfo($match[1], PATHINFO_EXTENSION);
908f5f5da8SGreg Roach        }
918f5f5da8SGreg Roach
928f5f5da8SGreg Roach        if (preg_match('/^\d FORM (.+)/m', $gedcom, $match)) {
938f5f5da8SGreg Roach            $this->multimedia_format = $match[1];
948f5f5da8SGreg Roach        }
958f5f5da8SGreg Roach
968f5f5da8SGreg Roach        if (preg_match('/^\d TYPE (.+)/m', $gedcom, $match)) {
978f5f5da8SGreg Roach            $this->source_media_type = $match[1];
988f5f5da8SGreg Roach        }
998f5f5da8SGreg Roach
1008f5f5da8SGreg Roach        if (preg_match('/^\d TITL (.+)/m', $gedcom, $match)) {
1018f5f5da8SGreg Roach            $this->descriptive_title = $match[1];
1028f5f5da8SGreg Roach        }
1038f5f5da8SGreg Roach    }
1048f5f5da8SGreg Roach
1058f5f5da8SGreg Roach    /**
10664b90bf1SGreg Roach     * Get the filename.
1078f5f5da8SGreg Roach     *
1088f5f5da8SGreg Roach     * @return string
1098f5f5da8SGreg Roach     */
110c1010edaSGreg Roach    public function filename(): string
111c1010edaSGreg Roach    {
1126e6a9947SGreg Roach        return $this->multimedia_file_refn;
1138f5f5da8SGreg Roach    }
1148f5f5da8SGreg Roach
1158f5f5da8SGreg Roach    /**
116d6641c58SGreg Roach     * Get the base part of the filename.
117d6641c58SGreg Roach     *
118d6641c58SGreg Roach     * @return string
119d6641c58SGreg Roach     */
120c1010edaSGreg Roach    public function basename(): string
121c1010edaSGreg Roach    {
122d6641c58SGreg Roach        return basename($this->multimedia_file_refn);
123d6641c58SGreg Roach    }
124d6641c58SGreg Roach
125d6641c58SGreg Roach    /**
126d6641c58SGreg Roach     * Get the folder part of the filename.
127d6641c58SGreg Roach     *
128d6641c58SGreg Roach     * @return string
129d6641c58SGreg Roach     */
130c1010edaSGreg Roach    public function dirname(): string
131c1010edaSGreg Roach    {
132d6641c58SGreg Roach        $dirname = dirname($this->multimedia_file_refn);
133d6641c58SGreg Roach
134d6641c58SGreg Roach        if ($dirname === '.') {
135d6641c58SGreg Roach            return '';
136d6641c58SGreg Roach        }
137b2ce94c6SRico Sonntag
138b2ce94c6SRico Sonntag        return $dirname;
139d6641c58SGreg Roach    }
140d6641c58SGreg Roach
141d6641c58SGreg Roach    /**
14264b90bf1SGreg Roach     * Get the format.
1438f5f5da8SGreg Roach     *
1448f5f5da8SGreg Roach     * @return string
1458f5f5da8SGreg Roach     */
146c1010edaSGreg Roach    public function format(): string
147c1010edaSGreg Roach    {
1488f5f5da8SGreg Roach        return $this->multimedia_format;
1498f5f5da8SGreg Roach    }
1508f5f5da8SGreg Roach
1518f5f5da8SGreg Roach    /**
15264b90bf1SGreg Roach     * Get the type.
1538f5f5da8SGreg Roach     *
1548f5f5da8SGreg Roach     * @return string
1558f5f5da8SGreg Roach     */
156c1010edaSGreg Roach    public function type(): string
157c1010edaSGreg Roach    {
1588f5f5da8SGreg Roach        return $this->source_media_type;
1598f5f5da8SGreg Roach    }
1608f5f5da8SGreg Roach
1618f5f5da8SGreg Roach    /**
16264b90bf1SGreg Roach     * Get the title.
1638f5f5da8SGreg Roach     *
1648f5f5da8SGreg Roach     * @return string
1658f5f5da8SGreg Roach     */
166c1010edaSGreg Roach    public function title(): string
167c1010edaSGreg Roach    {
1688f5f5da8SGreg Roach        return $this->descriptive_title;
1698f5f5da8SGreg Roach    }
1708f5f5da8SGreg Roach
1718f5f5da8SGreg Roach    /**
17264b90bf1SGreg Roach     * Get the fact ID.
17364b90bf1SGreg Roach     *
17464b90bf1SGreg Roach     * @return string
17564b90bf1SGreg Roach     */
176c1010edaSGreg Roach    public function factId(): string
177c1010edaSGreg Roach    {
17864b90bf1SGreg Roach        return $this->fact_id;
17964b90bf1SGreg Roach    }
18064b90bf1SGreg Roach
18164b90bf1SGreg Roach    /**
182d6641c58SGreg Roach     * @return bool
183d6641c58SGreg Roach     */
1848f53f488SRico Sonntag    public function isPendingAddition(): bool
185c1010edaSGreg Roach    {
186d6641c58SGreg Roach        foreach ($this->media->getFacts() as $fact) {
187905ab80aSGreg Roach            if ($fact->id() === $this->fact_id) {
188d6641c58SGreg Roach                return $fact->isPendingAddition();
189d6641c58SGreg Roach            }
190d6641c58SGreg Roach        }
191d6641c58SGreg Roach
192d6641c58SGreg Roach        return false;
193d6641c58SGreg Roach    }
194d6641c58SGreg Roach
195d6641c58SGreg Roach    /**
196d6641c58SGreg Roach     * @return bool
197d6641c58SGreg Roach     */
1988f53f488SRico Sonntag    public function isPendingDeletion(): bool
199c1010edaSGreg Roach    {
200d6641c58SGreg Roach        foreach ($this->media->getFacts() as $fact) {
201905ab80aSGreg Roach            if ($fact->id() === $this->fact_id) {
202d6641c58SGreg Roach                return $fact->isPendingDeletion();
203d6641c58SGreg Roach            }
204d6641c58SGreg Roach        }
205d6641c58SGreg Roach
206d6641c58SGreg Roach        return false;
207d6641c58SGreg Roach    }
208d6641c58SGreg Roach
209d6641c58SGreg Roach    /**
21064b90bf1SGreg Roach     * Display an image-thumbnail or a media-icon, and add markup for image viewers such as colorbox.
21164b90bf1SGreg Roach     *
21264b90bf1SGreg Roach     * @param int      $width      Pixels
21364b90bf1SGreg Roach     * @param int      $height     Pixels
21464b90bf1SGreg Roach     * @param string   $fit        "crop" or "contain"
21564b90bf1SGreg Roach     * @param string[] $attributes Additional HTML attributes
21664b90bf1SGreg Roach     *
21764b90bf1SGreg Roach     * @return string
21864b90bf1SGreg Roach     */
2198f53f488SRico Sonntag    public function displayImage($width, $height, $fit, $attributes = []): string
220c1010edaSGreg Roach    {
22164b90bf1SGreg Roach        if ($this->isExternal()) {
22264b90bf1SGreg Roach            $src    = $this->multimedia_file_refn;
22364b90bf1SGreg Roach            $srcset = [];
22464b90bf1SGreg Roach        } else {
22564b90bf1SGreg Roach            // Generate multiple images for displays with higher pixel densities.
22664b90bf1SGreg Roach            $src    = $this->imageUrl($width, $height, $fit);
22764b90bf1SGreg Roach            $srcset = [];
228c1010edaSGreg Roach            foreach ([
229c1010edaSGreg Roach                         2,
230c1010edaSGreg Roach                         3,
231c1010edaSGreg Roach                         4,
232c1010edaSGreg Roach                     ] as $x) {
23364b90bf1SGreg Roach                $srcset[] = $this->imageUrl($width * $x, $height * $x, $fit) . ' ' . $x . 'x';
23464b90bf1SGreg Roach            }
23564b90bf1SGreg Roach        }
23664b90bf1SGreg Roach
23764b90bf1SGreg Roach        $image = '<img ' . Html::attributes($attributes + [
23864b90bf1SGreg Roach                    'dir'    => 'auto',
23964b90bf1SGreg Roach                    'src'    => $src,
24064b90bf1SGreg Roach                    'srcset' => implode(',', $srcset),
2415be0d03dSGreg Roach                    'alt'    => htmlspecialchars_decode(strip_tags($this->media->getFullName())),
24264b90bf1SGreg Roach                ]) . '>';
24364b90bf1SGreg Roach
244e1d1700bSGreg Roach        if ($this->isImage()) {
24564b90bf1SGreg Roach            $attributes = Html::attributes([
24664b90bf1SGreg Roach                'class'      => 'gallery',
24764b90bf1SGreg Roach                'type'       => $this->mimeType(),
24860e3c46aSGreg Roach                'href'       => $this->imageUrl(0, 0, 'contain'),
2495be0d03dSGreg Roach                'data-title' => htmlspecialchars_decode(strip_tags($this->media->getFullName())),
25064b90bf1SGreg Roach            ]);
251e1d1700bSGreg Roach        } else {
252e1d1700bSGreg Roach            $attributes = Html::attributes([
253e1d1700bSGreg Roach                'type' => $this->mimeType(),
254e1d1700bSGreg Roach                'href' => $this->downloadUrl(),
255e1d1700bSGreg Roach            ]);
256e1d1700bSGreg Roach        }
25764b90bf1SGreg Roach
25864b90bf1SGreg Roach        return '<a ' . $attributes . '>' . $image . '</a>';
25964b90bf1SGreg Roach    }
26064b90bf1SGreg Roach
2614a9f750fSGreg Roach    /**
2624a9f750fSGreg Roach     * A list of image attributes
2634a9f750fSGreg Roach     *
2644a9f750fSGreg Roach     * @return string[]
2654a9f750fSGreg Roach     */
266c1010edaSGreg Roach    public function attributes(): array
267c1010edaSGreg Roach    {
2684a9f750fSGreg Roach        $attributes = [];
26964b90bf1SGreg Roach
2704a9f750fSGreg Roach        if (!$this->isExternal() || $this->fileExists()) {
2714a9f750fSGreg Roach            $file = $this->folder() . $this->multimedia_file_refn;
2724a9f750fSGreg Roach
2734a9f750fSGreg Roach            $attributes['__FILE_SIZE__'] = $this->fileSizeKB();
2744a9f750fSGreg Roach
2754a9f750fSGreg Roach            $imgsize = getimagesize($file);
2764a9f750fSGreg Roach            if (is_array($imgsize) && !empty($imgsize['0'])) {
2774a9f750fSGreg Roach                $attributes['__IMAGE_SIZE__'] = I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1']));
2784a9f750fSGreg Roach            }
2794a9f750fSGreg Roach        }
2804a9f750fSGreg Roach
2814a9f750fSGreg Roach        return $attributes;
2824a9f750fSGreg Roach    }
2834a9f750fSGreg Roach
2844a9f750fSGreg Roach    /**
2854a9f750fSGreg Roach     * check if the file exists on this server
2864a9f750fSGreg Roach     *
2874a9f750fSGreg Roach     * @return bool
2884a9f750fSGreg Roach     */
2898f53f488SRico Sonntag    public function fileExists(): bool
290c1010edaSGreg Roach    {
291d6641c58SGreg Roach        return !$this->isExternal() && file_exists($this->folder() . $this->multimedia_file_refn);
2924a9f750fSGreg Roach    }
2934a9f750fSGreg Roach
2944a9f750fSGreg Roach    /**
2954a9f750fSGreg Roach     * Is the media file actually a URL?
2964a9f750fSGreg Roach     */
297c1010edaSGreg Roach    public function isExternal(): bool
298c1010edaSGreg Roach    {
2994a9f750fSGreg Roach        return strpos($this->multimedia_file_refn, '://') !== false;
3004a9f750fSGreg Roach    }
3014a9f750fSGreg Roach
3024a9f750fSGreg Roach    /**
3034a9f750fSGreg Roach     * Is the media file an image?
3044a9f750fSGreg Roach     */
305c1010edaSGreg Roach    public function isImage(): bool
306c1010edaSGreg Roach    {
307c1010edaSGreg Roach        return in_array($this->extension(), [
308c1010edaSGreg Roach            'jpeg',
309c1010edaSGreg Roach            'jpg',
310c1010edaSGreg Roach            'gif',
311c1010edaSGreg Roach            'png',
312c1010edaSGreg Roach        ]);
3134a9f750fSGreg Roach    }
3144a9f750fSGreg Roach
3154a9f750fSGreg Roach    /**
31644379edcSGreg Roach     * Where is the file stored on disk?
31744379edcSGreg Roach     */
318c1010edaSGreg Roach    public function folder(): string
319c1010edaSGreg Roach    {
320*f4afa648SGreg Roach        return WT_DATA_DIR . $this->media->tree()->getPreference('MEDIA_DIRECTORY');
32144379edcSGreg Roach    }
32244379edcSGreg Roach
32344379edcSGreg Roach    /**
3244a9f750fSGreg Roach     * A user-friendly view of the file size
3254a9f750fSGreg Roach     *
3264a9f750fSGreg Roach     * @return int
3274a9f750fSGreg Roach     */
328c1010edaSGreg Roach    private function fileSizeBytes(): int
329c1010edaSGreg Roach    {
3304a9f750fSGreg Roach        try {
3314a9f750fSGreg Roach            return filesize($this->folder() . $this->multimedia_file_refn);
3322361dfe8SGreg Roach        } catch (Throwable $ex) {
3334a9f750fSGreg Roach            DebugBar::addThrowable($ex);
3344a9f750fSGreg Roach
3354a9f750fSGreg Roach            return 0;
3364a9f750fSGreg Roach        }
3374a9f750fSGreg Roach    }
3384a9f750fSGreg Roach
3394a9f750fSGreg Roach    /**
3404a9f750fSGreg Roach     * get the media file size in KB
3414a9f750fSGreg Roach     *
3424a9f750fSGreg Roach     * @return string
3434a9f750fSGreg Roach     */
3448f53f488SRico Sonntag    public function fileSizeKB(): string
345c1010edaSGreg Roach    {
346b0b72ea4SGreg Roach        $size = $this->fileSizeBytes();
347cdaafeeeSGreg Roach        $size = intdiv($size + 1023, 1024);
3484a9f750fSGreg Roach
349bbb76c12SGreg Roach        /* I18N: size of file in KB */
350bbb76c12SGreg Roach        return I18N::translate('%s KB', I18N::number($size));
3514a9f750fSGreg Roach    }
35264b90bf1SGreg Roach
35364b90bf1SGreg Roach    /**
3548f5f5da8SGreg Roach     * Get the filename on the server - for those (very few!) functions which actually
355bf7c4bf8SGreg Roach     * need the filename, such as the PDF reports.
3568f5f5da8SGreg Roach     *
3578f5f5da8SGreg Roach     * @return string
3588f5f5da8SGreg Roach     */
359c1010edaSGreg Roach    public function getServerFilename()
360c1010edaSGreg Roach    {
361*f4afa648SGreg Roach        $MEDIA_DIRECTORY = $this->media->tree()->getPreference('MEDIA_DIRECTORY');
3628f5f5da8SGreg Roach
36364b90bf1SGreg Roach        if ($this->isExternal() || !$this->multimedia_file_refn) {
3648f5f5da8SGreg Roach            // External image, or (in the case of corrupt GEDCOM data) no image at all
36564b90bf1SGreg Roach            return $this->multimedia_file_refn;
366b2ce94c6SRico Sonntag        }
367b2ce94c6SRico Sonntag
3688f5f5da8SGreg Roach        // Main image
36964b90bf1SGreg Roach        return WT_DATA_DIR . $MEDIA_DIRECTORY . $this->multimedia_file_refn;
3708f5f5da8SGreg Roach    }
3718f5f5da8SGreg Roach
3728f5f5da8SGreg Roach    /**
373e1d1700bSGreg Roach     * Generate a URL to download a non-image media file.
374e1d1700bSGreg Roach     *
375e1d1700bSGreg Roach     * @return string
376e1d1700bSGreg Roach     */
3778f53f488SRico Sonntag    public function downloadUrl(): string
378c1010edaSGreg Roach    {
379e1d1700bSGreg Roach        return route('media-download', [
380c0935879SGreg Roach            'xref'    => $this->media->xref(),
381*f4afa648SGreg Roach            'ged'     => $this->media->tree()->name(),
382e1d1700bSGreg Roach            'fact_id' => $this->fact_id,
383e1d1700bSGreg Roach        ]);
384e1d1700bSGreg Roach    }
385e1d1700bSGreg Roach
386e1d1700bSGreg Roach    /**
3878f5f5da8SGreg Roach     * Generate a URL for an image.
3888f5f5da8SGreg Roach     *
3898f5f5da8SGreg Roach     * @param int    $width  Maximum width in pixels
3908f5f5da8SGreg Roach     * @param int    $height Maximum height in pixels
3918f5f5da8SGreg Roach     * @param string $fit    "crop" or "contain"
3928f5f5da8SGreg Roach     *
3938f5f5da8SGreg Roach     * @return string
3948f5f5da8SGreg Roach     */
3958f53f488SRico Sonntag    public function imageUrl($width, $height, $fit): string
396c1010edaSGreg Roach    {
3978f5f5da8SGreg Roach        // Sign the URL, to protect against mass-resize attacks.
3988f5f5da8SGreg Roach        $glide_key = Site::getPreference('glide-key');
3998f5f5da8SGreg Roach        if (empty($glide_key)) {
4008f5f5da8SGreg Roach            $glide_key = bin2hex(random_bytes(128));
4018f5f5da8SGreg Roach            Site::setPreference('glide-key', $glide_key);
4028f5f5da8SGreg Roach        }
4038f5f5da8SGreg Roach
404*f4afa648SGreg Roach        if (Auth::accessLevel($this->media->tree()) > $this->media->tree()->getPreference('SHOW_NO_WATERMARK')) {
4058f5f5da8SGreg Roach            $mark = 'watermark.png';
4068f5f5da8SGreg Roach        } else {
4078f5f5da8SGreg Roach            $mark = '';
4088f5f5da8SGreg Roach        }
4098f5f5da8SGreg Roach
4104a9f750fSGreg Roach        $url_builder = UrlBuilderFactory::create(WT_BASE_URL, $glide_key);
4114a9f750fSGreg Roach
4124a9f750fSGreg Roach        $url = $url_builder->getUrl('index.php', [
4134a9f750fSGreg Roach            'route'     => 'media-thumbnail',
414c0935879SGreg Roach            'xref'      => $this->media->xref(),
415*f4afa648SGreg Roach            'ged'       => $this->media->tree()->name(),
4164a9f750fSGreg Roach            'fact_id'   => $this->fact_id,
4178f5f5da8SGreg Roach            'w'         => $width,
4188f5f5da8SGreg Roach            'h'         => $height,
4198f5f5da8SGreg Roach            'fit'       => $fit,
4208f5f5da8SGreg Roach            'mark'      => $mark,
4218f5f5da8SGreg Roach            'markh'     => '100h',
4228f5f5da8SGreg Roach            'markw'     => '100w',
4238f5f5da8SGreg Roach            'markalpha' => 25,
424c1010edaSGreg Roach            'or'        => 0,
425c1010edaSGreg Roach            // Intervention uses exif_read_data() which is very buggy.
4268f5f5da8SGreg Roach        ]);
4278f5f5da8SGreg Roach
4288f5f5da8SGreg Roach        return $url;
4298f5f5da8SGreg Roach    }
4308f5f5da8SGreg Roach
4318f5f5da8SGreg Roach    /**
4328f5f5da8SGreg Roach     * What file extension is used by this file?
4338f5f5da8SGreg Roach     *
4348f5f5da8SGreg Roach     * @return string
4358f5f5da8SGreg Roach     */
436c1010edaSGreg Roach    public function extension()
437c1010edaSGreg Roach    {
438a99c6938SGreg Roach        if (preg_match('/\.([a-zA-Z0-9]+)$/', $this->multimedia_file_refn, $match)) {
4398f5f5da8SGreg Roach            return strtolower($match[1]);
4408f5f5da8SGreg Roach        }
441b2ce94c6SRico Sonntag
442b2ce94c6SRico Sonntag        return '';
4438f5f5da8SGreg Roach    }
4448f5f5da8SGreg Roach
4458f5f5da8SGreg Roach    /**
4468f5f5da8SGreg Roach     * What is the mime-type of this object?
4478f5f5da8SGreg Roach     * For simplicity and efficiency, use the extension, rather than the contents.
4488f5f5da8SGreg Roach     *
4498f5f5da8SGreg Roach     * @return string
4508f5f5da8SGreg Roach     */
4518f53f488SRico Sonntag    public function mimeType(): string
452c1010edaSGreg Roach    {
4535225fdfcSGreg Roach        return self::MIME_TYPES[$this->extension()] ?? 'application/octet-stream';
4548f5f5da8SGreg Roach    }
4558f5f5da8SGreg Roach}
456