This repository has been archived by the owner on Feb 20, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ahdin module that provides Ahdin image compression factory.
- Loading branch information
Showing
16 changed files
with
810 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
bower_components |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,75 @@ | ||
# angular-ahdin | ||
angular-ahdin | ||
================ | ||
Lossy compression module for AngularJS applications. It takes image `File`s or `Blob`s and compresses them to `Blob`s. It also fixes image orientation according to image's EXIF metadata. | ||
|
||
Dependencies | ||
----- | ||
- AngularJS >=1.2.* | ||
- [blob-util](https://github.com/nolanlawson/blob-util) (comes blundled with angular-ahdin) | ||
- [load-image](https://github.com/blueimp/JavaScript-Load-Image) (comes blundled with angular-ahdin) | ||
|
||
Installation | ||
----------- | ||
``` | ||
$ bower install --save angular-ahdin | ||
``` | ||
|
||
Setting up the module | ||
---------- | ||
After installing the package make sure that the module `ahdin` is defined as your app's dependency. | ||
|
||
```html | ||
<script href="bower_components/angular-ahdin/dist/blob-util.min.js"></script> | ||
<script href="bower_components/angular-ahdin/dist/load-image.all.min.js"></script> | ||
<script href="bower_components/angular-ahdin/dist/ahdin.js"></script> | ||
|
||
<script> | ||
angular.module('yourAwesomeApp', ['ahdin']); | ||
<script> | ||
``` | ||
How to use Ahdin | ||
---------- | ||
In order to to compress images, inject `Ahdin` to your module and call `compress()`. Function returns a promise that will be resolved with compressed image `Blob`. | ||
```js | ||
angular | ||
.module('yourAwesomeApp') | ||
.factory('SomeFactory', function(Ahdin) { | ||
function compressFile(file) { | ||
Ahdin.compress({ | ||
sourceFile: file, | ||
maxWidth: 1000, | ||
outputFormat: 'png' | ||
}).then(function(compressedBlob) { | ||
doSomething(compressedBlob); | ||
}); | ||
} | ||
}); | ||
``` | ||
###compress() parameter object | ||
Parameter object that is passed to function `compress()` can have the following properties. | ||
```js | ||
var parameterObj = { | ||
// jpeg or png file that is instance of File or Blob | ||
sourceFile: file, // required | ||
// Maximum width of compressed photo in pixels | ||
maxWidth: 1000, // optional, defaults to original image width | ||
// Maximum height of compressed photo in pixels | ||
maxHeight: 1000, // optional, defaults to original image height | ||
// String defining compressed file mime type. Accepted values: 'jpeg' and 'png' | ||
outputFormat: 'png' // optional, default value 'jpeg' | ||
// Image quality when desired outputFormat is 'jpeg' or undefined. Take values | ||
// over 0 and less or equal to 1. If outputFormat is 'png' this has no effect. | ||
quality: 0.9 // optional, defaults to 0.8 | ||
}; | ||
var compressionPromise = Ahdin.compress(parameterObj); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "angular-ahdin", | ||
"version": "1.0.0", | ||
"homepage": "https://github.com/fastmonkeys/angular-ahdin", | ||
"authors": [ | ||
"Roope Hovi <[email protected]>" | ||
], | ||
"description": "Image file compression service for AngularJS applications", | ||
"main": [ | ||
"dist/libs/load-image.all.min.js", | ||
"dist/libs/blob-util.min.js", | ||
"dist/ahdin.js" | ||
], | ||
"moduleType": [ | ||
"globals" | ||
], | ||
"license": "MIT", | ||
"private": false, | ||
"ignore": [ | ||
"**/.*", | ||
"node_modules", | ||
"bower_components", | ||
"test" | ||
], | ||
"dependencies": { | ||
"angular": ">=1.2.*", | ||
"blob-util": "1.1.1", | ||
"blueimp-load-image": "1.13.0" | ||
}, | ||
"devDependencies": { | ||
"angular-mocks": ">=1.2.*" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
dependencies: | ||
override: | ||
- npm install | ||
- bower install | ||
- rm bower.json | ||
|
||
test: | ||
override: | ||
- rm -rf bower_components/angular && rm -rf bower_components/angular-mocks && bower install angular#1.2.* && bower install angular-mocks#1.2.* && npm test | ||
- rm -rf bower_components/angular && rm -rf bower_components/angular-mocks && bower install angular#1.3.* && bower install angular-mocks#1.3.* && npm test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
(function() { | ||
'use strict'; | ||
angular | ||
.module('ahdin', []); | ||
})(); | ||
|
||
(function() { | ||
'use strict'; | ||
angular | ||
.module('ahdin') | ||
.factory('Ahdin', imageCompressor); | ||
|
||
function imageCompressor($q, $window, $rootScope, loadImage, blobUtil, QUALITY) { | ||
var VALID_FORMATS = ['jpeg', 'png']; | ||
var quality; | ||
|
||
return { | ||
compress: compress | ||
}; | ||
|
||
function compress(params) { | ||
validateParams(params); | ||
quality = params.quality || QUALITY; | ||
function imageMetadataParsing(image) { return parseMetadata(image, params) } | ||
function scalingAndOrientation(args) { return scaleAndFixOrientation(args, params) } | ||
function qualityDecreasedDataUrl(image) { return decreaseImageQuality(image, params) } | ||
|
||
return blobToImage(params) | ||
.then(imageMetadataParsing) | ||
.then(scalingAndOrientation) | ||
.then(qualityDecreasedDataUrl) | ||
.then(dataUrlToBlob); | ||
} | ||
|
||
function blobToImage(params) { | ||
var imageDeferred = $q.defer(); | ||
var image = new Image(); | ||
image.onload = resolveImage; | ||
image.src = blobUtil.createObjectURL(params.sourceFile); | ||
return imageDeferred.promise; | ||
|
||
function resolveImage() { | ||
imageDeferred.resolve(image); | ||
$rootScope.$apply(); | ||
} | ||
} | ||
|
||
function parseMetadata(image, params) { | ||
var deferred = $q.defer(); | ||
loadImage.parseMetaData(params.sourceFile, resolveExtendedMetadata, params); | ||
return deferred.promise; | ||
|
||
function resolveExtendedMetadata(metaData) { | ||
deferred.resolve({metaData: metaData, image: image}); | ||
$rootScope.$apply(); | ||
} | ||
} | ||
|
||
function scaleAndFixOrientation(args, params) { | ||
var deferred = $q.defer(); | ||
var options = parseOptions(args, params); | ||
loadImage(params.sourceFile, resolveDeferred, options); | ||
return deferred.promise; | ||
|
||
function resolveDeferred(image) { | ||
deferred.resolve(image); | ||
$rootScope.$apply(); | ||
} | ||
} | ||
|
||
function parseOptions(args, params) { | ||
var image = args.image; | ||
var metaData = args.metaData; | ||
var maxHeight = (params && params.maxHeight) || image.height; | ||
var maxWidth = (params && params.maxWidth) || image.width; | ||
var options = { | ||
maxWidth: maxWidth, | ||
maxHeight: maxHeight | ||
}; | ||
|
||
if (metaData.exif) { | ||
options.orientation = metaData.exif.get('Orientation'); | ||
} | ||
|
||
return options; | ||
} | ||
|
||
function decreaseImageQuality(image, params) { | ||
var deferred = $q.defer(); | ||
var format = (params && params.outputFormat) || 'jpeg'; | ||
var mimeType = 'image/' + format; | ||
var canvas = $window.document.createElement('canvas'); | ||
canvas.width = image.width; | ||
canvas.height = image.height; | ||
canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height); | ||
var resolveData = { | ||
dataUrl: canvas.toDataURL(mimeType, quality), | ||
fileName: params.sourceFile.name | ||
}; | ||
deferred.resolve(resolveData); | ||
return deferred.promise; | ||
} | ||
|
||
function dataUrlToBlob(args) { | ||
var deferred = $q.defer(); | ||
var compressedBlob = blobUtil.dataURLToBlob(args.dataUrl); | ||
compressedBlob.then(addFileName); | ||
|
||
deferred.resolve(compressedBlob); | ||
$window.setTimeout(function() { | ||
$rootScope.$apply(); | ||
}); | ||
|
||
return deferred.promise; | ||
|
||
function addFileName(blob) { | ||
blob.name = args.fileName; | ||
} | ||
} | ||
|
||
function isPositiveNumber(value) { | ||
return angular.isNumber(value) && value > 0; | ||
} | ||
|
||
function validateParams(params) { | ||
params = params || {}; | ||
validateSourceFile(params.sourceFile); | ||
validateMaxWidth(params.maxWidth); | ||
validateMaxHeight(params.maxHeight); | ||
validateOutputFormat(params.outputFormat); | ||
validateQuality(params.quality); | ||
} | ||
|
||
function validateSourceFile(sourceFile) { | ||
var sourceImageValid = | ||
(sourceFile instanceof $window.File || sourceFile instanceof $window.Blob); | ||
if (!sourceImageValid) { | ||
throw new Error('params.sourceFile must be instance of File'); | ||
} | ||
} | ||
|
||
function validateMaxWidth(maxWidth) { | ||
var isMaxWidthValid = maxWidth === undefined || isPositiveNumber(maxWidth); | ||
if (!isMaxWidthValid) { | ||
throw new Error('params.maxWidth must be a positive Number'); | ||
} | ||
} | ||
|
||
function validateMaxHeight(maxHeight) { | ||
var isMaxHeightValid = maxHeight === undefined || isPositiveNumber(maxHeight); | ||
if (!isMaxHeightValid) { | ||
throw new Error('params.maxHeight must be a positive Number'); | ||
} | ||
} | ||
|
||
function validateOutputFormat(outputFormat) { | ||
var isInValidFormats = VALID_FORMATS.indexOf(outputFormat) > -1; | ||
var outputFormatValid = outputFormat ? isInValidFormats : true; | ||
if (!outputFormatValid) { | ||
throw new Error('params.outputFormat format must be one of [' + VALID_FORMATS + ']'); | ||
} | ||
} | ||
|
||
function validateQuality(quality) { | ||
var isQualityValid = quality === undefined || quality > 0 && quality <= 1; | ||
if (!isQualityValid) { | ||
throw new Error('params.quality must be a Number over 0 and less or equal to 1'); | ||
} | ||
} | ||
} | ||
})(); | ||
|
||
(function() { | ||
'use strict'; | ||
angular | ||
.module('ahdin') | ||
.constant('blobUtil', blobUtil) | ||
.constant('loadImage', loadImage) | ||
.constant('QUALITY', 0.8); | ||
})(); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.