-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from AtiX/feature/parseImages
Image Parsing
- Loading branch information
Showing
10 changed files
with
216 additions
and
3 deletions.
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
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
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
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
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,120 @@ | ||
Parser = require './Parser' | ||
marked = require 'marked' | ||
fs = require 'fs' | ||
path = require 'path' | ||
|
||
ExifImage = require('exif').ExifImage | ||
gm = require 'gm' | ||
|
||
## | ||
# Parses images and tries to load metadata information from them. | ||
# Adds helper method to access the image content directly | ||
# Constructor options and defaults: | ||
# createThumbnails: false | ||
# thumbnailSize: 400 | ||
# thumbnailQuality: 70 | ||
module.exports = class ImageParser extends Parser | ||
|
||
## | ||
# As for now, only parse jpg images | ||
doesParse: (filename) -> | ||
if not Parser.hasExtension filename, ['jpg'] | ||
return false | ||
|
||
# Don't re-parse auto generated thumbnails | ||
if filename.indexOf('.thumbnail.jpg', filename.length - 14) != -1 | ||
return false | ||
|
||
return true | ||
|
||
## | ||
# Parses the image (exif information) and adds functions to actually load the file content | ||
parse: (filename, dirNode) => | ||
return new Promise (resolve, reject) => | ||
# build up the entry | ||
imageEntry = {} | ||
|
||
# Determine key name | ||
imageEntry.name = path.basename filename, '.jpg' | ||
imageEntry.fileName = filename | ||
|
||
# Load exif information | ||
p = @loadExifData(filename) | ||
p = p.then (exifData) -> | ||
imageEntry.exifData = exifData | ||
|
||
# If desired by the user, create a thumbnail | ||
if @options.createThumbnails | ||
p = p.then => | ||
@createThumbnail filename, imageEntry | ||
|
||
# Attach functions to return the image content | ||
imageEntry.getImageData = () => return @returnImageData(filename) | ||
|
||
# Attach to node | ||
if not dirNode.hasProperty 'images' | ||
dirNode.setProperty 'images', {} | ||
|
||
images = dirNode.getProperty 'images' | ||
images[imageEntry.name] = imageEntry | ||
|
||
resolve(p) | ||
|
||
loadExifData: (filename) -> | ||
return new Promise (resolve, reject) -> | ||
try | ||
new ExifImage {image: filename}, (error, exifData) -> | ||
if error != false | ||
reject(error) | ||
return | ||
resolve(exifData) | ||
catch error | ||
reject(error) | ||
return | ||
|
||
createThumbnail: (filename, imageEntry) => | ||
return new Promise (resolve, reject) => | ||
thumbnailSize = @options.thumbnailSize or 300 | ||
thumbnailQuality = @options.thumbnailQuality or 70 | ||
|
||
thumbnailFilename = filename + '.thumbnail.jpg' | ||
|
||
gm(filename) | ||
.resize(thumbnailSize, thumbnailSize) | ||
.noProfile() | ||
.quality(thumbnailQuality) | ||
.compress('JPEG') | ||
.write thumbnailFilename, (err) => | ||
if err? | ||
reject(err) | ||
return | ||
|
||
imageEntry.thumbnail = { | ||
filename: thumbnailFilename | ||
getImageData: () => return @returnImageData(thumbnailFilename) | ||
} | ||
resolve() | ||
|
||
## | ||
# Load and cache image data | ||
# returns a promise | ||
returnImageData: (imageName) => | ||
@imageDataCache ?= {} | ||
|
||
if @imageDataCache[imageName]? | ||
return Promise.resolve(@imageDataCache[imageName]) | ||
|
||
return new Promise (resolve, reject) => | ||
fs.readFile imageName, (error, data) => | ||
if error? | ||
reject(error) | ||
return | ||
|
||
@imageDataCache[imageName] = data | ||
resolve(data) | ||
|
||
|
||
|
||
|
||
|
||
|
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
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,79 @@ | ||
chai = require 'chai' | ||
expect = chai.expect | ||
path = require 'path' | ||
fs = require 'fs' | ||
|
||
testUtilities = require './testUtilities' | ||
|
||
ImageParser = require '../src/parser/ImageParser.coffee' | ||
TreeNode = require '../src/TreeNode.coffee' | ||
|
||
testPath = undefined | ||
|
||
describe 'ImageParser', -> | ||
# Create an empty directory to test in | ||
beforeEach (done) -> | ||
testUtilities.createTempDirectory (dirName) -> | ||
testPath = dirName | ||
done() | ||
|
||
# Delete temporary directory and all its contents | ||
afterEach (done) -> | ||
testUtilities.deleteTempDirectory(testPath, done) | ||
|
||
it 'should only parse .jpg files', -> | ||
parser = new ImageParser() | ||
|
||
expect(parser.doesParse('myFile.png')).to.be.false | ||
expect(parser.doesParse('myFile.jpg')).to.be.true | ||
|
||
it 'should attach image metadata and helper functions', (done) -> | ||
# Copy test image | ||
fs.createReadStream('./test/ImageParserTest.jpg').pipe(fs.createWriteStream(path.join(testPath ,'testImage.jpg'))); | ||
|
||
node = new TreeNode() | ||
parser = new ImageParser() | ||
|
||
parser.parse(path.join(testPath, 'testImage.jpg'), node) | ||
.then -> | ||
# Expect an image property with the testImage key and exif data | ||
expect(node.hasProperty('images')).to.be.true | ||
imageProperty = node.getProperty 'images' | ||
|
||
image = imageProperty.testImage | ||
expect(image).not.to.be.null | ||
expect(image.exifData).not.to.be.null | ||
|
||
# Expect Actual data when requesting it | ||
image.getImageData() | ||
.then (imageData) -> | ||
expect(imageData.length).to.eql(9371) | ||
done() | ||
.catch (error) -> done (error) | ||
.catch (error) -> done(error) | ||
|
||
it 'should generate a thumbnail if configured to do so', (done) -> | ||
# Copy test image | ||
fs.createReadStream('./test/ImageParserTest.jpg').pipe(fs.createWriteStream(path.join(testPath ,'testImage.jpg'))); | ||
|
||
node = new TreeNode() | ||
parser = new ImageParser({createThumbnails: true}) | ||
|
||
parser.parse(path.join(testPath, 'testImage.jpg'), node) | ||
.then -> | ||
# Expect a thumbnail sub-property per image | ||
imageProperty = node.getProperty 'images' | ||
image = imageProperty.testImage | ||
|
||
expect(image.thumbnail).not.to.be.null | ||
expect(image.thumbnail.filename).not.to.be.null | ||
|
||
# Expect actual data when requesting thumbnail | ||
# (Since the exact image size might differ depending on the gm installation, use a range to test) | ||
image.thumbnail.getImageData() | ||
.then (imageData) -> | ||
expect(imageData.length > 1000 and imageData.length < 3000).to.be.true | ||
done() | ||
.catch (error) -> done (error) | ||
.catch (error) -> done(error) | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.