11ty / eleventy-img

Utility to perform build-time image transformations.
https://www.11ty.dev/docs/plugins/image/
433 stars 53 forks source link

Feature Request: Option to Preserve Metadata on Optimised Images #181

Open chrissy-dev opened 1 year ago

chrissy-dev commented 1 year ago

Eleventy Image when optimising images, strips out all metadata (e.g., EXIF, XMP, and IPTC) from the output images. I understand why this is the default approach, however this can be problematic in certain use cases where the metadata is important for the end user, such as photography or journalistic websites.

It would be great to have an option in Eleventy Image that allows users to preserve the metadata in the optimised images. This could be implemented as a configuration option, such as:

let options = {
    widths: [THUMB,FULL],
    formats: ['jpg'],
    preserveMetadata: true,
    outputDir: './src/static',
};

When this option is set to true, the plugin should retain the metadata in the optimised output image. If it's false or not set, the plugin should continue to behave as it does now, removing the metadata to reduce file size.

In my specific use case, I am utilising image files as the primary content for my Eleventy project, where each image serves as an individual entry within a collection. The metadata, including EXIF, XMP, and IPTC information, plays a crucial role in providing context and additional details about each image. Accessing the metadata as part of the collection allows me to render that on the page.

chrissy-dev commented 1 year ago

I've managed to solve this using fast-exif. I'll leave this open, incase a native solution is wanted.

eleventyConfig.addCollection("images", async (collectionApi) => {
    let files = await glob("./src/_raw/**/*.{jpg,jpeg,png,gif}");
    let allImages = [];

    for (const f of files) {
        console.log(`Resizing ${f}...`);

        let optimised = await Image(f, {
            widths: [THUMB, FULL],
            formats: ["jpg"],
            outputDir: "./src/static/",
            urlPath: "/static/",
            filenameFormat: function (id, src, width, format, options) {
                if (width === THUMB) return `thumb-${id}.${format}`;
                else return `${id}.${format}`;
            },
        });

        let metadata = await exif.read(f);

        allImages.push({
            metadata: metadata,
            optimised,
        });
    }
    return allImages;
});
zachleat commented 1 year ago

I think this overlaps with #52 which would allow folks to use https://sharp.pixelplumbing.com/api-output#withmetadata

chrissy-dev commented 1 year ago

I think this overlaps with #52 which would allow folks to use sharp.pixelplumbing.com/api-output#withmetadata

Yep, it does. I might stick in a PR to get the ball rolling on this.

ScreenDream commented 9 months ago

Yes, keeping image metadata like EXIF, XMP, and IPTC and also the ICC colour profile are critical for me as well. I'm a visual artist building my website with eleventy ATM and will be unable to use the image plugin at all if I can't keep metadata and ICC profiles - my images look washed out and the contact information on the images is missing after the processing.

I would be super thankful if somebody could implement the hooks to activate/access the "withMetadata" functionality that is already in sharp: https://sharp.pixelplumbing.com/api-output#withmetadata I am sadly not up to that myself.

Thanks a ton!

Tom

ScreenDream commented 9 months ago

I hacked it for now: In the sharp package, lib folder, constructor.js Line 261, set "withMetadata" to "true". This works for me as default, since I ALLWAYS want metadata to be preserved and ICC profiles kept. My colours are there again :-)

But it would be much better if this was part of the official 11ty plugin...

Cheers,

Tom

pdehaan commented 9 months ago

@ScreenDream, https://www.11ty.dev/docs/plugins/image/#usage has a section beneath the <ul> that says:

"Expand to see the full list of options (defaults shown)"

and has the following defaults:

const Image = require("@11ty/eleventy-img");

    let stats = await Image("…", {
        ...
        // Advanced options passed to sharp
        sharpOptions: {},
        sharpWebpOptions: {},
        sharpPngOptions: {},
        sharpJpegOptions: {},
        sharpAvifOptions: {},
    });

Does it work if you pass withMetadata: true in the sharpOptions object?

ScreenDream commented 9 months ago

@pdehaan Thanks a ton for your reply! That was the first thing I tried, sorry for not mentioning this here (I tried the discord first). Both as a "normal" option for the image plugin like: withMetadata: true, ... (One can always hope, right ;-) ) nor in the form of:

sharpOptions: {
    withMetadata: true,
},
....

This is how I got the other options to work, like the webp settings etc. And as I understand it, the "sharpOptions" in the image plugin only cater to the constructor options of sharp: https://sharp.pixelplumbing.com/api-constructor#sharp But not the output options, which "withMetadata" is a part of - at least that is how I understand the docs for 'Image'. But I would be very happy to be proven wrong - I'm very new to the world of 11ty and node.js so it's totally possible that I have the syntax wrong - got no error message though, it was just ignored. Didn't try Debug though.

I looked at the image plugin code, but was unable to clearly see where this setting would have to go, what would need to be put where, otherwise I'd be happy to implement it myself.

IMO this is absolutely mission critical for everybody where the image is the product - copyright information in the image, contact data and especially the ICC profile to keep the colours true.

Since the implementation in sharp is already there and allows very deep access to everything metadata in images, it would be an awesome feature, to allow people to set it all with code in 11ty. I could probably stop using Affinity Photo for that altogether and do it all - and much cleaner and faster - in 11ty.