├── .npmrc ├── tests ├── gulp.png ├── NodeJSPresentation.pdf └── test.js ├── .travis.yml ├── sample ├── gulpfile.js └── package.json ├── .editorconfig ├── package.json ├── LICENSE ├── index.js ├── readme.md └── .gitignore /.npmrc: -------------------------------------------------------------------------------- 1 | save=true 2 | save-exact=true 3 | -------------------------------------------------------------------------------- /tests/gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelmartineau/gulp-pdf-thumbnail/HEAD/tests/gulp.png -------------------------------------------------------------------------------- /tests/NodeJSPresentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samuelmartineau/gulp-pdf-thumbnail/HEAD/tests/NodeJSPresentation.pdf -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | script: "npm run coveralls" 5 | # Send coverage data to Coveralls 6 | after_script: "cat ./coverage/lcov.info | coveralls.js" 7 | -------------------------------------------------------------------------------- /sample/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const pdfThumbnail = require('gulp-pdf-thumbnail'); 3 | 4 | gulp.task('default', () => { 5 | return gulp.src('../tests/NodeJSPresentation.pdf') 6 | .pipe(pdfThumbnail()) 7 | .pipe(gulp.dest('')); 8 | }); 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gulp.js", 6 | "scripts": { 7 | "test": "gulp default" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "gulp-pdf-thumbnail": "../" 14 | }, 15 | "devDependencies": { 16 | "gulp": "^3.9.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-pdf-thumbnail", 3 | "version": "1.0.0", 4 | "description": "Gulp plugin that generate PNG thumbnail from PDF files.", 5 | "license": "MIT", 6 | "repository": "samuelmartineau/gulp-pdf-thumbnail", 7 | "author": { 8 | "name": "Samuel Martineau", 9 | "url": "samuelmartineau.github.io" 10 | }, 11 | "engines": { 12 | "node": ">=4.0.0" 13 | }, 14 | "scripts": { 15 | "test": "xo && npm run tape", 16 | "tape": "tape tests/**/*.js", 17 | "coveralls": "istanbul cover tape -R spec ./tests/**/*.js" 18 | }, 19 | "files": [ 20 | "index.js" 21 | ], 22 | "keywords": [ 23 | "gulpplugin", 24 | "thumbnail", 25 | "pdf", 26 | "gm" 27 | ], 28 | "dependencies": { 29 | "gm": "1.23.0", 30 | "gulp-util": "3.0.7", 31 | "replace-ext": "1.0.0", 32 | "through2": "2.0.1" 33 | }, 34 | "devDependencies": { 35 | "coveralls": "2.11.14", 36 | "istanbul": "0.4.5", 37 | "nock-exec": "0.1.0", 38 | "tape": "4.6.0", 39 | "vinyl": "1.2.0", 40 | "vinyl-file": "2.0.0", 41 | "xo": "0.16.0" 42 | }, 43 | "xo": { 44 | "esnext": true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Samuel Martineau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const exec = require('child_process').exec; 3 | const path = require('path'); 4 | const gutil = require('gulp-util'); 5 | const through = require('through2'); 6 | const gm = require('gm').subClass({ 7 | imageMagick: true 8 | }); 9 | const replaceExt = require('replace-ext'); 10 | 11 | function handleObjectStream(file, enc, cb) { 12 | if (file.isNull()) { 13 | gutil.log('Be carefull, you passed a file without contents'); 14 | cb(null, file); 15 | return; 16 | } 17 | 18 | if (file.isStream()) { 19 | cb(new gutil.PluginError('gulp-pdf-thumbnail-generator', 'Streaming not supported')); 20 | return; 21 | } 22 | 23 | if (path.extname(file.path) !== '.pdf') { 24 | cb(new gutil.PluginError('gulp-pdf-thumbnail-generator', path.extname(file.path) + ' extention is not supported')); 25 | return; 26 | } 27 | 28 | const that = this; 29 | // select the first page 30 | gm(file.path + '[0]') 31 | .out('+adjoin') 32 | .trim() 33 | .toBuffer('PNG', (err, buffer) => { 34 | if (err) { 35 | cb(new gutil.PluginError('gulp-pdf-thumbnail-generator', err)); 36 | return; 37 | } 38 | 39 | file.contents = buffer; 40 | file.path = replaceExt(file.path, '.png'); 41 | that.push(file); 42 | cb(); 43 | }); 44 | } 45 | 46 | module.exports = () => { 47 | // Check if imageMagick is installed 48 | exec('convert -version', (error, stdout) => { 49 | if (error || !stdout || stdout.toLowerCase().indexOf('imagemagick') === -1) { 50 | new gutil.PluginError('gulp-pdf-thumbnail-generator', 'ImageMagick not installed'); // eslint-disable-line no-new 51 | } 52 | }); 53 | 54 | return through.obj(handleObjectStream); 55 | }; 56 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # gulp-pdf-thumbnail 2 | 3 | [![NPM version](https://img.shields.io/npm/v/gulp-pdf-thumbnail.svg?style=flat)](https://www.npmjs.com/package/gulp-pdf-thumbnail) 4 | [![Build Status](https://travis-ci.org/samuelmartineau/gulp-pdf-thumbnail.svg?branch=master)](https://travis-ci.org/samuelmartineau/gulp-pdf-thumbnail) 5 | [![Coverage Status](https://coveralls.io/repos/samuelmartineau/gulp-pdf-thumbnail/badge.svg?branch=master&service=github)](https://coveralls.io/github/samuelmartineau/gulp-pdf-thumbnail?branch=master) 6 | [![Dependency Status](https://david-dm.org/samuelmartineau/gulp-pdf-thumbnail.svg)](https://david-dm.org/samuelmartineau/gulp-pdf-thumbnail) 7 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 8 | 9 | [![NPM](https://nodei.co/npm/gulp-pdf-thumbnail.png?downloads=true&stars=true)](https://nodei.co/npm/gulp-pdf-thumbnail/) 10 | 11 | ## What this? 12 | 13 | > Gulp plugin that generate PNG thumbnail from PDF files. 14 | 15 | ## Getting started 16 | 17 | First download and install GraphicsMagick or ImageMagick. 18 | 19 | [imagemagick!] (http://www.imagemagick.org) 20 | [graphicsmagick!] (http://www.graphicsmagick.org/) 21 | [ghostscript!] (http://www.ghostscript.com/) 22 | 23 | ## Install 24 | 25 | ``` 26 | $ npm install --save-dev gulp-pdf-thumbnail 27 | ``` 28 | 29 | ## Usage 30 | 31 | ```js 32 | var gulp = require('gulp'); 33 | var pdfThumbnail = require('gulp-pdf-thumbnail'); 34 | 35 | gulp.task('default', function () { 36 | return gulp.src('src/file.pdf') 37 | .pipe(pdfThumbnail()) 38 | .pipe(gulp.dest('dist')); 39 | }); 40 | ``` 41 | 42 | ## License 43 | 44 | MIT © [Samuel Martineau](http://samuelmartineau.github.io) 45 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | const Stream = require('stream'); 2 | const path = require('path'); 3 | const test = require('tape'); 4 | const nockExec = require('nock-exec'); 5 | const File = require('vinyl'); 6 | const read = require('vinyl-file').read; 7 | const pdfThumbnail = require('../'); 8 | 9 | test('Plugin test', t => { 10 | t.plan(5); 11 | 12 | const stream = pdfThumbnail(); 13 | const dummy = new File(); 14 | 15 | stream.on('data', file => { 16 | t.true(file === dummy, 'should do nothing with the file if it hasn\'t contents'); 17 | }); 18 | stream.write(dummy); 19 | stream.end(); 20 | 21 | const stream2 = pdfThumbnail(); 22 | const dummy2 = new File(); 23 | dummy2.contents = new Stream(); 24 | stream2.on('error', () => { 25 | t.pass('should fail if contents is a Stream'); 26 | }); 27 | stream2.write(dummy2); 28 | stream2.end(); 29 | 30 | read(path.join(__dirname, 'NodeJSPresentation.pdf')) 31 | .then(file => { 32 | const stream = pdfThumbnail(); 33 | 34 | stream.on('data', file => { 35 | t.true(path.extname(file.path) === '.png', 'should create a png file from a pdf'); 36 | }); 37 | stream.end(file); 38 | }); 39 | 40 | read(path.join(__dirname, 'gulp.png')) 41 | .then(file => { 42 | const stream = pdfThumbnail(); 43 | 44 | stream.on('error', err => { 45 | t.pass('should fail if the input file is not a pdf', err); 46 | }); 47 | stream.end(file); 48 | }); 49 | 50 | const stream3 = pdfThumbnail(); 51 | nockExec('convert -version').err(' zsh: command not found: convert').reply(0, 'zsh: command not found: convert'); 52 | try { 53 | throw stream3.on(); 54 | } catch (err) { 55 | t.pass('should fail imagemagick is not installed'); 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/intellij,webstorm,osx,linux,node 2 | 3 | ### Intellij ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 5 | 6 | *.iml 7 | 8 | ## Directory-based project format: 9 | .idea/ 10 | # if you remove the above rule, at least ignore the following: 11 | 12 | # User-specific stuff: 13 | # .idea/workspace.xml 14 | # .idea/tasks.xml 15 | # .idea/dictionaries 16 | 17 | # Sensitive or high-churn files: 18 | # .idea/dataSources.ids 19 | # .idea/dataSources.xml 20 | # .idea/sqlDataSources.xml 21 | # .idea/dynamic.xml 22 | # .idea/uiDesigner.xml 23 | 24 | # Gradle: 25 | # .idea/gradle.xml 26 | # .idea/libraries 27 | 28 | # Mongo Explorer plugin: 29 | # .idea/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.ipr 33 | *.iws 34 | 35 | ## Plugin-specific files: 36 | 37 | # IntelliJ 38 | /out/ 39 | 40 | # mpeltonen/sbt-idea plugin 41 | .idea_modules/ 42 | 43 | # JIRA plugin 44 | atlassian-ide-plugin.xml 45 | 46 | # Crashlytics plugin (for Android Studio and IntelliJ) 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | 51 | 52 | ### WebStorm ### 53 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 54 | 55 | *.iml 56 | 57 | ## Directory-based project format: 58 | .idea/ 59 | # if you remove the above rule, at least ignore the following: 60 | 61 | # User-specific stuff: 62 | # .idea/workspace.xml 63 | # .idea/tasks.xml 64 | # .idea/dictionaries 65 | 66 | # Sensitive or high-churn files: 67 | # .idea/dataSources.ids 68 | # .idea/dataSources.xml 69 | # .idea/sqlDataSources.xml 70 | # .idea/dynamic.xml 71 | # .idea/uiDesigner.xml 72 | 73 | # Gradle: 74 | # .idea/gradle.xml 75 | # .idea/libraries 76 | 77 | # Mongo Explorer plugin: 78 | # .idea/mongoSettings.xml 79 | 80 | ## File-based project format: 81 | *.ipr 82 | *.iws 83 | 84 | ## Plugin-specific files: 85 | 86 | # IntelliJ 87 | /out/ 88 | 89 | # mpeltonen/sbt-idea plugin 90 | .idea_modules/ 91 | 92 | # JIRA plugin 93 | atlassian-ide-plugin.xml 94 | 95 | # Crashlytics plugin (for Android Studio and IntelliJ) 96 | com_crashlytics_export_strings.xml 97 | crashlytics.properties 98 | crashlytics-build.properties 99 | 100 | 101 | ### OSX ### 102 | .DS_Store 103 | .AppleDouble 104 | .LSOverride 105 | 106 | # Icon must end with two \r 107 | Icon 108 | 109 | 110 | # Thumbnails 111 | ._* 112 | 113 | # Files that might appear in the root of a volume 114 | .DocumentRevisions-V100 115 | .fseventsd 116 | .Spotlight-V100 117 | .TemporaryItems 118 | .Trashes 119 | .VolumeIcon.icns 120 | 121 | # Directories potentially created on remote AFP share 122 | .AppleDB 123 | .AppleDesktop 124 | Network Trash Folder 125 | Temporary Items 126 | .apdisk 127 | 128 | 129 | ### Linux ### 130 | *~ 131 | 132 | # KDE directory preferences 133 | .directory 134 | 135 | # Linux trash folder which might appear on any partition or disk 136 | .Trash-* 137 | 138 | 139 | ### Node ### 140 | # Logs 141 | logs 142 | *.log 143 | npm-debug.log* 144 | 145 | # Runtime data 146 | pids 147 | *.pid 148 | *.seed 149 | 150 | # Directory for instrumented libs generated by jscoverage/JSCover 151 | lib-cov 152 | 153 | # Coverage directory used by tools like istanbul 154 | coverage 155 | 156 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 157 | .grunt 158 | 159 | # node-waf configuration 160 | .lock-wscript 161 | 162 | # Compiled binary addons (http://nodejs.org/api/addons.html) 163 | build/Release 164 | 165 | # Dependency directory 166 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 167 | node_modules 168 | NodeJSPresentation.png 169 | --------------------------------------------------------------------------------