Giter VIP home page Giter VIP logo

image's Introduction

Contao image library

This library provides methods to resize images based on resize configurations and generates responsive images to be used with <picture> and srcset. It is used in Contao to handle on-the-fly resizing of images.

Installation

php composer.phar require contao/image

Usage

Simple resize:

$imagine = new \Imagine\Gd\Imagine();
$resizer = new Resizer('/path/to/cache/dir');
$image = new Image('/path/to/image.jpg', $imagine);

$config = (new ResizeConfiguration())
    ->setWidth(100)
    ->setHeight(100)
    ->setMode(ResizeConfiguration::MODE_CROP)
;

$options = (new ResizeOptions())
    ->setImagineOptions([
        'jpeg_quality' => 95,
        'interlace' => \Imagine\Image\ImageInterface::INTERLACE_PLANE,
    ])
    ->setBypassCache(true)
    ->setTargetPath('/custom/target/path.jpg')
;

$resizedImage = $resizer->resize($image, $config, $options);

$resizedImage->getPath(); // /custom/target/path.jpg
$resizedImage->getUrl('/custom/target'); // path.jpg
$resizedImage->getUrl('/custom/target', 'https://example.com/'); // https://example.com/path.jpg

Responsive image:

$imagine = new \Imagine\Gd\Imagine();

$resizer = new Resizer('/path/to/cache/dir');
$pictureGenerator = new PictureGenerator($resizer);
$image = new Image('/path/to/image.jpg', $imagine);

$config = (new PictureConfiguration())
    ->setSize((new PictureConfigurationItem())
        ->setResizeConfig((new ResizeConfiguration())
            ->setWidth(100)
            ->setHeight(100)
            ->setMode(ResizeConfiguration::MODE_CROP)
        )
        ->setDensities('1x, 2x')
        ->setSizes('100vw')
    )
    ->setSizeItems([
        (new PictureConfigurationItem())
            ->setResizeConfig((new ResizeConfiguration())
                ->setWidth(400)
                ->setHeight(200)
                ->setMode(ResizeConfiguration::MODE_CROP)
            )
            ->setDensities('1x, 2x')
            ->setSizes('100vw')
            ->setMedia('(min-width: 900px)')
    ])
;

$options = (new ResizeOptions());
$picture = $pictureGenerator->generate($image, $config, $options);

$picture->getImg('/path/to');
/* [
    'src' => 'cache/dir/4/image-de332f09.jpg',
    'width' => 100,
    'height' => 100,
    'srcset' => 'cache/dir/4/image-de332f09.jpg 100w, cache/dir/4/image-9e0829dd.jpg 200w',
    'sizes' => '100vw',
] */

$picture->getSources('/path/to', 'https://example.com/');
/* [
    [
        'src' => 'https://example.com/cache/dir/c/image-996db4cf.jpg',
        'width' => 400,
        'height' => 200,
        'srcset' => 'https://example.com/cache/dir/c/image-996db4cf.jpg 400w, https://example.com/cache/dir/2/image-457dc5e0.jpg 800w',
        'sizes' => '100vw',
        'media' => '(min-width: 900px)',
    ],
] */

image's People

Contributors

aschempp avatar ausi avatar leofeyer avatar m-vo avatar toflar avatar xchs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

image's Issues

Prevent resizing of images when resize mode is proportional and width and height of the image equal

The ResizeConfiguration has a method where it checks whether an resize is unnecessary. Theres a missing check whether the height and width on the image are already equal when the resize mode is "proportional".

One good reason to change this behavior is: The image URLs don't change during an upgrade from an older version, this could be bad in perspective of seo (google images rankings).

This function:

/**
* Returns true if the resize would have no effect.
*/
public function isEmpty(): bool
{
return 0 === $this->width && 0 === $this->height && 0 === $this->zoomLevel;
}

Would need to check it like this:

    /**
     * Returns true if the resize would have no effect.
     */
    public function isEmpty(): bool
    {
        $size_and_mode_proportional = ($this->width === $this->height) && ($this->mode === self::MODE_PROPORTIONAL);

        return (0 === $this->width
            && 0 === $this->height
            && 0 === $this->zoomLevel)
            || $size_and_mode_proportional;
    }

Already used by contao 4

Sorry, if I abuse this for a question.

Is this library already used by contao 4?

I try to rewrite my imageondemand extension for contao 4 and currently searching how to implement it the best way. So I found this library but no informations. If this will be used in contao 4 in the future I want to rely on.

X descriptor doesn’t get corrected for too small images

// 1500x1500 large image
$image = new Image('/path/to/image.jpg', $imagine);

