diff --git a/Classes/Controller/DummyImageController.php b/Classes/Controller/DummyImageController.php new file mode 100644 index 0000000..9ade562 --- /dev/null +++ b/Classes/Controller/DummyImageController.php @@ -0,0 +1,170 @@ + 9999 || $height > 9999 ) { + $width = 9999; + } + + if ($height > 9999) { + $height = 9999; + } + + if (is_null($text)) { + $text = (string)$width . ' x ' . (string)$height; + } + + // create imagine + $palette = new Palette\RGB(); + $bgColor = $palette->color($backgroundColor); + $textColor = $palette->color($foregroundColor); + + // create image + $imageBox = new Box($width, $height); + $image = $this->imagineService->create($imageBox, $bgColor); + + // render shape + $this->renderShape($image, $textColor, $width, $height); + + // render text + if ($text) { + $this->renderText($image, $textColor, $width, $height, $text); + } + + // build result + $this->response->setHeader( 'Cache-Control', 'max-age=883000000'); + $this->response->setHeader( 'Content-type', 'image/png'); + return $image->get('png'); + } + + /** + * @param ImageInterface $image + * @param ColorInterface $color + * @param int $width + * @param int $height + */ + protected function renderShape(ImageInterface $image, ColorInterface $color, int $width, int $height): void + { + $imageAspectRatio = $width / $height; + $baseShapeWidth = 600; + $baseShapeHeight = 400; + $baseShapeAspectRatio = $baseShapeWidth / $baseShapeHeight; + + $baseShape = [ + new Point(15, 250), // left ground + new Point(15, 15), // left top + new Point(585, 15), // right top + new Point(585, 250), // right ground + new Point(580, 250), + + new Point(440, 110), // small mountain + new Point(360, 190), // saddle + new Point(220, 50), // big mountain + + new Point(20, 250), + ]; + + // transform shape to center of the image + $factor = ($imageAspectRatio > $baseShapeAspectRatio) ? (float)$height / (float)$baseShapeHeight : (float)$width / (float)$baseShapeWidth; + $xoffset = ($imageAspectRatio > $baseShapeAspectRatio) ? ($width - ($baseShapeWidth * $factor)) / 2.0 : 0.0; + $yoffset = ($imageAspectRatio < $baseShapeAspectRatio) ? ($height - ($baseShapeHeight * $factor)) / 2.0 : 0.0; + $transformedShape = array_map( + function (Point $point) use ($factor, $xoffset, $yoffset) { + return new Point($point->getX() * $factor + $xoffset, $point->getY() * $factor + $yoffset); + }, + $baseShape + ); + + // adjust some points based on aspect ratio + if ($imageAspectRatio < $baseShapeAspectRatio) { + $transformedShape[1] = new Point($transformedShape[1]->getX(), $baseShape[1]->getY() * $factor); + $transformedShape[2] = new Point($transformedShape[2]->getX(), $baseShape[2]->getY() * $factor); + } else { + $transformedShape[0] = new Point($baseShape[0]->getX() * $factor, $transformedShape[0]->getY()); + $transformedShape[1] = new Point($baseShape[0]->getX() * $factor, $transformedShape[1]->getY()); + $transformedShape[2] = new Point($width - $baseShape[0]->getX() * $factor, $transformedShape[2]->getY()); + $transformedShape[3] = new Point($width - $baseShape[0]->getX() * $factor, $transformedShape[3]->getY()); + } + + // finally draw image + $image->draw()->polygon( + $transformedShape, + $color, + true, + 1 + ); + } + + /** + * @param ImageInterface $image + * @param ColorInterface $textColor + * @param int $width + * @param int $height + * @param string $text + */ + protected function renderText(ImageInterface $image, ColorInterface $textColor, int $width, int $height, string $text): void + { + $initialFontSize = 10; + $fontFile = $this->packageManager->getPackage('Neos.Neos')->getPackagePath() . "Resources/Public/Fonts/NotoSans/NotoSans-Regular.ttf"; + $initialFont = $this->imagineService->font($fontFile, $initialFontSize, $textColor); + + // scale text to fit the image + $initialFontBox = $initialFont->box($text); + $targetFontWidth = $width * .5; + $targetFontHeight = $height * .3; + $correctedFontSizeByWidth = $targetFontWidth * $initialFontSize / $initialFontBox->getWidth(); + $correctedFontSizeByHeight = $targetFontHeight * $initialFontSize / $initialFontBox->getHeight(); + + // render actual text + $actualFont = $this->imagineService->font($fontFile, min([$correctedFontSizeByWidth, $correctedFontSizeByHeight]), $textColor); + $actualFontBox = $actualFont->box($text); + $imageCenterPosition = new Point($width / 2 , $height / 2); + $textCenterPosition = new Point\Center($actualFontBox); + $centeredTextPosition = new Point($imageCenterPosition->getX() - $textCenterPosition->getX(), ($height * .73 - $actualFontBox->getHeight() * .5)); + $image->draw()->text($text, $actualFont, $centeredTextPosition); + } + +} \ No newline at end of file diff --git a/Classes/EelHelpers/DummyImageSourceHelper.php b/Classes/EelHelpers/DummyImageSourceHelper.php index dbbf1cb..4881146 100644 --- a/Classes/EelHelpers/DummyImageSourceHelper.php +++ b/Classes/EelHelpers/DummyImageSourceHelper.php @@ -15,6 +15,16 @@ class DummyImageSourceHelper extends AbstractImageSourceHelper implements Scalab protected $text = null; + protected $baseUri = ''; + + /** + * @param ControllerContext $controllerContext + */ + public function __construct(string $baseUri) + { + $this->baseUri = $baseUri; + } + /** * @param int $baseWidth */ @@ -73,17 +83,16 @@ public function scale(float $factor): ImageSourceHelperInterface public function src(): string { - $url = 'https://dummyimage.com'; - - $url .= '/' . $this->getWidth() . 'x' . $this->getHeight(); - $url .= '/' . ($this->backgroundColor ?: '000'); - $url .= '/' . ($this->foregroundColor ?: 'fff'); - - if ($this->text) { - $url .= '&text=' . urlencode($this->text); - } - - return $url; + $uri = $this->baseUri . '?' . http_build_query ( + [ + 'width' => $this->getWidth(), + 'height' => $this->getHeight(), + 'backgroundColor' => ($this->backgroundColor ?: '000'), + 'foregroundColor' => ($this->foregroundColor ?: 'fff'), + 'text' => ($this->text ?: $this->getWidth() . ' x ' . $this->getHeight()) + ] + ); + return $uri; } public function getWidth() : int diff --git a/Classes/FusionObjects/DummyImageSourceImplementation.php b/Classes/FusionObjects/DummyImageSourceImplementation.php index cf7ca64..71150da 100644 --- a/Classes/FusionObjects/DummyImageSourceImplementation.php +++ b/Classes/FusionObjects/DummyImageSourceImplementation.php @@ -55,7 +55,10 @@ public function getText() */ public function createHelper() : ImageSourceHelperInterface { - $helper = new DummyImageSourceHelper(); + $uriBuilder = $this->runtime->getControllerContext()->getUriBuilder()->reset()->setCreateAbsoluteUri(true); + $baseUri = $uriBuilder->uriFor('image', [], 'DummyImage', 'Sitegeist.Kaleidoscope'); + + $helper = new DummyImageSourceHelper($baseUri); if ($baseWidth = $this->getBaseWidth()) { $helper->setBaseWidth($baseWidth); diff --git a/Configuration/Objects.yaml b/Configuration/Objects.yaml new file mode 100644 index 0000000..2367e45 --- /dev/null +++ b/Configuration/Objects.yaml @@ -0,0 +1,5 @@ +Sitegeist\Kaleidoscope\Controller\DummyImageController: + properties: + imagineService: + object: + factoryObjectName: Neos\Imagine\ImagineFactory \ No newline at end of file diff --git a/Configuration/Policy.yaml b/Configuration/Policy.yaml new file mode 100644 index 0000000..d379dd9 --- /dev/null +++ b/Configuration/Policy.yaml @@ -0,0 +1,11 @@ +privilegeTargets: + 'Neos\Flow\Security\Authorization\Privilege\Method\MethodPrivilege': + 'Sitegeist.Kaleidoscope:DummyImage': + matcher: 'method(Sitegeist\Kaleidoscope\Controller\DummyImageController->(image)Action())' + +roles: + 'Neos.Flow:Everybody': + privileges: + - + privilegeTarget: 'Sitegeist.Kaleidoscope:DummyImage' + permission: GRANT \ No newline at end of file diff --git a/Configuration/Routes.yaml b/Configuration/Routes.yaml new file mode 100644 index 0000000..29900da --- /dev/null +++ b/Configuration/Routes.yaml @@ -0,0 +1,12 @@ +## +# Kaleidoscope-DummyImage + +- + name: 'Kaleidoscope-DummyImage' + uriPattern: 'kaleidoscope/dummyimage' + defaults: + '@package': 'Sitegeist.Kaleidoscope' + '@controller': 'DummyImage' + '@action': 'image' + httpMethods: ['GET'] + appendExceedingArguments: TRUE diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 3838594..7d74f13 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -3,3 +3,9 @@ Neos: fusion: autoInclude: Sitegeist.Kaleidoscope: true + + Flow: + mvc: + routes: + 'Sitegeist.Kaleidoscope': + position: 'before Neos.Neos' \ No newline at end of file diff --git a/README.md b/README.md index e63e591..346868d 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,12 @@ We want to help implementing responsive-images in the context of atomic-fusion and enable previewing fusion-components and their full responsive behavior in the Sitegeist.Monocle living styleguide. -Sitegeist.Kaleidoscope comes with Fusion-ImageSources for Assets, DummyImages, Resources -and static Uris. +Sitegeist.Kaleidoscope comes with four Fusion-ImageSources: + +- Assets: Images uploaded by Editors +- DummyImages: Dummyimages created by a local service +- Resources: Static resources from Packages +- static Uris: any Url ### Authors & Sponsors diff --git a/composer.json b/composer.json index 376b21e..2165ad8 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "require": { "neos/flow": "*", "neos/fusion-afx": "*", - "neos/media": "*" + "neos/media": "*", + "neos/imagine": "*" }, "autoload": { "psr-4": {