wiki-grav/plugins/automagic-images/automagic-images.php
2022-06-28 13:43:53 +02:00

229 lines
7.4 KiB
PHP

<?php
namespace Grav\Plugin;
use Grav\Common\Plugin;
use Grav\Common\Page\Page;
use Grav\Common\Flex\Types\Pages\PageObject;
use RocketTheme\Toolbox\Event\Event;
use Grav\Common\Data;
use Grav\Common\Grav;
use PHPHtmlParser\Dom;
use PHPHtmlParser\Options;
require_once 'adapters/imagick.php';
require_once 'adapters/gd.php';
/**
* Class AutomagicImagesPlugin
* @package Grav\Plugin
*/
class AutomagicImagesPlugin extends Plugin
{
/**
* @var string
*/
protected $adapter;
/**
* @var array
*/
protected $sizes;
/**
* @return array
*/
public static function getSubscribedEvents()
{
return [
'onAdminSave' => ['onAdminSave', 0],
'onOutputGenerated' => ['onOutputGenerated', 0]
];
}
/**
* Determine whether a particular dependency is installed.
* @param string $adapter Either 'gd' or 'imagick'
* @return bool
*/
protected function dependencyCheck($adapter = 'gd')
{
if ($adapter === 'gd') {
return extension_loaded('gd');
}
if ($adapter === 'imagick') {
return class_exists('\Imagick');
}
}
/**
* Determine which adapter is preferred and whether or not it's available.
* Construct an instance of that adapter and return it.
* @param string $source - Source image path
* @return mixed - Either an instance of ImagickAdapter, GDAdapter or false if none of the extensions were available
*/
protected function getImageAdapter($source)
{
$imagick_exists = $this->dependencyCheck('imagick');
$gd_exists = $this->dependencyCheck('gd');
if ($this->adapter === 'imagick') {
if ($imagick_exists) {
return new ImagickAdapter($source);
} else if ($gd_exists) {
return new GDAdapter($source);
}
} else if ($this->adapter === 'gd') {
if ($gd_exists) {
return new GDAdapter($source);
} else if ($imagick_exists) {
return new ImagickAdapter($source);
}
}
}
/**
* Resizes an image using either Imagick or GD
* @param string $source - Source image path
* @param string $target - Target image path
* @param float $width - Target width
* @param float $height - Target height
* @param int [$quality=95] - Compression quality for target image
* @return bool - Returns true on success, otherwise false
*/
protected function resizeImage($source, $target, $width, $height, $quality = 95)
{
$adapter = $this->getImageAdapter($source);
$adapter->resize($width, $height);
$adapter->setQuality($quality);
return $adapter->save($target);
}
/**
* Called when a page is saved from the admin plugin. Will generate
* responsive image alternatives for images that don't have any.
*/
public function onAdminSave($event)
{
$page = $event['object'];
if (!($page instanceof Page || $page instanceof PageObject)) {
return false;
}
if (!$this->dependencyCheck('imagick') && !$this->dependencyCheck('gd')) {
$this->grav['admin']->setMessage('Neither Imagick nor GD seem to be installed. Automagic Images needs one of them to work.', 'warning');
return;
}
$this->sizes = (array) $this->config->get('plugins.automagic-images.sizes');
$this->adapter = $this->config->get('plugins.automagic-images.adapter', 'imagick');
foreach ($page->media()->images() as $filename => $medium) {
$srcset = $medium->srcset(false);
if ($srcset != '') {
continue;
}
// We can't rely on the path returned from the image's own path
// method, since it points to the directory where the image is saved
// rather than where the original is stored. This means it could
// point to the global image cache directory.
$page_path = $page->path();
$source_path = "$page_path/$filename";
$info = pathinfo($source_path);
$count = 0;
foreach ($this->sizes as $i => $size) {
if ($size['width'] >= $medium->width) {
continue;
}
$count++;
$basename = str_replace(" ", "-", $info['filename']);
$ext = strtolower($info['extension']);
$dest_path = "{$info['dirname']}/{$basename}@{$count}x.{$ext}";
$width = $size['width'];
$quality = $size['quality'];
$height = ($width / $medium->width) * $medium->height;
$this->resizeImage($source_path, $dest_path, $width, $height, $quality, $medium->width, $medium->height);
}
$remove_original = $this->config->get('plugins.automagic-images.remove_original');
if ($count > 0) {
$original_index = $count + 1;
if ($remove_original) {
unlink($source_path);
} else {
rename($source_path, "{$info['dirname']}/{$basename}@{$original_index}x.{$ext}");
}
$fixed_source = str_replace($info['filename'], $basename, $source_path);
$fixed_source = str_replace($info['extension'], $ext, $fixed_source);
rename("{$info['dirname']}/{$basename}@1x.{$ext}", $fixed_source);
}
$message = "Resized $filename $count times";
if ($remove_original) {
$message .= ' (and removed the original image)';
}
$this->grav['admin']->setMessage($message, 'info');
}
}
/**
* Iterates over images in page content that was generated via twig and adds
* sizes attribute (not cacheable)
*
* @return void
*/
public function onOutputGenerated()
{
if ($this->isAdmin()) {
return;
}
$config = (array) $this->config->get('plugins.automagic-images');
$page = $this->grav['page'];
// dump($this->grav->output); exit;
$config = $this->mergeConfig($page);
if ($config['enabled']) {
include __DIR__ . '/vendor/autoload.php';
$dom = new Dom;
$dom->loadStr($this->grav->output,
(new Options())->setCleanupInput(false)
);
$images = $dom->find('img');
$arrClasses = [];
foreach ($config['sizesattr'] as $array) {
$arrClasses[$array['class']] = $array['directive'];
}
foreach ($images as $image) {
$sizesattr = "";
$classes = explode(" ", $image->getAttribute('class'));
foreach ($classes as $class) {
if (array_key_exists($class, $arrClasses)) {
$sizesattr = $arrClasses[$class];
}
}
if ($sizesattr == "") {
if (array_key_exists('default', $arrClasses)) {
$sizesattr = $arrClasses['default'];
}
}
if ($sizesattr != "") {
$image->setAttribute('sizes', $sizesattr);
}
}
$this->grav->output = $dom->outerHtml;
}
}
}