├── .gitignore ├── .npmignore ├── MIT-LICENSE.txt ├── README.md ├── bin └── optimage ├── index.js ├── package.json └── test ├── Modfile ├── test.gif ├── test.jpg └── test.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Include your project-specific ignores in this file 2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 3 | 4 | # Numerous always-ignore extensions 5 | *.diff 6 | *.err 7 | *.orig 8 | *.log 9 | *.rej 10 | *.swo 11 | *.swp 12 | *.vi 13 | *~ 14 | *.sass-cache 15 | 16 | # OS or Editor folders 17 | .DS_Store 18 | ._* 19 | Thumbs.db 20 | .cache 21 | .project 22 | .settings 23 | .tmproj 24 | nbproject 25 | *.sublime-project 26 | *.sublime-workspace 27 | 28 | # Dreamweaver added files 29 | _notes 30 | dwsync.xml 31 | 32 | # Komodo 33 | *.komodoproject 34 | .komodotools 35 | 36 | # Espresso 37 | *.esproj 38 | *.espressostorage 39 | 40 | # Rubinius 41 | *.rbc 42 | 43 | # Folders to ignore 44 | .hg 45 | .svn 46 | .CVS 47 | intermediate 48 | publish 49 | .idea 50 | node_modules 51 | 52 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | doc/ 2 | test/ 3 | example/ 4 | node_modules/ 5 | .* 6 | *~ -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 yuanyan 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-optimage 2 | === 3 | 4 | Image optimizer, PNG, JPEG and GIF image compress on OS X, Linux, FreeBSD and Windows. 5 | 6 | ## Install 7 | 8 | Install with NPM: `npm install --save optimage` 9 | 10 | ## Example usage 11 | 12 | ```js 13 | var optimage = require('optimage'); 14 | 15 | optimage({ 16 | inputFile: "test.png", 17 | outputFile: "test.min.png" 18 | }, function(err, res){ 19 | // res.inputFile 20 | // res.outputFile 21 | // res.saved 22 | }); 23 | ``` 24 | 25 | ## License 26 | 27 | MIT. 28 | -------------------------------------------------------------------------------- /bin/optimage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | var optimage = require('../'); 5 | var argv = process.argv; 6 | 7 | var options = { 8 | inputFile: argv[2], 9 | outputFile: argv[3] 10 | }; 11 | 12 | optimage(options, function(err, res){ 13 | var saved = res.saved; 14 | var stderr = res.stderr; 15 | 16 | if (stderr.indexOf('already optimized') !== -1 || saved < 10) { 17 | console.log(options.inputFile, "(already optimized)", ">", options.outputFile); 18 | }else{ 19 | console.log(options.inputFile, "(saved "+ saved+ "Bytes)", ">", options.outputFile); 20 | } 21 | }) -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var execFile = require('child_process').execFile; 4 | 5 | var optimage = module.exports = exports = function (options, done){ 6 | 7 | var inputFile = options.inputFile; 8 | var outputFile = options.outputFile; 9 | var verbose = options.verbose; 10 | var level = options.level; 11 | var progressive = options.progressive; 12 | var binPath= ''; 13 | var args= []; 14 | 15 | outputFile = outputFile || inputFile; 16 | 17 | switch ( path.extname(inputFile) ){ 18 | 19 | // 1. Basic optimisation 20 | // optipng xx.png -out xx2.png 21 | // optipng xx.png -dir ./img 22 | // default -o2 23 | 24 | // TODO 25 | // 2. Removing unnecessary chunks 26 | // pngcrush -q -rem gAMA -rem alla -rem text image.png image.crushed.png 27 | // 3. Reducing the colour palette 28 | // pngnq -f -n 32 -s 3 image.png 29 | // 4. Re-compressing final image 30 | // advpng -z -4 image.png 31 | case '.png': 32 | binPath = require('optipng-bin').path; 33 | // OptiPNG can't overwrite without creating a backup file 34 | // https://sourceforge.net/tracker/?func=detail&aid=3607244&group_id=151404&atid=780913 35 | if (path.resolve(outputFile) !== path.resolve(inputFile) && fs.existsSync(outputFile)) { 36 | fs.unlinkSync(outputFile); 37 | } 38 | if(options.arg) args = args.concat(options.arg); 39 | args.push('-strip', 'all', inputFile, "-out", outputFile, '-o', level||2 ); 40 | break; 41 | 42 | // jpegtran [switches] inputfile outputfile 43 | case '.jpg': 44 | case '.jpeg': 45 | binPath = require('jpegtran-bin').path; 46 | args.push('-copy', 'none', '-optimize'); 47 | if(progressive) args.push('-progressive'); 48 | if(options.arg) args = args.concat(options.arg); 49 | args.push('-outfile', outputFile, inputFile) 50 | break; 51 | 52 | // maybe support to do a merge with frames? 53 | // that means we need an array for inputOutput. 54 | case '.gif': 55 | binPath = require('gifsicle').path; 56 | args.push('-o', outputFile, inputFile, '-O', level||2 ); 57 | if (options.arg) arg = args.concat(options.arg); 58 | break; 59 | 60 | default: 61 | // Invoke the callback with a fail message 62 | done(new Error('Unable to determine file format'), null); 63 | return; 64 | } 65 | 66 | var originalSize = fs.statSync(inputFile).size; 67 | 68 | execFile(binPath, args, function(err, stdout, stderr) { 69 | 70 | if (verbose) { 71 | stdout && console.log(stdout); 72 | stderr && console.log(stderr); 73 | } 74 | 75 | options.stdout = stdout; 76 | options.stderr = stderr; 77 | 78 | if(!err){ 79 | options.saved = originalSize - fs.statSync(outputFile).size; 80 | } 81 | 82 | done(err, options); 83 | }); 84 | 85 | }; 86 | 87 | // Task.JS API 88 | exports.run = function (options, done) { 89 | 90 | var dest = options.dest; 91 | var file = exports.file; 92 | 93 | exports.async.forEach(exports.files, function(inputFile, cb){ 94 | var outputFile = dest; 95 | // change to file copy to file 96 | if(file.isFile(inputFile) && file.isDirname(dest)){ 97 | var filename = path.basename(inputFile); 98 | outputFile = path.join(dest, filename); 99 | } 100 | options.inputFile = inputFile; 101 | options.outputFile = outputFile; 102 | optimage(options, cb); 103 | 104 | }, done); 105 | }; 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "optimage", 3 | "description": "Image optimizer", 4 | "version": "0.2.0", 5 | "homepage": "https://github.com/yuanyan/node-optimage", 6 | "author": { 7 | "name": "yuanyan", 8 | "url": "https://github.com/yuanyan" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/yuanyan/node-optimage.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/yuanyan/node-optimage/issues" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "https://github.com/yuanyan/node-optimage/blob/master/MIT-LICENSE.txt" 21 | } 22 | ], 23 | "bin": "bin/optimage", 24 | "engines": { 25 | "node": ">= 0.6.0" 26 | }, 27 | "dependencies": { 28 | "jpegtran-bin": "~0.2.1", 29 | "optipng-bin": "~0.3.1", 30 | "gifsicle": "~0.1.4" 31 | }, 32 | "devDependencies": { 33 | "modjs": "*" 34 | }, 35 | "keywords": ["png", "jpg", "jpeg", "gif", "image"] 36 | } 37 | -------------------------------------------------------------------------------- /test/Modfile: -------------------------------------------------------------------------------- 1 | // A sample Modfile 2 | // More info at https://github.com/modulejs/modjs/ 3 | 4 | module.exports = { 5 | plugins: { 6 | "optimage": "../index.js" 7 | }, 8 | tasks: { 9 | "optimage": { 10 | jpg:{ 11 | src: "./test.jpg", 12 | dest: "./test.min.jpg" 13 | }, 14 | png: { 15 | src: "./test.png", 16 | dest: "./test.min.png" 17 | }, 18 | gif: { 19 | src: “./test.gif”, 20 | dest: “./test.min.gif” 21 | } 22 | } 23 | }, 24 | targets: { 25 | default: "optimage" 26 | } 27 | }; -------------------------------------------------------------------------------- /test/test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanyan/node-optimage/f3bde223f42edd1c0c9dca27825a59c3b5d0a80d/test/test.gif -------------------------------------------------------------------------------- /test/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanyan/node-optimage/f3bde223f42edd1c0c9dca27825a59c3b5d0a80d/test/test.jpg -------------------------------------------------------------------------------- /test/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanyan/node-optimage/f3bde223f42edd1c0c9dca27825a59c3b5d0a80d/test/test.png --------------------------------------------------------------------------------