brianium / watermarkjs

:rice_scene: Watermarking for the browser
http://brianium.github.io/watermarkjs/
Other
1.73k stars 232 forks source link

Return watermarked image as JPEG #42

Open davidquon opened 6 years ago

davidquon commented 6 years ago

Kind of related to https://github.com/brianium/watermarkjs/issues/34, the watermarked image is returned as a PNG image object even though the original object was a JPEG. Is there a way to return the watermarked image as a JPEG so EXIF data can be written to it?

skliffmueller commented 6 years ago

I am having similar issues, as when jpegs get converted to png's, the compression is horrible.

https://github.com/brianium/watermarkjs/blob/bb3d021a0ce3eaf5d353daa03de46196e2695b44/lib/canvas/index.js#L8

That function should be designed to accept the following: use the image source type when all images are the same accept a user defined parameter to select image type accept a user defined quality parameter default to png if all else fails.

I will be working on a fork in the next few days.

API for reference https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL

Edit: This may not help the exif data issue, I do not think canvas retains that kind of information. But how I got around the exif issue is I just rotated the canvas accordingly based off exif-js library

Here is a snippet of code for reference:

dataURLtoFile(dataurl, filename) {
        var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
        while(n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new File([u8arr], filename, {type:mime});
    }

    resetOrientation(fileData, fileName, orientation) {
        return new Promise((resolve, reject) => {
            var img = new Image();

            img.onload = () => {
                var width = img.width,
                    height = img.height,
                    canvas = document.createElement('canvas'),
                    ctx = canvas.getContext("2d");

                // set proper canvas dimensions before transform & export
                if (4 < orientation && orientation < 9) {
                    canvas.width = height;
                    canvas.height = width;
                } else {
                    canvas.width = width;
                    canvas.height = height;
                }

                // transform context before drawing image
                switch (orientation) {
                    case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
                    case 3: ctx.transform(-1, 0, 0, -1, width, height ); break;
                    case 4: ctx.transform(1, 0, 0, -1, 0, height ); break;
                    case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
                    case 6: ctx.transform(0, 1, -1, 0, height , 0); break;
                    case 7: ctx.transform(0, -1, -1, 0, height , width); break;
                    case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
                    default: break;
                }

                // draw image
                ctx.drawImage(img, 0, 0);

                // export base64
                let newFile = this.dataURLtoFile(canvas.toDataURL(), fileName);
                resolve(newFile);
            };

            img.src = URL.createObjectURL(fileData);
        })

    }
    rotateImages(file) {
        var fr = new FileReader();
        fr.onload = () => {
            let tags = EXIF.readFromBinaryFile(fr.result);
            this.resetOrientation(file.data, file.name, tags['Orientation']).then(newFile => {

            })
        };
        fr.readAsArrayBuffer(file.data);
    }
davidquon commented 6 years ago

That function should be designed to accept the following: use the image source type when all images are the same accept a user defined parameter to select image type accept a user defined quality parameter default to png if all else fails.

Makes sense to me. 👍 @brianium - I'm not clear on how the options configuration works for watermark. Is there any documentation what configurations are accepted?

Edit: This may not help the exif data issue, I do not think canvas retains that kind of information. But how I got around the exif issue is I just rotated the canvas accordingly based off exif-js library

Once converted to a canvas the exif data will be lost unless saved and written back to the image file (if possible depending on the image type). Even if I save the exif data prior to watermarking I cannot write it back to the image file since a PNG is currently always being returned.

davidquon commented 6 years ago

By the way thanks @skliffmueller as this was the line of code I was looking for and was alluding me. 🍻 https://github.com/brianium/watermarkjs/blob/bb3d021a0ce3eaf5d353daa03de46196e2695b44/lib/canvas/index.js#L8

brianium commented 6 years ago

@davidquon the options parameter does not do much right now. Maybe I could do a better job documenting this, but the relevant documentation is here. For now the only property used is init which is a function that receives an Image before it loads. The relevant code is here.

My thought on using an options object was that it could grow to support other things if necessary :)

Let me know if I can answer anything else :)