$config = (new PictureConfiguration())
    ->setSize((new PictureConfigurationItem())
        ->setResizeConfig((new ResizeConfiguration())
            ->setWidth(1000)
        )
        ->setDensities('1x, 2x')
    )
;

$pictureGenerator->generate($image, $config, new ResizeOptions())->getImg('/path/to')['srcset'];
// cache/dir/4/image-de332f09.jpg 1x, image.jpg 2x

The descriptor for the image.jpg should be 1.5x in this case, because the image didn’t get upscaled and is only 1500 pixels wide instead of the intended 2000.

Scaling factors should get rounded

A resize configuration with a width of 1920 and the densities set to 1.3333x should result in an image width of 2560 instead of 2559.

Symlinked cache directory resolves to invalid paths

Deferred images store the source path of the image relative to the cache directory in

$targetPath = Path::makeRelative($targetPath, $this->cacheDir);

When resizing the path gets appended to the cache dir path in

new Image($this->cacheDir.'/'.$config['path'], $imagine, $this->filesystem),

Resulting in something like /contao/assets/images/../../files/image.jpg. Now if /contao/assets/images is a symlink to /foo/bar/baz the path gets converted to /foo/files/image.jpg which is wrong. We should resolve the .. parts before creating the Image object.

Read EXIF metadata and show images with orientation

I think there should be support for image orientation.
Currently pictures taken in portrait format are displayed with the wrong orientation.

I'm not sure where it should be integrated. Either the images should be rotated to the right orientation during the upload process, or the orientation should be taken into account with each resize.

The imagine library support reading Exif metadata (https://imagine.readthedocs.io/en/latest/usage/metadata.html#exif-metadata-reader). They warn that it adds some overhead to the image handling, but I don't think it's too much in relation to the time needed for the resize process itself.
BTW, Nextcloud is supporting image orientation and it's incredible fast on my virtual server, so maybe the overhead is negligible.

Problem with responsive images

Issue by @mlwebworker
October 26th, 2017, 19:12 GMT

Es geht um die neue Variante bei Pixeldichte/Skalierungsfaktor - die Breitenbeschreibung (z.B. 300w), die ich sehr begrüße.
Bei einem Test ist mir allerdings zufällig aufgefallen, dass ein für mich nicht nachvollziehbares srcset entsteht, falls die angegebene Breite größer ist als die Breite des Originalbildes.
Beschrieben im Forum https://community.contao.org/de/showthread.php?68549-Responsive-geht-gar-nicht&p=454886&viewfull=1#post454886.
Da wir grundsätzlich nicht ausschließen können, dass Redakteure das auf zu kleine Originalbilder anwenden, sollte dieser Fall m.E. genauso abgefangen werden wie das passiert, wenn ich für ein Bild 600w angebe aber das Originalbild diese Breite gar nicht hergibt.

IPTC/EXIF metadata get lost when resizing images in Contao

We're having a legal issue right now, that boils down to:

  • when using stock images (istock, adobe stock, fotolia etc.) the source file contains copyright information
  • when resizing the image in Contao, the copyright information gets lost
  • removing copyright information from images is problematic, not to say: illegal

I did a good deal of investigation there, but still have not figured out what specifically Imagine is doing differently. Because Imagemagick and Graphicsmagick have native support for the IPTC and EXIF metadata and retain the data with no extra settings. Console tools behave the same as both PHP extensions. GD does not know how to handle metadata, but it's limited in many other ways too - that's where IM or GM come into play.

But channeling the process through Imagine is doing something to explicitly remove metadata :(

I found a couple of tickets there, but they've been closed for many years due to inactivity.

How could this be solved?
The image handling in Contao is a real USP, fixing the legal issue would be a mandatory step for me to keep it that way :)

Length of either side cannot be 0 or negative, current size is 2x0

See https://community.contao.org/de/showthread.php?81203

If a small image gets resized with a very wide aspect ratio the calculated height might end up being zero. This is invalid as there are no images with 0 height or width.

I think we should force a minimum width/height of 1 in

return new ResizeCoordinates(
new Box(
(int) round($size[0] * $scale),
(int) round($size[1] * $scale)
),
new Point(
(int) round($cropStart[0] * $scale),
(int) round($cropStart[1] * $scale)
),
new Box(
(int) round($cropSize[0] * $scale),
(int) round($cropSize[1] * $scale)
)
);

Use symfony/filesystem instead of webmozart/path-util

Composer told me

Package webmozart/path-util is abandoned, you should avoid using it. Use symfony/filesystem instead.

so I checked

composer why webmozart/path-util
contao/image  1.1.1  requires  webmozart/path-util (^2.0)  

Race condition

When resizing a lot of images one might run into race conditions. See stack trace:

ErrorException: fopen(/var/www/vhosts/foobar/httpdocs/releases/106/assets/images/deferred/5/e09d6c21-833fdbd0.jpg.json): failed to open stream: No such file or directory
#13 vendor/contao/image/src/DeferredImageStorageFilesystem.php(87): handleError
#12 src/AppBundle/Sentry/ErrorHandler.php(27): handleError
#11 src/AppBundle/Sentry/ErrorHandler.php(0): fopen
#10 vendor/contao/image/src/DeferredImageStorageFilesystem.php(87): getLocked
#9 vendor/contao/image/src/DeferredResizer.php(87): resizeDeferredImage
#8 vendor/contao/core-bundle/src/Command/ResizeImagesCommand.php(162): resizeImage
#7 vendor/contao/core-bundle/src/Command/ResizeImagesCommand.php(139): execute
#6 vendor/symfony/console/Command/Command.php(255): run
#5 vendor/symfony/console/Application.php(939): doRunCommand
#4 vendor/symfony/framework-bundle/Console/Application.php(87): doRunCommand
#3 vendor/symfony/console/Application.php(273): doRun
#2 vendor/symfony/framework-bundle/Console/Application.php(73): doRun
#1 vendor/symfony/console/Application.php(149): run
#0 vendor/contao/manager-bundle/bin/contao-console(42): null

Gmagick does not like tempnam without file extension

The resizer uses a temporary file for atomic write operations:

image/src/Resizer.php

Lines 123 to 127 in 9cd4150

// Atomic write operation
$tmpPath = $this->filesystem->tempnam($dir, 'img');
$this->filesystem->chmod($tmpPath, 0666, umask());
$imagineImage->save($tmpPath, $imagineOptions);
$this->filesystem->rename($tmpPath, $path, true);

This temporary file, however, does not have a file extension, which leads to an No encode delegate for this image format (webp) error in Gmagick on Ubuntu 16.04. The error did not occur in my dev environment on macOS.

Appending the file extension to the temporary file fixed the issue:

// Atomic write operation
$tmpPath = $this->filesystem->tempnam($dir, 'img');
$this->filesystem->chmod($tmpPath, 0666, umask());

// Append the file extension to the temporary file
rename($tmpPath, $tmpPath.'.'.$imagineOptions['format']);
$tmpPath .= '.'.$imagineOptions['format'];

$imagineImage->save($tmpPath, $imagineOptions);
$this->filesystem->rename($tmpPath, $path, true);

Not sure if there is a better way to accomplish this though.

Different imagine options should result in a different image path

In this example, the second resize returns the same image as the first one:

$options1 = (new ResizeOptions())
    ->setImagineOptions(['jpeg_quality' => 100])
;
$options2 = (new ResizeOptions())
    ->setImagineOptions(['jpeg_quality' => 50])
;

$resizedImage1 = $resizer->resize(..., ..., $options1);
$resizedImage2 = $resizer->resize(..., ..., $options2);

Relative path parts don’t get removed

$resizer = new Resizer('/path/to/subdir/../cache/dir');
$image = $resizer->resize(...);
$image->getUrl('/path/to');

This returns subdir/../cache/dir/image.jpg instead of cache/dir/image.jpg

Could probably get fixed by #11

Bug with resize configuration

When my image is too small to be resized, I thought contao/image is not supposed to touch it.
This is the case when I use this code:

$resizeConfig = new ResizeConfiguration();
$resizeConfig->setWidth($width)->setHeight($height)->setMode(ResizeConfiguration::MODE_PROPORTIONAL);
$image = $this->imageFactory->create($absolutePath, $resizeConfig);

However, if I do not set any mode (so the default one CROP is used), like so:

$resizeConfig = new ResizeConfiguration();
$resizeConfig->setWidth($width)->setHeight($height);
$image = $this->imageFactory->create($absolutePath, $resizeConfig);

I get a cropped version anyway. Is that intended? Even if my $width and $height are both smaller than what I set on the ResizeConfiguration?

Improve zlib stream test

The current test is not enough and may lead to

PHP message: PHP Warning:  XMLReader::open(): 
Unable to open source data in /vendor/contao/image/src/Image.php on line 201

The following diff solves the issue.

         if (null === $zlibSupport) {
-            $zlibSupport = \in_array('compress.zlib', stream_get_wrappers(), true);
+            $zlibSupport = \in_array('compress.zlib', stream_get_wrappers(), true)
+                && ($reader = new XMLReader)
+                && @$reader->open('compress.zlib://data:text/xml,<x/>') === true
+                && @$reader->read() === true
+                && @$reader->close() === true;
         }

behavior of responsive images when original image is smaller than target size

Affected version(s)
4.6.13 (I think in all 4.x)

Description
Too small images will be upscaled in frontend which causes an extreme loss of quality

How to reproduce
I have an image with a width of 71px. I make an image size setting with a media query (min-width: 1200px) and the following settings: width: 200px, Resize mode: Proportional, Pixel densities/scale factors: 1x, 1.5x, 2x (all other settings are empty). If I include the image with the image size, the image will be scaled to the target width of 200px, because there's a scale of 0.355x:

<picture>
<source srcset="files/xxx.jpg 0.355x" media="(min-width: 1200px)">
<img src="files/xxx.jpg" srcset="files/xxx.jpg 0.355x" alt="xxx" itemprop="image">
</picture>

bildschirmfoto 2019-01-18 um 12 27 38

This behavior is not correct, because the image should not be additional upscaled. The upscaling for the retina screen is at least 2x by default and additionally 1/0,355 = 2,817. So the final upscaling of the image will be 2*1/0,355 = 5,633802817.

If I don't use the media query setting and add my settings to the default setting of the image size, the behavior is correct: there's no output of scaling and the image is not bigger than the original size.

bildschirmfoto 2019-01-18 um 12 27 10

So the behavior should be identical and there should be no scaling factor output if the original image is smaller than the target image size.

/cc @ausi

Max zoom density setting

Currently if you have an image with a very small important part and an image size with a zoom level of 100% you will get very small images that will be shown blurry to the user as they will get upscaled by the browser.

It might make sense to add a setting for a minimum density that ensures that the zoom will stop as soon as the minimum density is reached. Possible names: zoomMinDensity, maxZoomDensity,…

/cc @Toflar

unexpected behaviour with svg-images and image-sizes

Issue by @asaage
April 10th, 2018, 15:56 GMT

i noticed that when i use image-sizes in combination with svg-images
additional svg's in the srcset are created under assets/images/

<img src="assets/images/4/foo-b4a0c460.svg"
 srcset="assets/images/4/foo-b4a0c460.svg 1x, assets/images/3/foo-eeec80d9.svg 2x" 
width="250" height="250" alt="foo" itemprop="image">

The svg's i am using do not contain width/height attributes - only the viewbox attribute and i did not set any important-part for them in the filemanager.
I would expect contao to just set the width and height on the <img> and not create additional svg's with contained width/height attributes.

Of course i could just use a different image-size without pixel-density descriptors but i have mixed filetypes in a gallery so that's not really an option.

The same source appears multiple times for too small images

// 100x100 large image
$image = new Image('/path/to/image.jpg', $imagine);

$config = (new PictureConfiguration())
    ->setSize((new PictureConfigurationItem())
        ->setResizeConfig((new ResizeConfiguration())
            ->setWidth(50)
        )
        ->setDensities('1x, 2x, 3x, 4x')
    )
;

$pictureGenerator->generate($image, $config, new ResizeOptions())->getImg('/path/to')['srcset'];
// cache/dir/4/image-de332f09.jpg 1x, image.jpg 2x, image.jpg 2x, image.jpg 2x

Duplicate sources should get removed from $srcset in PictureGenerator.php:99.

Add more resize modes

Should we add more resize modes like a version of box that adds a background color to the image to get exactly the configured size?

A general background color setting might be useful for other cases too like converting images with transparency to JPEG.

/cc @bennyborn
Related: contao/contao#3347

Are there other resize modes that might be useful?

Percentage values for the important part

Working on contao/core-bundle#651 I realized that it would have made much more sense to use percentages instead of integer-pixel-values for the ImportantPart. I see some benefits with it:

  1. If an image has no important part it just defaults to {x: 0%, y: 0%, width: 100%, height: 100%} and we don’t have to read the dimensions from the image as in Image::getImportantPart()
  2. An important part object can be validated very easily without needing to know anything about the image.
  3. Features like contao/core#8360 (comment) would be easier to implement.

Is it too late to change that in image 0.4 / core-bundle 4.4?
cc @contao/developers

Remove constructors from interfaces

I think constructors should not be part of an interface in most cases. I should remove the constructors of all interfaces in this library.

@contao/developers do you agree?

Add a prefix argument to Image::getUrl()

To be used like:

$image = new Image('/path/to/root/foo/bar.jpg', ...);
$image->getUrl('/path/to/root', 'https://cdn.example.com/images/');
// 'https://cdn.example.com/images/foo/bar.jpg'

Image attributes partial override

Is it possible to override only one of the needed attributes (set in the "Dateiverwaltung", var = default_image_meta) in the image ("Metadaten überschreiben")?
I suggest to show default_image_meta every time "Metadaten überschreiben" is needed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.