<?php
namespace Slivki\Services;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\ORM\EntityManagerInterface;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Gd\Font;
use Imagine\Gd\Imagine;
use Imagine\Image\Box;
use Imagine\Image\Palette\RGB;
use Imagine\Image\Point;
use Slivki\Entity\Media;
use Slivki\Entity\Media\AbstractMediaBase;
use Slivki\Entity\Media\OfferAppTeaserMedia;
use Slivki\Entity\Media\OfferMobileTeaserMedia;
use Slivki\Entity\Media\OfferTeaserMedia;
use Slivki\Entity\MediaSize;
use Slivki\Entity\MediaType;
use Slivki\Entity\Offer;
use Slivki\Entity\SaleProduct;
use Slivki\Util\SoftCache;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\KernelInterface;
class ImageService
{
const CACHE_NAME = "media-1-0-";
const MEDIA_ROOT = "/znijki-media/";
const INITIAL_PATH = "initial";
const BACKGROUNDS_PATH = "backgrounds/";
const BACKGROUNDS_CERTIFICATES_PATH = "backgrounds/certificates/";
const FALLBACK_IMAGE = "/common-img/d.gif";
const DEFAULT_AVATAR = "/images/default_avatar.png?v=2";
const IMAGE_URL_CACHE_EXPIRE = 1800;
const CACHE_KEY = "-url-1-0-";
private $mediaRepository;
private $entityManager;
private $webRoot;
private $baseUrl;
public function __construct(
EntityManagerInterface $entityManager,
KernelInterface $kernel,
string $baseUrl
) {
$this->mediaRepository = $entityManager->getRepository(Media::class);
$this->entityManager = $entityManager;
$this->webRoot = $kernel->getProjectDir() . "/public";
$this->baseUrl = $baseUrl;
}
public function getImageURLCached (AbstractMediaBase $media = null, $width, $height) {
if (!$media) {
return self::FALLBACK_IMAGE;
}
$softCache = new SoftCache(self::CACHE_NAME);
$cacheKey = $media->getID() . self::CACHE_KEY . $width . "x" . $height;
$imageURLCached = $softCache->get($cacheKey);
if ($imageURLCached) {
return $imageURLCached;
}
$imageURL = $this->getImageURL($media, $width, $height);
if (!$imageURL) {
return self::FALLBACK_IMAGE;
}
$softCache->set($cacheKey, $imageURL, self::IMAGE_URL_CACHE_EXPIRE);
return $imageURL;
}
public function mediaCacheReload(AbstractMediaBase $media): void
{
$softCache = new SoftCache(self::CACHE_NAME);
$softCache->delete(sprintf('%s%s%s', $media->getID(), self::CACHE_KEY, '0x0'));
foreach ($media->getSizes() as $mediaSize) {
$this->resizeImage($media, $mediaSize->getWidth(), $mediaSize->getHeight());
}
}
public function getImageURLCachedWithDomain(?AbstractMediaBase $media, int $width, int $height): string
{
return $this->baseUrl . $this->getImageURLCached($media, $width, $height);
}
public function getFallbackImageURLWithDomain(): string
{
return $this->baseUrl . self::FALLBACK_IMAGE;
}
public function getFullAvatarUrlByPath(string $path): string
{
return \sprintf('%s%s%s%s', $this->baseUrl, self::MEDIA_ROOT, self::INITIAL_PATH, $path);
}
public function getImageURL(AbstractMediaBase $media, $width, $height) {
$media = $this->entityManager->merge($media);
if ($width == 0 && $height == 0) {
return self::MEDIA_ROOT . self::INITIAL_PATH . $media->getPath() . $media->getName();
}
$mediaSizes = $media->getSizes();
$mediaSize = null;
foreach ($mediaSizes->toArray() as $size) {
if ($size->getWidth() == $width && $size->getHeight() == $height) {
$mediaSize = $size;
break;
}
}
if (!$mediaSize) {
$imageCreated = false;
$mediaSize = new MediaSize();
$mediaSize->setWidth($width);
$mediaSize->setHeight($height);
$mediaSize->setMedia($media);
try {
$this->entityManager->persist($mediaSize);
$this->entityManager->flush($mediaSize);
} catch (UniqueConstraintViolationException $exception) {
$imageCreated = false;
}
if (!$imageCreated) {
if (!$this->resizeImage($media, $width, $height)) {
//return false;
//$softCache = new SoftCache(self::CACHE_NAME);
//$cacheKey = $media->getID() . "-url-" . $width . "x" . $height;
//$softCache->set($cacheKey, self::FALLBACK_IMAGE, self::IMAGE_URL_CACHE_EXPIRE);
//return self::FALLBACK_IMAGE;
}
}
}
return $this->getMediaURL($media, $width, $height);
}
public function clearMediaUrlCache(AbstractMediaBase $media): void
{
$softCache = new SoftCache(self::CACHE_NAME);
$softCache->delete(sprintf('%d%s%dx%d', $media->getID(), self::CACHE_KEY, 0, 0));
foreach ($media->getSizes() as $mediaSize) {
$softCache->delete(
sprintf(
'%d%s%dx%d',
$media->getID(),
self::CACHE_KEY,
$mediaSize->getWidth(),
$mediaSize->getHeight(),
)
);
}
}
public function resizeImage(AbstractMediaBase $media, $width, $height) {
$mediaPathNew = $this->getMediaPath($media, $width, $height);
$this->makeMediaPath($mediaPathNew);
$originalMediaPath = $this->webRoot . self::MEDIA_ROOT . self::INITIAL_PATH . $media->getPath() . $media->getName();
if ($width == 0 && $height == 0) {
$fileSystem = new Filesystem();
if (!$fileSystem->exists($originalMediaPath)) {
return false;
}
$fileSystem->copy($originalMediaPath, $this->getFullMediaPath($media, $width, $height));
return true;
}
try {
$imagine = new Imagine();
$image = $imagine->open($originalMediaPath);
$errorReporting = error_reporting(E_ERROR);
$exifData = exif_read_data($originalMediaPath);
$rotateValue = 0;
if ($exifData !== false && isset($exifData['Orientation'])) {
switch ($exifData['Orientation']) {
case 8:
$rotateValue = -90;
break;
case 3:
$rotateValue = 180;
break;
case 6:
$rotateValue = 90;
break;
}
if ($rotateValue != 0) {
$image->rotate($rotateValue);
}
}
error_reporting($errorReporting);
if ($width == 0) {
$sourceImageSize = $image->getSize();
$newImageSize = $sourceImageSize->heighten($height);
} else if ($height == 0) {
$sourceImageSize = $image->getSize();
$newImageSize = $sourceImageSize->widen($width);
} else {
$newImageSize = new Box($width, $height);
}
$image->resize($newImageSize);
$fileName = $this->getFullMediaPath($media, $width, $height);
$image->save($fileName);
if ($media instanceof OfferTeaserMedia || $media instanceof OfferMobileTeaserMedia || $media instanceof OfferAppTeaserMedia) {
$tinifyResult = $this->tinify($fileName);
if (isset($tinifyResult['output']['url'])) {
copy($tinifyResult['output']['url'], $fileName);
}
}
} catch (InvalidArgumentException $exception) {
return false;
}
$softCache = new SoftCache(self::CACHE_NAME);
$cacheKey = $media->getID() . self::CACHE_KEY . $width . "x" . $height;
$softCache->delete($cacheKey);
return true;
}
private function getMediaPath(AbstractMediaBase $media, $width, $height) {
return "w" . $width . "_" . $height . $media->getPath();
}
public function getFullMediaPath(AbstractMediaBase $media, $width, $height) {
return $this->webRoot . self::MEDIA_ROOT . $this->getMediaPath($media, $width, $height) . $media->getName();
}
public function saveMailingTeaserImage(Offer $offer) {
$width = 500;
$height = 324;
$captionHeight = 60;
$teaserMedia = $offer->getTeaserMedia();
if (!$teaserMedia) {
return false;
}
$initialMediaPath = $this->webRoot . self::MEDIA_ROOT . self::INITIAL_PATH . $teaserMedia->getPath();
$teaserMediaName = $teaserMedia->getName();
if (!file_exists($initialMediaPath . $teaserMediaName)) {
return false;
}
$imagine = new Imagine();
$image = $imagine->open($initialMediaPath . $teaserMediaName);
$newImageSize = new Box($width, $height);
$image->resize($newImageSize);
if ($offer->getMailingTeaserMedia()) {
$newFileName = $offer->getMailingTeaserMedia()->getName();
} else {
$newFileName = 'm_' . $teaserMediaName;
$fs = new Filesystem();
while ($fs->exists($initialMediaPath. $newFileName)) {
$newFileName = time() . '_' . $newFileName;
}
}
$caption = $offer->getCaptionName();
if ($caption) {
$palette = new RGB();
$captionFont = new Font('/home/virtwww/slivki/public/fonts/roboto/Roboto-Bold.ttf' , 28, $palette->color('#ffffff'));
$caption = mb_strtoupper($caption);
$captionBox = $captionFont->box($caption);
while ($captionBox->getWidth() > $width - 20) {
$caption = str_replace('...', '', $caption);
$caption = mb_substr($caption, 0, -1) . '...';
$captionBox = $captionFont->box($caption);
}
$captionCoordinates = [
new Point(0, $height - $captionHeight),
new Point($width, $height - $captionHeight),
new Point($width, $height),
new Point(0, $height),
];
$image->draw()->polygon($captionCoordinates, $palette->color($offer->getCaptionColor()), true);
$image->draw()->text($caption, $captionFont, new Point((int)($width/2 - $captionBox->getWidth()/2), $height - 46));
}
$image->save($initialMediaPath . $newFileName, ['jpeg_quality' => 100]);
return $newFileName;
}
public function tinify($fileName) {
$apiKey = 'J7BW6n1n3gN3VmQ5rC8mqcnKdf4lsrJq';
$url = 'https://api.tinify.com/shrink';
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_USERPWD, 'api:' . $apiKey);
curl_setopt($curl, CURLOPT_POSTFIELDS, file_get_contents($fileName));
curl_setopt($curl, CURLOPT_URL, $url);
$result = curl_exec($curl);
curl_close($curl);
if (!$result) {
return false;
}
$result = json_decode($result, true);
return $result;
}
public function saveSaleProductMailingTeaserImage(SaleProduct $saleProduct) {
$width = 524;
$height = 340;
$padding = 20;
$suplpierLogoHeight = 60;
$teaserMedia = $saleProduct->getTeaserMedia();
if (!$teaserMedia) {
return false;
}
$initialMediaPath = $this->webRoot . self::MEDIA_ROOT . self::INITIAL_PATH . $teaserMedia->getPath();
$teaserMediaName = $teaserMedia->getName();
if (!file_exists($initialMediaPath . $teaserMediaName)) {
return false;
}
$imagine = new Imagine();
$image = $imagine->open($initialMediaPath . $teaserMediaName);
$newImageSize = new Box($width, $height);
$image->resize($newImageSize);
if ($saleProduct->getMailingTeaserMedia()) {
$newFileName = $saleProduct->getMailingTeaserMedia()->getName();
} else {
$newFileName = 'm_' . $teaserMediaName;
$fs = new Filesystem();
while ($fs->exists($initialMediaPath. $newFileName)) {
$newFileName = time() . '_' . $newFileName;
}
}
if ($saleProduct->getRegularPrice() > 0) {
$palette = new RGB();
$discountLabelFont = new Font('/home/virtwww/slivki/public/fonts/roboto/Roboto-Bold.ttf' , 28, $palette->color('#333333'));
$discountLabelBgColor = $palette->color('#00e196');
$discountLabelText = '-' . round($saleProduct->getDiscountPercent(), 0) . '%';
$discountLabelBox = $discountLabelFont->box($discountLabelText);
$discountLabelCoordinates = [
new Point($width - $discountLabelBox->getWidth() - 2 * $padding, 0),
new Point($width, 0),
new Point($width, $discountLabelBox->getHeight() + 2 * $padding),
new Point($width - $discountLabelBox->getWidth() - 2 * $padding, $discountLabelBox->getHeight() + 2 * $padding),
];
$image->draw()->polygon($discountLabelCoordinates, $discountLabelBgColor, true);
$discountLabelPosition = new Point($width - $discountLabelBox->getWidth() - $padding, $padding);
$image->draw()->text($discountLabelText, $discountLabelFont, $discountLabelPosition);
}
$supplierLogo = $this->entityManager->getRepository(Media::class)
->getMedia($saleProduct->getSaleVersion()->getSale()->getID(), MediaType::TYPE_SALE_FLIER_LOGO_ID);
if ($supplierLogo) {
$supplierLogo = $supplierLogo[0];
$supplierLogoImageFile = $this->webRoot . self::MEDIA_ROOT . self::INITIAL_PATH . $supplierLogo->getPath() . $supplierLogo->getName();
if (file_exists($supplierLogoImageFile)) {
$supplierLogoImage = $imagine->open($supplierLogoImageFile);
$imageSize = $supplierLogoImage->getSize();
$newSize = $imageSize->heighten($suplpierLogoHeight);
$supplierLogoImage->resize($newSize);
$image->paste($supplierLogoImage, new Point(10, 10));
}
}
$image->save($initialMediaPath . $newFileName, ['jpeg_quality' => 100]);
return $newFileName;
}
private function getMediaURL(AbstractMediaBase $media, $width, $height) {
return self::MEDIA_ROOT . $this->getMediaPath($media, $width, $height) . $media->getName();
}
private function makeMediaPath($mediaPath) {
$fileSystem = new Filesystem();
if ($fileSystem->exists($this->webRoot . self::MEDIA_ROOT . $mediaPath)) {
return;
}
$folders = explode("/", $mediaPath);
$mediaPath = $this->webRoot . self::MEDIA_ROOT;
foreach ($folders as $folder) {
if ($folder == "") {
continue;
}
$mediaPath .= $folder;
if (!$fileSystem->exists($mediaPath)) {
$fileSystem->mkdir($mediaPath);
}
$mediaPath .= "/";
}
}
}