JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images

前端 未结 12 1026
花落未央
花落未央 2020-11-22 11:59

Digital camera photos are often saved as JPEG with an EXIF \"orientation\" tag. To display correctly, images need to be rotated/mirrored depending on which orientation is se

12条回答
  •  醉梦人生
    2020-11-22 12:39

    Wunderbart's post worked for me combined with statler's improvements. Adding a few more comments and syntax cleanup, and also passing back the orientation value and I have the following code feel free to use. Just call readImageFile() function below and you get back the transformed image and the original orientation.

    const JpegOrientation = [
        "NOT_JPEG",
        "NORMAL",
        "FLIP-HORIZ",
        "ROT180",
        "FLIP-HORIZ-ROT180",
        "FLIP-HORIZ-ROT270",
        "ROT270",
        "FLIP-HORIZ-ROT90",
        "ROT90"
    ];
    
    
    //Provided a image file, determines the orientation of the file based on the EXIF information.
    //Calls the "callback" function with an index into the JpegOrientation array. 
    //If the image is not a JPEG, returns 0. If  the orientation value cannot be read (corrupted file?) return -1.
    function getOrientation(file, callback) {
        
        const reader = new FileReader();
        reader.onload = (e) => {
    
            const view = new DataView(e.target.result);
            
            if (view.getUint16(0, false) !== 0xFFD8) {
                return callback(0);  //NOT A JPEG FILE
            }
            
            const length = view.byteLength;
            let offset = 2;
            while (offset < length) {
                
                if (view.getUint16(offset+2, false) <= 8)   //unknown?
                    return callback(-1);
                
                const marker = view.getUint16(offset, false);
                offset += 2;
                if (marker === 0xFFE1) {
                    
                    if (view.getUint32(offset += 2, false) !== 0x45786966) 
                        return callback(-1); //unknown?
                    
    
                    const little = view.getUint16(offset += 6, false) === 0x4949;
                    offset += view.getUint32(offset + 4, little);
                    const tags = view.getUint16(offset, little);
                    offset += 2;
                    for (var i = 0; i < tags; i++) {
                        if (view.getUint16(offset + (i * 12), little) === 0x0112) {
                            return callback(view.getUint16(offset + (i * 12) + 8, little));   //found orientation code
                        }
                    }
                }
                else if ((marker & 0xFF00) !== 0xFF00) {
                    break;
                }
                else { 
                    offset += view.getUint16(offset, false);
                }
            }
            
            return callback(-1); //unknown?
        };
        reader.readAsArrayBuffer(file);
    }
    
    //Takes a jpeg image file as base64 and transforms it back to original, providing the
    //transformed image in callback.  If the image is not a jpeg or is already in normal orientation,
    //just calls the callback directly with the source.
    //Set type to the desired output type if transformed, default is image/jpeg for speed.
    function resetOrientation(srcBase64, srcOrientation, callback, type = "image/jpeg") {
        
        if (srcOrientation <= 1) {  //no transform needed
            callback(srcBase64);
            return;
        }
        
        const img = new Image();    
    
        img.onload = () => {
            const width = img.width;
            const height = img.height;
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext("2d");
    
            // set proper canvas dimensions before transform & export
            if (4 < srcOrientation && srcOrientation < 9) {
                canvas.width = height;
                canvas.height = width;
            } else {
                canvas.width = width;
                canvas.height = height;
            }
    
            // transform context before drawing image
            switch (srcOrientation) {
                  
                  //case 1: normal, no transform needed
                  
                  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
            callback(canvas.toDataURL(type), srcOrientation);
        };
    
        img.src = srcBase64;
    };
    
    
    //Read an image file, providing the returned data to callback. If the image is jpeg
    //and is transformed according to EXIF info, transform it first.
    //The callback function receives the image data and the orientation value (index into JpegOrientation)
    export function readImageFile(file, callback) {
    
        getOrientation(file, (orientation) => {
    
            console.log("Read file \"" + file.name + "\" with orientation: " + JpegOrientation[orientation]);
    
            const reader = new FileReader();
            reader.onload = () => {  //when reading complete
    
                const img = reader.result;
                resetOrientation(img, orientation, callback);
            };
            reader.readAsDataURL(file);  //start read
            
        });
    }
    

提交回复
热议问题