Skip to content

Instantly share code, notes, and snippets.

@Snesi
Last active April 24, 2020 03:16
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Snesi/a20a0fd298c6c32598644353edf2eb7f to your computer and use it in GitHub Desktop.
Save Snesi/a20a0fd298c6c32598644353edf2eb7f to your computer and use it in GitHub Desktop.
Angular 2+ Pipe that auto-rotate images locally in the browser by parsing exif data in pipe inspired by https://gist.github.com/runeb/c11f864cd7ead969a5f0
import { Pipe, PipeTransform } from '@angular/core'
import { SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl, DomSanitizer } from '@angular/platform-browser'
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/observable/bindcallback'
const rotation = {
1: 'rotate(0deg)',
3: 'rotate(180deg)',
6: 'rotate(90deg)',
8: 'rotate(270deg)'
}
const arrayBufferToBase64 = ( buffer ) => {
let binary = ''
const bytes = new Uint8Array( buffer )
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] )
}
return window.btoa( binary )
}
@Pipe({
name: 'rotationCorrection'
})
export class RotationCorrectionPipe implements PipeTransform {
constructor() {}
public transform(image: Blob): Observable<string> {
const orientation = Observable.bindCallback<string>(this.orientation)
return orientation(image)
}
// Exif orientation value to css transform mapping
// Does not include flipped orientations
orientation(file, callback) {
const fileReader = new FileReader()
fileReader.onloadend = function() {
const base64img = 'data:' + file.type + ';base64,' + arrayBufferToBase64(fileReader.result)
const scanner = new DataView(fileReader.result)
let idx = 0
let value = 1 // Non-rotated is the default
if (fileReader.result.length < 2 || scanner.getUint16(idx) !== 0xFFD8) {
// Not a JPEG
if (callback) {
callback(rotation[value])
}
return
}
idx += 2
let maxBytes = scanner.byteLength
while (idx < maxBytes - 2) {
const uint16 = scanner.getUint16(idx)
idx += 2
switch (uint16) {
case 0xFFE1: // Start of EXIF
const exifLength = scanner.getUint16(idx)
maxBytes = exifLength - idx
idx += 2
break
case 0x0112: // Orientation tag
// Read the value, its 6 bytes further out
// See page 102 at the following URL
// http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf
value = scanner.getUint16(idx + 6, false)
maxBytes = 0 // Stop scanning
break
}
}
if (callback) {
callback(rotation[value])
}
}
fileReader.readAsArrayBuffer(file)
}
}
@Snesi
Copy link
Author

Snesi commented Sep 29, 2017

Needs to be used in combination with async pipe like so:

photoBlob | rotationCorrection | async

@kamleshkumarverma1986
Copy link

How to use this pipe in html template .. can i have example of it.
i tried with adding this with image tags photoBlob | rotationCorrection | async.
but did not worked

@choli
Copy link

choli commented May 25, 2018

Thanks for the gist! It helped me a lot.
Anyhow this only returns the rotate(xdeg) command, but the base64img variable is only created initially but never used again.
I am not an expert, but I guess this is either unused or (compared to the plain JS inspiration) not finished :)
Thank you anyways though!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment