104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeau 404fd306cSNickeaunamespace ComboStrap; 504fd306cSNickeau 604fd306cSNickeauuse ComboStrap\Web\Url; 704fd306cSNickeau 804fd306cSNickeau/** 904fd306cSNickeau * 1004fd306cSNickeau * A Image Raster processing class that: 1104fd306cSNickeau * * takes as input: 1204fd306cSNickeau * * a {@link FetcherRaster::buildFromUrl() fetch URL}: 1304fd306cSNickeau * * from an HTTP request 1404fd306cSNickeau * * or {@link MediaMarkup::getFetchUrl() markup}) 1504fd306cSNickeau * * or data via setter 1604fd306cSNickeau * * outputs: 1704fd306cSNickeau * * a {@link FetcherRaster::getFetchPath() raster image file} for: 1804fd306cSNickeau * * an HTTP response 1904fd306cSNickeau * * or further local processing 2004fd306cSNickeau * * or a {@link FetcherRaster::getFetchUrl() fetch url} to use in a {@link RasterImageLink img html tag} 2104fd306cSNickeau * 2204fd306cSNickeau */ 2304fd306cSNickeauclass FetcherRaster extends IFetcherLocalImage 2404fd306cSNickeau{ 2504fd306cSNickeau 2604fd306cSNickeau use FetcherTraitWikiPath { 2704fd306cSNickeau setSourcePath as protected setOriginalPathTrait; 2804fd306cSNickeau } 2904fd306cSNickeau 3004fd306cSNickeau const CANONICAL = "raster"; 3104fd306cSNickeau const FAKE_LENGTH_FOR_BROKEN_IMAGES = 10; 3204fd306cSNickeau 3304fd306cSNickeau 3404fd306cSNickeau private int $imageWidth; 3504fd306cSNickeau private int $imageWeight; 3604fd306cSNickeau 3704fd306cSNickeau 3804fd306cSNickeau /** 3904fd306cSNickeau * @param string $imageId 4004fd306cSNickeau * @param null $rev 4104fd306cSNickeau * @return FetcherRaster 4204fd306cSNickeau * @throws ExceptionBadArgument 4304fd306cSNickeau * @throws ExceptionBadSyntax 4404fd306cSNickeau * @throws ExceptionNotExists 4504fd306cSNickeau */ 4604fd306cSNickeau public static function createImageRasterFetchFromId(string $imageId, $rev = null): FetcherRaster 4704fd306cSNickeau { 4804fd306cSNickeau return FetcherRaster::createImageRasterFetchFromPath(WikiPath::createMediaPathFromId($imageId, $rev)); 4904fd306cSNickeau } 5004fd306cSNickeau 5104fd306cSNickeau /** 5204fd306cSNickeau * @param Path $path 5304fd306cSNickeau * @return FetcherRaster 5404fd306cSNickeau * @throws ExceptionBadArgument 5504fd306cSNickeau * @throws ExceptionBadSyntax 5604fd306cSNickeau * @throws ExceptionNotExists 5704fd306cSNickeau */ 5804fd306cSNickeau public static function createImageRasterFetchFromPath(Path $path): FetcherRaster 5904fd306cSNickeau { 6004fd306cSNickeau $path = WikiPath::createFromPathObject($path); 6104fd306cSNickeau return self::createEmptyRaster() 6204fd306cSNickeau ->setSourcePath($path); 6304fd306cSNickeau } 6404fd306cSNickeau 6504fd306cSNickeau public static function createEmptyRaster(): FetcherRaster 6604fd306cSNickeau { 6704fd306cSNickeau return new FetcherRaster(); 6804fd306cSNickeau } 6904fd306cSNickeau 7004fd306cSNickeau /** 7104fd306cSNickeau * @throws ExceptionBadArgument 7204fd306cSNickeau */ 7304fd306cSNickeau public static function createRasterFromFetchUrl(Url $fetchUrl): FetcherRaster 7404fd306cSNickeau { 7504fd306cSNickeau $fetchImageRaster = self::createEmptyRaster(); 7604fd306cSNickeau $fetchImageRaster->buildFromUrl($fetchUrl); 7704fd306cSNickeau return $fetchImageRaster; 7804fd306cSNickeau } 7904fd306cSNickeau 8004fd306cSNickeau 8104fd306cSNickeau /** 8204fd306cSNickeau * @return int - the width of the image from the file 8304fd306cSNickeau */ 8404fd306cSNickeau public function getIntrinsicWidth(): int 8504fd306cSNickeau { 8604fd306cSNickeau return $this->imageWidth; 8704fd306cSNickeau } 8804fd306cSNickeau 8904fd306cSNickeau public function getFetchUrl(Url $url = null): Url 9004fd306cSNickeau { 9104fd306cSNickeau /** 9204fd306cSNickeau * 9304fd306cSNickeau * Because {@link FetcherRaster} does not create the image itself 9404fd306cSNickeau * but dokuwiki does, we need to add the with and height dimension 9504fd306cSNickeau * if the ratio is asked 9604fd306cSNickeau * 9704fd306cSNickeau * Before all other parent requirement such as 9804fd306cSNickeau * ({@link FetcherImage::getTok()} uses them 9904fd306cSNickeau * 10004fd306cSNickeau * Note that we takes the target value 10104fd306cSNickeau * before setting them otherwise it will affect the calculcation 10204fd306cSNickeau * ie if we set the height and then calculatiing the target width, we will get 10304fd306cSNickeau * a mini difference 10404fd306cSNickeau * 10504fd306cSNickeau */ 10604fd306cSNickeau try { 10704fd306cSNickeau $this->getRequestedAspectRatio(); 10804fd306cSNickeau $targetHeight = $this->getTargetHeight(); 10904fd306cSNickeau $targetWidth = $this->getTargetWidth(); 11004fd306cSNickeau $this->setRequestedWidth($targetWidth); 11104fd306cSNickeau $this->setRequestedHeight($targetHeight); 11204fd306cSNickeau } catch (ExceptionNotFound $e) { 11304fd306cSNickeau // 11404fd306cSNickeau } 11504fd306cSNickeau 11604fd306cSNickeau $url = parent::getFetchUrl($url); 11704fd306cSNickeau 11804fd306cSNickeau /** 11904fd306cSNickeau * Trait 12004fd306cSNickeau */ 12104fd306cSNickeau $this->addLocalPathParametersToFetchUrl($url, self::$MEDIA_QUERY_PARAMETER); 12204fd306cSNickeau 12304fd306cSNickeau return $url; 12404fd306cSNickeau } 12504fd306cSNickeau 12604fd306cSNickeau 12704fd306cSNickeau /** 12804fd306cSNickeau * @return int - the height of the image from the file 12904fd306cSNickeau */ 13004fd306cSNickeau public function getIntrinsicHeight(): int 13104fd306cSNickeau { 13204fd306cSNickeau return $this->imageWeight; 13304fd306cSNickeau } 13404fd306cSNickeau 13504fd306cSNickeau /** 13604fd306cSNickeau * We check the existence of the file at build time 13704fd306cSNickeau * because when we build the url, we make it at several breakpoints. 13804fd306cSNickeau * We therefore needs the intrinsic dimension (height and weight) 13904fd306cSNickeau * 14004fd306cSNickeau * @throws ExceptionBadSyntax - if the path is not valid image format 14104fd306cSNickeau */ 14204fd306cSNickeau private 14304fd306cSNickeau function analyzeImageIfNeeded() 14404fd306cSNickeau { 14504fd306cSNickeau 14604fd306cSNickeau if (!FileSystems::exists($this->getSourcePath())) { 14704fd306cSNickeau // The user may type a bad path 14804fd306cSNickeau // We don't throw as we want to be able to build 14904fd306cSNickeau LogUtility::warning("The path ({$this->getSourcePath()}) does not exists"); 15004fd306cSNickeau // broken image in the browser does not have any dimension 15104fd306cSNickeau // todo: https://bitsofco.de/styling-broken-images/ 15204fd306cSNickeau $this->imageWidth = self::FAKE_LENGTH_FOR_BROKEN_IMAGES; 15304fd306cSNickeau $this->imageWeight = self::FAKE_LENGTH_FOR_BROKEN_IMAGES; 15404fd306cSNickeau return; 15504fd306cSNickeau } 15604fd306cSNickeau 15704fd306cSNickeau /** 15804fd306cSNickeau * Based on {@link media_image_preview_size()} 15904fd306cSNickeau * $dimensions = media_image_preview_size($this->id, '', false); 16004fd306cSNickeau */ 16104fd306cSNickeau $path = $this->getSourcePath()->toLocalPath(); 16204fd306cSNickeau $imageSize = getimagesize($path->toAbsolutePath()->toAbsoluteId()); 16304fd306cSNickeau if ($imageSize === false) { 16404fd306cSNickeau throw new ExceptionBadSyntax("We couldn't retrieve the type and dimensions of the image ($this). The image format seems to be not supported.", self::CANONICAL); 16504fd306cSNickeau } 16604fd306cSNickeau $this->imageWidth = (int)$imageSize[0]; 16704fd306cSNickeau if (empty($this->imageWidth)) { 16804fd306cSNickeau throw new ExceptionBadSyntax("We couldn't retrieve the width of the image ($this)", self::CANONICAL); 16904fd306cSNickeau } 17004fd306cSNickeau $this->imageWeight = (int)$imageSize[1]; 17104fd306cSNickeau if (empty($this->imageWeight)) { 17204fd306cSNickeau throw new ExceptionBadSyntax("We couldn't retrieve the height of the image ($this)", self::CANONICAL); 17304fd306cSNickeau } 17404fd306cSNickeau 17504fd306cSNickeau } 17604fd306cSNickeau 17704fd306cSNickeau 17804fd306cSNickeau /** 17904fd306cSNickeau * We overwrite the {@link FetcherTraitImage::getRequestedWidth()} 18004fd306cSNickeau * because we don't scale up for raster image 18104fd306cSNickeau * to not lose quality. 18204fd306cSNickeau * 18304fd306cSNickeau * @return int 18404fd306cSNickeau * @throws ExceptionNotFound 18504fd306cSNickeau */ 18604fd306cSNickeau public 18704fd306cSNickeau function getRequestedWidth(): int 18804fd306cSNickeau { 18904fd306cSNickeau 19004fd306cSNickeau /** 19104fd306cSNickeau * Test, requested width should not be bigger than the media Height 19204fd306cSNickeau * If this is the case, we return the media width 19304fd306cSNickeau */ 19404fd306cSNickeau $requestedWidth = parent::getRequestedWidth(); 19504fd306cSNickeau 19604fd306cSNickeau /** 19704fd306cSNickeau * A width was requested 19804fd306cSNickeau */ 19904fd306cSNickeau $mediaWidth = $this->getIntrinsicWidth(); 20004fd306cSNickeau if ($requestedWidth > $mediaWidth) { 20104fd306cSNickeau global $ID; 20204fd306cSNickeau if ($ID !== "wiki:syntax") { 203*66a68f7fSgerardnico /** 204*66a68f7fSgerardnico * Info and not warning level because they fill the error log 205*66a68f7fSgerardnico * They don't really break anything and it's difficult 206*66a68f7fSgerardnico * to see when it's intended (ie there is no better image or not) 207*66a68f7fSgerardnico */ 20804fd306cSNickeau // There is a bug in the wiki syntax page 20904fd306cSNickeau // {{wiki:dokuwiki-128.png?200x50}} 21004fd306cSNickeau // https://forum.dokuwiki.org/d/19313-bugtypo-how-to-make-a-request-to-change-the-syntax-page-on-dokuwikii 211*66a68f7fSgerardnico LogUtility::info("For the image ($this), the requested width of ($requestedWidth) can not be bigger than the intrinsic width of ($mediaWidth). The width was then set to its natural width ($mediaWidth)", self::CANONICAL); 21204fd306cSNickeau } 21304fd306cSNickeau return $mediaWidth; 21404fd306cSNickeau } 21504fd306cSNickeau 21604fd306cSNickeau return $requestedWidth; 21704fd306cSNickeau 21804fd306cSNickeau } 21904fd306cSNickeau 22004fd306cSNickeau 22104fd306cSNickeau /** 22204fd306cSNickeau * 22304fd306cSNickeau */ 22404fd306cSNickeau public 22504fd306cSNickeau function getTargetHeight(): int 22604fd306cSNickeau { 22704fd306cSNickeau 22804fd306cSNickeau try { 22904fd306cSNickeau $requestedHeight = $this->getRequestedHeight(); 23004fd306cSNickeau 23104fd306cSNickeau // it should not be bigger than the media Height 23204fd306cSNickeau $mediaHeight = $this->getIntrinsicHeight(); 23304fd306cSNickeau if ($requestedHeight > $mediaHeight) { 234*66a68f7fSgerardnico /** 235*66a68f7fSgerardnico * Info and not warning level because they fill the error log 236*66a68f7fSgerardnico * They don't really break anything and it's difficult 237*66a68f7fSgerardnico * to see when it's intended (ie there is no better image or not) 238*66a68f7fSgerardnico */ 239*66a68f7fSgerardnico LogUtility::info("For the image ($this), the requested height of ($requestedHeight) can not be bigger than the intrinsic height of ($mediaHeight). The height was then set to its natural height ($mediaHeight)", self::CANONICAL); 24004fd306cSNickeau return $mediaHeight; 24104fd306cSNickeau } 24204fd306cSNickeau } catch (ExceptionNotFound $e) { 24304fd306cSNickeau // no request height 24404fd306cSNickeau } 24504fd306cSNickeau return parent::getTargetHeight(); 24604fd306cSNickeau 24704fd306cSNickeau 24804fd306cSNickeau } 24904fd306cSNickeau 2503e9dc6bfSgerardnico public function getTargetWidth(): int 2513e9dc6bfSgerardnico { 2523e9dc6bfSgerardnico $targetWidth = parent::getTargetWidth(); 2533e9dc6bfSgerardnico $intrinsicWidth = $this->getIntrinsicWidth(); 2543e9dc6bfSgerardnico if ($targetWidth > $intrinsicWidth) { 2553e9dc6bfSgerardnico LogUtility::warning("For the image ($this), the calculated width of ($targetWidth) cannot be bigger than the intrinsic width of ($targetWidth). The requested width was then set to its natural width ($intrinsicWidth).", self::CANONICAL); 2563e9dc6bfSgerardnico return $intrinsicWidth; 2573e9dc6bfSgerardnico } 2583e9dc6bfSgerardnico return $targetWidth; 2593e9dc6bfSgerardnico } 2603e9dc6bfSgerardnico 26104fd306cSNickeau 26204fd306cSNickeau function getFetchPath(): LocalPath 26304fd306cSNickeau { 26404fd306cSNickeau /** 26504fd306cSNickeau * In fetch.php 26604fd306cSNickeau * if($HEIGHT && $WIDTH) { 26704fd306cSNickeau * $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT); 26804fd306cSNickeau * } else { 26904fd306cSNickeau * $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT); 27004fd306cSNickeau * } 27104fd306cSNickeau */ 27204fd306cSNickeau throw new ExceptionRuntime("Fetch Raster image is not yet implemented"); 27304fd306cSNickeau } 27404fd306cSNickeau 27504fd306cSNickeau 27604fd306cSNickeau /** 27704fd306cSNickeau * @param TagAttributes $tagAttributes 27804fd306cSNickeau * @return FetcherRaster 27904fd306cSNickeau * @throws ExceptionBadArgument - if the path is not an image 28004fd306cSNickeau * @throws ExceptionBadSyntax - if the image is badly encoded 28104fd306cSNickeau * @throws ExceptionNotExists - if the image does not exists 28204fd306cSNickeau */ 28304fd306cSNickeau 28404fd306cSNickeau public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherImage 28504fd306cSNickeau { 28604fd306cSNickeau 28704fd306cSNickeau parent::buildFromTagAttributes($tagAttributes); 28804fd306cSNickeau $this->buildOriginalPathFromTagAttributes($tagAttributes); 28904fd306cSNickeau $this->analyzeImageIfNeeded(); 29004fd306cSNickeau return $this; 29104fd306cSNickeau 29204fd306cSNickeau } 29304fd306cSNickeau 29404fd306cSNickeau /** 29504fd306cSNickeau * @throws ExceptionBadSyntax - if the file is badly encoded 29604fd306cSNickeau * @throws ExceptionNotExists - if the file does not exists 29704fd306cSNickeau */ 29804fd306cSNickeau public function setSourcePath(WikiPath $path): FetcherRaster 29904fd306cSNickeau { 30004fd306cSNickeau $this->setOriginalPathTrait($path); 30104fd306cSNickeau $this->analyzeImageIfNeeded(); 30204fd306cSNickeau return $this; 30304fd306cSNickeau } 30404fd306cSNickeau 30504fd306cSNickeau 30604fd306cSNickeau public function getFetcherName(): string 30704fd306cSNickeau { 30804fd306cSNickeau return self::CANONICAL; 30904fd306cSNickeau } 31004fd306cSNickeau 31104fd306cSNickeau public function __toString() 31204fd306cSNickeau { 31304fd306cSNickeau return $this->getSourcePath()->__toString(); 31404fd306cSNickeau } 31504fd306cSNickeau 3163e9dc6bfSgerardnico /** 3173e9dc6bfSgerardnico * @return int 3183e9dc6bfSgerardnico * @throws ExceptionNotFound 3193e9dc6bfSgerardnico * We can upscale, we limit then the requested height to the internal size 3203e9dc6bfSgerardnico */ 3213e9dc6bfSgerardnico public function getRequestedHeight(): int 3223e9dc6bfSgerardnico { 3233e9dc6bfSgerardnico $requestedHeight = parent::getRequestedHeight(); 3243e9dc6bfSgerardnico $intrinsicHeight = $this->getIntrinsicHeight(); 3253e9dc6bfSgerardnico if ($requestedHeight > $intrinsicHeight) { 3263e9dc6bfSgerardnico LogUtility::warning("For the image ($this), the requested height of ($requestedHeight) can not be bigger than the intrinsic height of ($intrinsicHeight). The height was then set to its natural height ($intrinsicHeight)", self::CANONICAL); 3273e9dc6bfSgerardnico return $intrinsicHeight; 3283e9dc6bfSgerardnico } 3293e9dc6bfSgerardnico return $requestedHeight; 3303e9dc6bfSgerardnico } 3313e9dc6bfSgerardnico 33204fd306cSNickeau 33304fd306cSNickeau} 334