1*04fd306cSNickeau<?php 2*04fd306cSNickeau 3*04fd306cSNickeau 4*04fd306cSNickeaunamespace ComboStrap; 5*04fd306cSNickeau 6*04fd306cSNickeauuse ComboStrap\Web\Url; 7*04fd306cSNickeau 8*04fd306cSNickeau/** 9*04fd306cSNickeau * 10*04fd306cSNickeau * A Image Raster processing class that: 11*04fd306cSNickeau * * takes as input: 12*04fd306cSNickeau * * a {@link FetcherRaster::buildFromUrl() fetch URL}: 13*04fd306cSNickeau * * from an HTTP request 14*04fd306cSNickeau * * or {@link MediaMarkup::getFetchUrl() markup}) 15*04fd306cSNickeau * * or data via setter 16*04fd306cSNickeau * * outputs: 17*04fd306cSNickeau * * a {@link FetcherRaster::getFetchPath() raster image file} for: 18*04fd306cSNickeau * * an HTTP response 19*04fd306cSNickeau * * or further local processing 20*04fd306cSNickeau * * or a {@link FetcherRaster::getFetchUrl() fetch url} to use in a {@link RasterImageLink img html tag} 21*04fd306cSNickeau * 22*04fd306cSNickeau */ 23*04fd306cSNickeauclass FetcherRaster extends IFetcherLocalImage 24*04fd306cSNickeau{ 25*04fd306cSNickeau 26*04fd306cSNickeau use FetcherTraitWikiPath { 27*04fd306cSNickeau setSourcePath as protected setOriginalPathTrait; 28*04fd306cSNickeau } 29*04fd306cSNickeau 30*04fd306cSNickeau const CANONICAL = "raster"; 31*04fd306cSNickeau const FAKE_LENGTH_FOR_BROKEN_IMAGES = 10; 32*04fd306cSNickeau 33*04fd306cSNickeau 34*04fd306cSNickeau private int $imageWidth; 35*04fd306cSNickeau private int $imageWeight; 36*04fd306cSNickeau 37*04fd306cSNickeau 38*04fd306cSNickeau /** 39*04fd306cSNickeau * @param string $imageId 40*04fd306cSNickeau * @param null $rev 41*04fd306cSNickeau * @return FetcherRaster 42*04fd306cSNickeau * @throws ExceptionBadArgument 43*04fd306cSNickeau * @throws ExceptionBadSyntax 44*04fd306cSNickeau * @throws ExceptionNotExists 45*04fd306cSNickeau */ 46*04fd306cSNickeau public static function createImageRasterFetchFromId(string $imageId, $rev = null): FetcherRaster 47*04fd306cSNickeau { 48*04fd306cSNickeau return FetcherRaster::createImageRasterFetchFromPath(WikiPath::createMediaPathFromId($imageId, $rev)); 49*04fd306cSNickeau } 50*04fd306cSNickeau 51*04fd306cSNickeau /** 52*04fd306cSNickeau * @param Path $path 53*04fd306cSNickeau * @return FetcherRaster 54*04fd306cSNickeau * @throws ExceptionBadArgument 55*04fd306cSNickeau * @throws ExceptionBadSyntax 56*04fd306cSNickeau * @throws ExceptionNotExists 57*04fd306cSNickeau */ 58*04fd306cSNickeau public static function createImageRasterFetchFromPath(Path $path): FetcherRaster 59*04fd306cSNickeau { 60*04fd306cSNickeau $path = WikiPath::createFromPathObject($path); 61*04fd306cSNickeau return self::createEmptyRaster() 62*04fd306cSNickeau ->setSourcePath($path); 63*04fd306cSNickeau } 64*04fd306cSNickeau 65*04fd306cSNickeau public static function createEmptyRaster(): FetcherRaster 66*04fd306cSNickeau { 67*04fd306cSNickeau return new FetcherRaster(); 68*04fd306cSNickeau } 69*04fd306cSNickeau 70*04fd306cSNickeau /** 71*04fd306cSNickeau * @throws ExceptionBadArgument 72*04fd306cSNickeau */ 73*04fd306cSNickeau public static function createRasterFromFetchUrl(Url $fetchUrl): FetcherRaster 74*04fd306cSNickeau { 75*04fd306cSNickeau $fetchImageRaster = self::createEmptyRaster(); 76*04fd306cSNickeau $fetchImageRaster->buildFromUrl($fetchUrl); 77*04fd306cSNickeau return $fetchImageRaster; 78*04fd306cSNickeau } 79*04fd306cSNickeau 80*04fd306cSNickeau 81*04fd306cSNickeau /** 82*04fd306cSNickeau * @return int - the width of the image from the file 83*04fd306cSNickeau */ 84*04fd306cSNickeau public function getIntrinsicWidth(): int 85*04fd306cSNickeau { 86*04fd306cSNickeau return $this->imageWidth; 87*04fd306cSNickeau } 88*04fd306cSNickeau 89*04fd306cSNickeau public function getFetchUrl(Url $url = null): Url 90*04fd306cSNickeau { 91*04fd306cSNickeau /** 92*04fd306cSNickeau * 93*04fd306cSNickeau * Because {@link FetcherRaster} does not create the image itself 94*04fd306cSNickeau * but dokuwiki does, we need to add the with and height dimension 95*04fd306cSNickeau * if the ratio is asked 96*04fd306cSNickeau * 97*04fd306cSNickeau * Before all other parent requirement such as 98*04fd306cSNickeau * ({@link FetcherImage::getTok()} uses them 99*04fd306cSNickeau * 100*04fd306cSNickeau * Note that we takes the target value 101*04fd306cSNickeau * before setting them otherwise it will affect the calculcation 102*04fd306cSNickeau * ie if we set the height and then calculatiing the target width, we will get 103*04fd306cSNickeau * a mini difference 104*04fd306cSNickeau * 105*04fd306cSNickeau */ 106*04fd306cSNickeau try { 107*04fd306cSNickeau $this->getRequestedAspectRatio(); 108*04fd306cSNickeau $targetHeight = $this->getTargetHeight(); 109*04fd306cSNickeau $targetWidth = $this->getTargetWidth(); 110*04fd306cSNickeau $this->setRequestedWidth($targetWidth); 111*04fd306cSNickeau $this->setRequestedHeight($targetHeight); 112*04fd306cSNickeau } catch (ExceptionNotFound $e) { 113*04fd306cSNickeau // 114*04fd306cSNickeau } 115*04fd306cSNickeau 116*04fd306cSNickeau $url = parent::getFetchUrl($url); 117*04fd306cSNickeau 118*04fd306cSNickeau /** 119*04fd306cSNickeau * Trait 120*04fd306cSNickeau */ 121*04fd306cSNickeau $this->addLocalPathParametersToFetchUrl($url, self::$MEDIA_QUERY_PARAMETER); 122*04fd306cSNickeau 123*04fd306cSNickeau return $url; 124*04fd306cSNickeau } 125*04fd306cSNickeau 126*04fd306cSNickeau 127*04fd306cSNickeau /** 128*04fd306cSNickeau * @return int - the height of the image from the file 129*04fd306cSNickeau */ 130*04fd306cSNickeau public function getIntrinsicHeight(): int 131*04fd306cSNickeau { 132*04fd306cSNickeau return $this->imageWeight; 133*04fd306cSNickeau } 134*04fd306cSNickeau 135*04fd306cSNickeau /** 136*04fd306cSNickeau * We check the existence of the file at build time 137*04fd306cSNickeau * because when we build the url, we make it at several breakpoints. 138*04fd306cSNickeau * We therefore needs the intrinsic dimension (height and weight) 139*04fd306cSNickeau * 140*04fd306cSNickeau * @throws ExceptionBadSyntax - if the path is not valid image format 141*04fd306cSNickeau */ 142*04fd306cSNickeau private 143*04fd306cSNickeau function analyzeImageIfNeeded() 144*04fd306cSNickeau { 145*04fd306cSNickeau 146*04fd306cSNickeau if (!FileSystems::exists($this->getSourcePath())) { 147*04fd306cSNickeau // The user may type a bad path 148*04fd306cSNickeau // We don't throw as we want to be able to build 149*04fd306cSNickeau LogUtility::warning("The path ({$this->getSourcePath()}) does not exists"); 150*04fd306cSNickeau // broken image in the browser does not have any dimension 151*04fd306cSNickeau // todo: https://bitsofco.de/styling-broken-images/ 152*04fd306cSNickeau $this->imageWidth = self::FAKE_LENGTH_FOR_BROKEN_IMAGES; 153*04fd306cSNickeau $this->imageWeight = self::FAKE_LENGTH_FOR_BROKEN_IMAGES; 154*04fd306cSNickeau return; 155*04fd306cSNickeau } 156*04fd306cSNickeau 157*04fd306cSNickeau /** 158*04fd306cSNickeau * Based on {@link media_image_preview_size()} 159*04fd306cSNickeau * $dimensions = media_image_preview_size($this->id, '', false); 160*04fd306cSNickeau */ 161*04fd306cSNickeau $path = $this->getSourcePath()->toLocalPath(); 162*04fd306cSNickeau $imageSize = getimagesize($path->toAbsolutePath()->toAbsoluteId()); 163*04fd306cSNickeau if ($imageSize === false) { 164*04fd306cSNickeau 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); 165*04fd306cSNickeau } 166*04fd306cSNickeau $this->imageWidth = (int)$imageSize[0]; 167*04fd306cSNickeau if (empty($this->imageWidth)) { 168*04fd306cSNickeau throw new ExceptionBadSyntax("We couldn't retrieve the width of the image ($this)", self::CANONICAL); 169*04fd306cSNickeau } 170*04fd306cSNickeau $this->imageWeight = (int)$imageSize[1]; 171*04fd306cSNickeau if (empty($this->imageWeight)) { 172*04fd306cSNickeau throw new ExceptionBadSyntax("We couldn't retrieve the height of the image ($this)", self::CANONICAL); 173*04fd306cSNickeau } 174*04fd306cSNickeau 175*04fd306cSNickeau } 176*04fd306cSNickeau 177*04fd306cSNickeau 178*04fd306cSNickeau /** 179*04fd306cSNickeau * We overwrite the {@link FetcherTraitImage::getRequestedWidth()} 180*04fd306cSNickeau * because we don't scale up for raster image 181*04fd306cSNickeau * to not lose quality. 182*04fd306cSNickeau * 183*04fd306cSNickeau * @return int 184*04fd306cSNickeau * @throws ExceptionNotFound 185*04fd306cSNickeau */ 186*04fd306cSNickeau public 187*04fd306cSNickeau function getRequestedWidth(): int 188*04fd306cSNickeau { 189*04fd306cSNickeau 190*04fd306cSNickeau /** 191*04fd306cSNickeau * Test, requested width should not be bigger than the media Height 192*04fd306cSNickeau * If this is the case, we return the media width 193*04fd306cSNickeau */ 194*04fd306cSNickeau $requestedWidth = parent::getRequestedWidth(); 195*04fd306cSNickeau 196*04fd306cSNickeau /** 197*04fd306cSNickeau * A width was requested 198*04fd306cSNickeau */ 199*04fd306cSNickeau $mediaWidth = $this->getIntrinsicWidth(); 200*04fd306cSNickeau if ($requestedWidth > $mediaWidth) { 201*04fd306cSNickeau global $ID; 202*04fd306cSNickeau if ($ID !== "wiki:syntax") { 203*04fd306cSNickeau // There is a bug in the wiki syntax page 204*04fd306cSNickeau // {{wiki:dokuwiki-128.png?200x50}} 205*04fd306cSNickeau // https://forum.dokuwiki.org/d/19313-bugtypo-how-to-make-a-request-to-change-the-syntax-page-on-dokuwikii 206*04fd306cSNickeau LogUtility::warning("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); 207*04fd306cSNickeau } 208*04fd306cSNickeau return $mediaWidth; 209*04fd306cSNickeau } 210*04fd306cSNickeau 211*04fd306cSNickeau return $requestedWidth; 212*04fd306cSNickeau 213*04fd306cSNickeau } 214*04fd306cSNickeau 215*04fd306cSNickeau 216*04fd306cSNickeau /** 217*04fd306cSNickeau * 218*04fd306cSNickeau */ 219*04fd306cSNickeau public 220*04fd306cSNickeau function getTargetHeight(): int 221*04fd306cSNickeau { 222*04fd306cSNickeau 223*04fd306cSNickeau try { 224*04fd306cSNickeau $requestedHeight = $this->getRequestedHeight(); 225*04fd306cSNickeau 226*04fd306cSNickeau // it should not be bigger than the media Height 227*04fd306cSNickeau $mediaHeight = $this->getIntrinsicHeight(); 228*04fd306cSNickeau if ($requestedHeight > $mediaHeight) { 229*04fd306cSNickeau LogUtility::warning("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); 230*04fd306cSNickeau return $mediaHeight; 231*04fd306cSNickeau } 232*04fd306cSNickeau } catch (ExceptionNotFound $e) { 233*04fd306cSNickeau // no request height 234*04fd306cSNickeau } 235*04fd306cSNickeau return parent::getTargetHeight(); 236*04fd306cSNickeau 237*04fd306cSNickeau 238*04fd306cSNickeau } 239*04fd306cSNickeau 240*04fd306cSNickeau 241*04fd306cSNickeau function getFetchPath(): LocalPath 242*04fd306cSNickeau { 243*04fd306cSNickeau /** 244*04fd306cSNickeau * In fetch.php 245*04fd306cSNickeau * if($HEIGHT && $WIDTH) { 246*04fd306cSNickeau * $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT); 247*04fd306cSNickeau * } else { 248*04fd306cSNickeau * $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT); 249*04fd306cSNickeau * } 250*04fd306cSNickeau */ 251*04fd306cSNickeau throw new ExceptionRuntime("Fetch Raster image is not yet implemented"); 252*04fd306cSNickeau } 253*04fd306cSNickeau 254*04fd306cSNickeau 255*04fd306cSNickeau /** 256*04fd306cSNickeau * @param TagAttributes $tagAttributes 257*04fd306cSNickeau * @return FetcherRaster 258*04fd306cSNickeau * @throws ExceptionBadArgument - if the path is not an image 259*04fd306cSNickeau * @throws ExceptionBadSyntax - if the image is badly encoded 260*04fd306cSNickeau * @throws ExceptionNotExists - if the image does not exists 261*04fd306cSNickeau */ 262*04fd306cSNickeau 263*04fd306cSNickeau public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherImage 264*04fd306cSNickeau { 265*04fd306cSNickeau 266*04fd306cSNickeau parent::buildFromTagAttributes($tagAttributes); 267*04fd306cSNickeau $this->buildOriginalPathFromTagAttributes($tagAttributes); 268*04fd306cSNickeau $this->analyzeImageIfNeeded(); 269*04fd306cSNickeau return $this; 270*04fd306cSNickeau 271*04fd306cSNickeau } 272*04fd306cSNickeau 273*04fd306cSNickeau /** 274*04fd306cSNickeau * @throws ExceptionBadSyntax - if the file is badly encoded 275*04fd306cSNickeau * @throws ExceptionNotExists - if the file does not exists 276*04fd306cSNickeau */ 277*04fd306cSNickeau public function setSourcePath(WikiPath $path): FetcherRaster 278*04fd306cSNickeau { 279*04fd306cSNickeau $this->setOriginalPathTrait($path); 280*04fd306cSNickeau $this->analyzeImageIfNeeded(); 281*04fd306cSNickeau return $this; 282*04fd306cSNickeau } 283*04fd306cSNickeau 284*04fd306cSNickeau 285*04fd306cSNickeau public function getFetcherName(): string 286*04fd306cSNickeau { 287*04fd306cSNickeau return self::CANONICAL; 288*04fd306cSNickeau } 289*04fd306cSNickeau 290*04fd306cSNickeau public function __toString() 291*04fd306cSNickeau { 292*04fd306cSNickeau return $this->getSourcePath()->__toString(); 293*04fd306cSNickeau } 294*04fd306cSNickeau 295*04fd306cSNickeau 296*04fd306cSNickeau} 297