xref: /plugin/mediathumbnails/thumb_engines.php (revision e19533e13ade8665caee6886acf0de2dbdc8883d)
147d2d32aSternite<?php
247d2d32aSternite/**
347d2d32aSternite * DokuWiki Plugin mediathumbnails (thumb_engine class and subclasses)
447d2d32aSternite *
547d2d32aSternite * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
647d2d32aSternite * @author  Thomas Schäfer <thomas.schaefer@itschert.net>
747d2d32aSternite */
847d2d32aSternite
947d2d32aSterniteabstract class thumb_engine {
1047d2d32aSternite
11*e19533e1Sternite	protected ?thumbnail $thumbnail = null;
12*e19533e1Sternite	protected bool $state_failed = false;
1347d2d32aSternite
1447d2d32aSternite	public function __construct(thumbnail $thumbnail) {
1547d2d32aSternite		$this->thumbnail = $thumbnail;
1647d2d32aSternite	}
1747d2d32aSternite
1847d2d32aSternite	protected function getSourceFilepath() {
1947d2d32aSternite		return $this->thumbnail->getSourceFilepath();
2047d2d32aSternite	}
2147d2d32aSternite
2247d2d32aSternite	protected function getTargetFilepath() {
2347d2d32aSternite		return $this->thumbnail->getFilepath();
2447d2d32aSternite	}
2547d2d32aSternite
2647d2d32aSternite	protected function getTargetMaxDimension() {
2747d2d32aSternite		return $this->thumbnail->getMaxDimension();
2847d2d32aSternite	}
2947d2d32aSternite
30*e19533e1Sternite	public function act(): bool {
3147d2d32aSternite		if ($this->act_internal()) {
3247d2d32aSternite			// Set timestamp to the source file's timestamp (this is used to check in later passes if the file already exists in the correct version).
33*e19533e1Sternite			$sourceFilePath = $this->getSourceFilepath();
34*e19533e1Sternite			$targetFilePath = $this->getTargetFilepath();
35*e19533e1Sternite			if (filemtime($sourceFilePath) !== filemtime($targetFilePath)) {
36*e19533e1Sternite				touch($sourceFilePath, filemtime($targetFilePath));
3747d2d32aSternite			}
3847d2d32aSternite			return true;
3947d2d32aSternite		}
4047d2d32aSternite		return false;
4147d2d32aSternite	}
4247d2d32aSternite
4347d2d32aSternite	// Checks if a thumbnail file for the current file version has already been created
44*e19533e1Sternite	protected function thumb_needs_update(): bool {
4547d2d32aSternite		return !file_exists($this->getTargetFilepath()) || filemtime($this->getTargetFilepath()) !== filemtime($this->getSourceFilepath());
4647d2d32aSternite	}
4747d2d32aSternite
48*e19533e1Sternite	public function has_failed(): bool {
49*e19533e1Sternite		return $this->state_failed;
50*e19533e1Sternite	}
5147d2d32aSternite
52*e19533e1Sternite	public abstract function act_internal(): bool;
53*e19533e1Sternite
54*e19533e1Sternite	public abstract function getFileSuffix(): string;
5547d2d32aSternite}
5647d2d32aSternite
5747d2d32aSterniteclass thumb_pdf_engine extends thumb_engine {
5847d2d32aSternite
59*e19533e1Sternite	public function getFileSuffix(): string {
6047d2d32aSternite		return "jpg";
6147d2d32aSternite	}
6247d2d32aSternite
63*e19533e1Sternite	public function act_internal(): bool {
6447d2d32aSternite		if ($this->thumb_needs_update()) {
65a99f1509Sternite			//if file does not exist
66a99f1509Sternite			if (!file_exists($this->getSourceFilepath())) {
67a99f1509Sternite				return false;
68a99f1509Sternite			}
6947d2d32aSternite			$im = new imagick($this->getSourceFilepath()."[0]");
7033e1a005Sternite            // the following line was in the original code. Issue #6 (https://github.com/ternite/dokuwiki-plugin-mediathumbnails/issues/6)
7133e1a005Sternite            // indicated there might be problems with colors, so I uncommented the line (TS, 2025-10-11)
7233e1a005Sternite			//$im->setImageColorspace(255);
7347d2d32aSternite			$im->setResolution(300, 300);
7447d2d32aSternite			$im->setCompressionQuality(95);
7547d2d32aSternite			$im->setImageFormat('jpeg');
7647d2d32aSternite			//$im->resizeImage($this->getTargetMaxDimension(),0,imagick::FILTER_LANCZOS,0.9);
7747d2d32aSternite			//$im->thumbnailImage($this->getTargetMaxDimension(),$this->getTargetMaxDimension(),true,false);
7847d2d32aSternite			$im->writeImage($this->getTargetFilepath());
7947d2d32aSternite			$im->clear();
8047d2d32aSternite			$im->destroy();
8147d2d32aSternite
8247d2d32aSternite			// unfortunately, resizeImage or thumbnailImage leads to a black thumbnail in my setup, so I reopen the file and resize it now.
8347d2d32aSternite			$im = new imagick($this->getTargetFilepath());
8447d2d32aSternite			$im->thumbnailImage($this->getTargetMaxDimension(),$this->getTargetMaxDimension(),true,false);
8547d2d32aSternite			$im->writeImage($this->getTargetFilepath());
8647d2d32aSternite			$im->clear();
8747d2d32aSternite			$im->destroy();
8847d2d32aSternite
8947d2d32aSternite			return true;
9047d2d32aSternite		} else {
9147d2d32aSternite			return true;
9247d2d32aSternite		}
9347d2d32aSternite	}
9447d2d32aSternite}
9547d2d32aSternite
9647d2d32aSterniteclass thumb_img_engine extends thumb_engine {
9747d2d32aSternite
98*e19533e1Sternite	public function getFileSuffix(): string {
9947d2d32aSternite		return getFileSuffix($this->getSourceFilepath());
10047d2d32aSternite	}
10147d2d32aSternite
102*e19533e1Sternite	public function act_internal(): bool {
10347d2d32aSternite		if ($this->thumb_needs_update()) {
10447d2d32aSternite			$im = new imagick( $this->getSourceFilepath() );
10547d2d32aSternite			$im->thumbnailImage($this->getTargetMaxDimension(),$this->getTargetMaxDimension(),true,false);
10647d2d32aSternite			$im->writeImage($this->getTargetFilepath());
10747d2d32aSternite			$im->clear();
10847d2d32aSternite			$im->destroy();
10947d2d32aSternite
11047d2d32aSternite			return true;
11147d2d32aSternite		} else {
11247d2d32aSternite			return true;
11347d2d32aSternite		}
11447d2d32aSternite	}
11547d2d32aSternite}
11647d2d32aSternite
11747d2d32aSterniteclass thumb_zip_engine extends thumb_engine {
11847d2d32aSternite
11947d2d32aSternite	private array $thumb_paths;
12047d2d32aSternite	private $file_suffix = "";
12147d2d32aSternite
12247d2d32aSternite	public function __construct(thumbnail $thumbnail, array $thumb_paths) {
12347d2d32aSternite		parent::__construct($thumbnail);
12447d2d32aSternite		$this->thumb_paths = $thumb_paths;
12547d2d32aSternite	}
12647d2d32aSternite
127*e19533e1Sternite	public function getFileSuffix(): string {
12847d2d32aSternite		return $this->file_suffix;
12947d2d32aSternite	}
13047d2d32aSternite
131*e19533e1Sternite	public function act_internal(): bool {
13247d2d32aSternite
13347d2d32aSternite		$zip = new ZipArchive;
13447d2d32aSternite		if ($zip->open($this->getSourceFilepath()) !== true) {
13547d2d32aSternite			// file is no zip or cannot be opened
13647d2d32aSternite			return false;
13747d2d32aSternite		}
13847d2d32aSternite
13947d2d32aSternite		// The media file exists and acts as a zip file!
14047d2d32aSternite
141*e19533e1Sternite		// Check all possible paths (configured in configuration key 'thumb_paths') if there is a file available - if there are multiple files, the first one found is used.
142*e19533e1Sternite		$thumbnail_found = false;
14347d2d32aSternite		foreach($this->thumb_paths as $thumbnail_path) {
14447d2d32aSternite			$this->file_suffix = substr(strrchr($thumbnail_path,'.'),1);
14547d2d32aSternite
14647d2d32aSternite			if ($zip->locateName($thumbnail_path) !== false) {
14747d2d32aSternite
14847d2d32aSternite				if (!$this->thumb_needs_update()) {
14947d2d32aSternite					return true;
15047d2d32aSternite				}
15147d2d32aSternite
15247d2d32aSternite				// Get the thumbnail file!
15347d2d32aSternite				$fp = $zip->getStream($thumbnail_path);
15447d2d32aSternite				if(!$fp) {
15547d2d32aSternite					return false;
15647d2d32aSternite				}
15747d2d32aSternite
15847d2d32aSternite				$thumbnaildata = '';
15947d2d32aSternite				while (!feof($fp)) {
16047d2d32aSternite					$thumbnaildata .= fread($fp, 8192);
16147d2d32aSternite				}
16247d2d32aSternite
16347d2d32aSternite				fclose($fp);
16447d2d32aSternite
16547d2d32aSternite				// Write thumbnail file to media folder
16647d2d32aSternite				file_put_contents($this->getTargetFilepath(), $thumbnaildata);
16747d2d32aSternite
168*e19533e1Sternite				$thumbnail_found = true;
16947d2d32aSternite				return true;
17047d2d32aSternite			}
17147d2d32aSternite		}
17247d2d32aSternite
173*e19533e1Sternite		// if we reach this point, no thumbnail file was found within the zip file
174*e19533e1Sternite		if (!$thumbnail_found) {
175*e19533e1Sternite			$this->state_failed = true;
176*e19533e1Sternite			msg("plugin mediathumbnails: No thumbnail found inside zip file " . $this->getSourceFilepath());
177*e19533e1Sternite		}
178*e19533e1Sternite
179*e19533e1Sternite		return false;
18047d2d32aSternite	}
18147d2d32aSternite}