diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..06f64d2 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,180 @@ +{ + "parserOptions": { + "ecmaVersion": 8, + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + }, + "sourceType": "module" + }, + + "env": { + "es6": true, + "node": true + }, + + "plugins": [ + ], + + "globals": { + "document": false, + "navigator": false, + "window": false, + "app": false, + "WebSocket": false, + "alert": false, + "d3": false, + "angular": false, + "CodeMirror": true, + "saveAs": true, + "Blob": true, + "marked": true, + "THREE": true + }, + + "rules": { + "accessor-pairs": "error", + "arrow-spacing": ["error", { "before": true, "after": true }], + "block-spacing": ["error", "always"], + "brace-style": ["error", "1tbs", { "allowSingleLine": true }], + "camelcase": ["error", { "properties": "never" }], + "comma-dangle": ["error", { + "arrays": "never", + "objects": "never", + "imports": "never", + "exports": "never", + "functions": "never" + }], + "comma-spacing": ["error", { "before": false, "after": true }], + "comma-style": ["error", "last"], + "constructor-super": "error", + "curly": ["error", "multi-line"], + "dot-location": ["error", "property"], + "eol-last": "error", + "eqeqeq": ["error", "always", { "null": "ignore" }], + "func-call-spacing": ["error", "never"], + "generator-star-spacing": ["error", { "before": true, "after": true }], + "handle-callback-err": ["error", "^(err|error)$" ], + "indent": ["error", 3, { "SwitchCase": 1 }], + "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], + "keyword-spacing": ["error", { "before": true, "after": true }], + "new-cap": ["error", { "newIsCap": false, "properties": false}], + "new-parens": "error", + "no-array-constructor": "error", + "no-caller": "error", + "no-class-assign": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": "error", + "no-const-assign": "error", + "no-constant-condition": ["error", { "checkLoops": false }], + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-args": "error", + "no-dupe-class-members": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-eval": "error", + "no-ex-assign": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-boolean-cast": "error", + "no-extra-parens": ["error", "functions"], + "no-fallthrough": "error", + "no-floating-decimal": "error", + "no-func-assign": "error", + "no-global-assign": "error", + "no-implied-eval": "error", + "no-inner-declarations": ["error", "functions"], + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-iterator": "error", + "no-label-var": "error", + "no-labels": ["error", { "allowLoop": false, "allowSwitch": false }], + "no-lone-blocks": "error", + "no-mixed-operators": ["error", { + "groups": [ + ["==", "!=", "===", "!==", ">", ">=", "<", "<="], + ["&&", "||"], + ["in", "instanceof"] + ], + "allowSamePrecedence": true + }], + "no-mixed-spaces-and-tabs": "error", + "no-multi-spaces": "error", + "no-multi-str": "error", + "no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 0 }], + "no-negated-in-lhs": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-object": "error", + "no-new-require": "error", + "no-new-symbol": "error", + "no-new-wrappers": "error", + "no-obj-calls": "error", + "no-octal": "error", + "no-octal-escape": "error", + "no-path-concat": "error", + "no-proto": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-return-assign": ["error", "except-parens"], + "no-return-await": "error", + "no-self-assign": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-tabs": "error", + "no-template-curly-in-string": "error", + "no-this-before-super": "error", + "no-throw-literal": "error", + "no-trailing-spaces": "error", + "no-undef": "error", + "no-undef-init": "error", + "no-unexpected-multiline": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": ["error", { "defaultAssignment": false }], + "no-unreachable": "error", + "no-unsafe-finally": "error", + "no-unsafe-negation": "error", + "no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }], + "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }], + "no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }], + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-constructor": "error", + "no-useless-escape": "error", + "no-useless-rename": "error", + "no-useless-return": "error", + "no-whitespace-before-property": "error", + "no-with": "error", + "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], + "operator-linebreak": ["error", "after", { "overrides": { "?": "before", ":": "before" } }], + "prefer-promise-reject-errors": "error", + "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }], + "rest-spread-spacing": ["error", "never"], + "semi": ["error", "always"], + "semi-spacing": ["error", { "before": false, "after": true }], + "space-before-blocks": ["error", "always"], + "space-before-function-paren": ["error", "always"], + "space-in-parens": ["error", "never"], + "space-infix-ops": "error", + "space-unary-ops": ["error", { "words": true, "nonwords": false }], + "spaced-comment": ["error", "always", { + "line": { "markers": ["*package", "!", "/", ",", "="] }, + "block": { "balanced": true, "markers": ["*package", "!", ",", ":", "::", "flow-include"], "exceptions": ["*"] } + }], + "symbol-description": "error", + "template-curly-spacing": ["error", "never"], + "template-tag-spacing": ["error", "never"], + "unicode-bom": ["error", "never"], + "use-isnan": "error", + "valid-typeof": ["error", { "requireStringLiterals": true }], + "wrap-iife": ["error", "any", { "functionPrototypeMethods": true }], + "yield-star-spacing": ["error", "both"], + "yoda": ["error", "never"], + } +} diff --git a/example.js b/example.js index f15c2b2..de3c174 100644 --- a/example.js +++ b/example.js @@ -1,22 +1,22 @@ var url2pdf = require('./index'); var q = require('q'); -console.log("Running example"); +console.log('Running example'); q.all([ - url2pdf.renderPdf("http://www.google.com", {paperSize: {orientation: "landscape"}}), - url2pdf.renderPdf("http://www.google.com"), + url2pdf.renderPdf('http://www.google.com', {paperSize: {orientation: 'landscape'}}), + url2pdf.renderPdf('http://www.google.com'), - url2pdf.renderFromHTML("

Not working due to needed loading time - no image might be present

"), - url2pdf.renderFromHTML("

Page loaded with timeout. You should see a big image

", {loadTimeout: 2000}) - ]) - .then(function (paths) { - console.log("Created PDFs @", paths); - // manual clenup could be done here, but better use autocelan - // if you want a manual cleanup, disable autoclean by setting option: autoCleanFileAgeInSec = -1 - // then do the cleanup manual; use a timeout to prevent deleting pending operations - // console.log("Deleted", url2pdf.cleanup(5)); // remove all files older than 5 seconds - }) - .catch(function (err) { - console.error(err.stack); - }); + url2pdf.renderFromHTML("

Not working due to needed loading time - no image might be present

"), + url2pdf.renderFromHTML("

Page loaded with timeout. You should see a big image

", {loadTimeout: 2000}) +]) + .then(function (paths) { + console.log('Created PDFs @', paths); + // manual clenup could be done here, but better use autocelan + // if you want a manual cleanup, disable autoclean by setting option: autoCleanFileAgeInSec = -1 + // then do the cleanup manual; use a timeout to prevent deleting pending operations + // console.log("Deleted", url2pdf.cleanup(5)); // remove all files older than 5 seconds + }) + .catch(function (err) { + console.error(err.stack); + }); diff --git a/index.js b/index.js index 4fe90eb..e3b23a0 100644 --- a/index.js +++ b/index.js @@ -1,133 +1,163 @@ -/*jslint node: true */ -'use strict'; +var findRemove = require('find-remove'), + _ = require('lodash'), + phantom = require('phantom'), + q = require('q'), + // node intern modules + fs = require('fs'), + crypto = require('crypto'), + execSync = require('child_process').execSync, + path = require('path'); -var http = require("http"); -var phantom = require("phantom"); -var fs = require("fs"); -var q = require('q'); -var path = require('path'); -var findRemove = require('find-remove'); -var _ = require('lodash'); var _opts = { - paperSize: { - format: "A4", - orientation: 'portrait', - margin: '1cm' - }, - saveDir: path.join(__dirname, "pdfTemp"), - idLength: 30, - possibleIdChars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", - loadTimeout: 500, - autoCleanFileAgeInSec: 20 + paperSize: { + format: 'A4', + orientation: 'portrait', + margin: '1cm' + }, + saveDir: path.join(__dirname, '/pdfTemp'), + idLength: 30, + possibleIdChars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + loadTimeout: 500, + autoCleanFileAgeInSec: 20, + debug: false }; module.exports = { - opts: _opts, - renderPdf: function renderpdf(url, opts) { - return render(url, opts, "url"); - }, - renderFromHTML: function renderFromHTML(htmlString, opts) { - return render(htmlString, opts, "htmlContent"); - }, - cleanup: _cleanup -}; + opts: _opts, + renderPdf: function renderpdf (url, opts) { + return render(url, opts, 'url'); + }, + renderFromHTML: function renderFromHTML (htmlString, opts) { + return render(htmlString, opts, 'htmlContent'); + }, + cleanup: _cleanup, + join: _join +}; -function render(string, opts, renderType) { - var deferred = q.defer(); - var page, fileName, ph, fullPath; - - opts = mergeOpts(opts); - phantom.create(['--ignore-ssl-errors=yes']) - .then(function(_ph) { - ph = _ph; - return ph.createPage(); - }) - .then(function(_page) { - page = _page; - return page.property("paperSize", opts.paperSize); - }) - .then(function() { - if (renderType == "url") { - return page.open(string); // string = url path - } else if (renderType == "htmlContent") { - return page.property("content", string); // string = html doc as string - } else { - console.log("wrong render type: " + renderType); - ph.exit(); - } - }) - .then(function(status) { - fileName = makeid(opts.idLength) + ".pdf"; - fullPath = opts.saveDir + "/" + fileName; - return promiseWithTimeout(opts.loadTimeout); - }) - .then(function() { - return page.render(fullPath); - }) - .then(function() { - deferred.resolve(fullPath); - }) - .catch(function(err) { - deferred.reject(err); - }) - .then(function() { - ph.exit(); - if(0 < opts.autoCleanFileAgeInSec){ - _cleanup(opts.autoCleanFileAgeInSec); - } - }); - return deferred.promise; +function render (string, opts, renderType) { + opts = mergeOpts(opts); + + var deferred = q.defer(), + page, fileName, ph, fullPath, + + // log phantoms output in debugging mode + phantomOpts = opts.debug ? { + logLevel: 'debug' + } : {}; + + phantom.create(['--ignore-ssl-errors=yes'], phantomOpts) + .then(function (_ph) { + ph = _ph; + return ph.createPage(); + }) + .then(function (_page) { + page = _page; + + // log messages from phantom browser console in debugging mode + if (opts.debug) { + page.property('onConsoleMessage', function (msg) { + console.log('Phantom console Message:'); + console.log(msg); + }); + } + + return page.property('paperSize', opts.paperSize); + }) + .then(function () { + if (renderType === 'url') { + return page.open(string); // string = url path + } else if (renderType === 'htmlContent') { + return page.property('content', string); // string = html doc as string + } else { + console.log('wrong render type: ' + renderType); + ph.exit(); + } + }) + .then(function (status) { + fileName = makeid(opts.idLength) + '.pdf'; + fullPath = opts.saveDir + '/' + fileName; + return promiseWithTimeout(opts.loadTimeout); + }) + .then(function () { + return page.render(fullPath); + }) + .then(function () { + deferred.resolve(fullPath); + }) + .catch(function (err) { + deferred.reject(err); + }) + .then(function () { + ph.exit(); + if (opts.autoCleanFileAgeInSec > 0) { + _cleanup(opts.autoCleanFileAgeInSec); + } + }); + + return deferred.promise; } - -function mergeOpts(newOpts) { - if (newOpts) { - return _.merge({}, _opts, newOpts); - } else { - return _opts; - } +function mergeOpts (newOpts) { + if (newOpts) { + return _.merge({}, _opts, newOpts); + } else { + return _opts; + } } -function promiseWithTimeout(duration) { - var deferred = q.defer(); - setTimeout(deferred.resolve, duration); - return deferred.promise; +function promiseWithTimeout (duration) { + var deferred = q.defer(); + setTimeout(deferred.resolve, duration); + return deferred.promise; } -function makeid(strLength) { - if (!strLength) strLength = 30; - var text = ""; - var possible = _opts.possibleIdChars; +function makeid (strLength) { + if (!strLength) strLength = 30; + var text = ''; + var possible = _opts.possibleIdChars; - for (var i = 0; i < strLength; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; + for (var i = 0; i < strLength; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; } -function closure(fn) { - var _arguments = arguments; - delete arguments[0]; - return function() { - fn.apply(_arguments); - }; +function _cleanup (ageInSeconds, opts) { + opts = mergeOpts(opts); + return findRemove(opts.saveDir, { + age: { + seconds: ageInSeconds + } + }); } -function _cleanup(ageInSeconds) { - return findRemove(_opts.saveDir, { - age: { - seconds: ageInSeconds - } +function _join (filePathArray, exportUrl, onlyFile) { + + exportUrl = exportUrl || _opts.saveDir; + exportUrl += crypto.randomBytes(20).toString('hex'); + + execSync('gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=' + exportUrl + ' ' + filePathArray.join(' ')); + + filePathArray.forEach(function (filePath) { // delete merged files + fs.unlinkSync(filePath); }); + + if (onlyFile) { // read file in, delete file, return content + var fileContent = fs.readFileSync(exportUrl); + fs.unlinkSync(exportUrl); + return fileContent; + } else { // leave file and return url + return exportUrl; + } } diff --git a/package.json b/package.json index a849980..50cded5 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,36 @@ { - "name": "url2pdf", - "version": "0.5.0", + "name": "url2pdf-plus", + "version": "0.5.7", "description": "Fetches a URL and converts HTML to PDF files using PhantomJS", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Ryan Knell", - "license": "ISC", - "dependencies": { - "bindings": "^1.2.1", - "find-remove": "^0.2.10", - "lodash": "^4.8.1", - "phantom": "^2.0.9", - "q": "^1.1.2" + "author": { + "name": "Felix Furtmayr" }, - "_shasum": "cce8fd909f7464b54f16b91a69d1fd18c650edd8", - "_from": "url2pdf@latest", - "_npmVersion": "2.1.10", - "_nodeVersion": "0.10.22", - "_npmUser": { - "name": "ryanknell", - "email": "sales@snappyapps.com.au" + "license": "MIT", + "repository": { + "type": "git", + "url": "git@github.com:FelixFurtmayr/url2pdf.git" }, "maintainers": [ - "ryanknell " + { + "name": "Felix Furtmayr", + "email": "ff@rapidfacture.com" + } ], - "dist": { - "shasum": "cce8fd909f7464b54f16b91a69d1fd18c650edd8", - "tarball": "http://registry.npmjs.org/url2pdf/-/url2pdf-0.0.2.tgz" + "main": "index.js", + "scripts": {}, + "dependencies": { + "bindings": "1.3.0", + "find-remove": "1.2.0", + "lodash": "4.17.5", + "phantom": "^2.0.9", + "q": "^1.1.2" }, - "directories": {}, - "_resolved": "https://registry.npmjs.org/url2pdf/-/url2pdf-0.0.2.tgz", "devDependencies": {}, - "repository": { - "type": "git", - "url": "git+https://github.com/rknell/url2pdf.git" - }, "keywords": [ "html2pdf", "url2pdf", "html", "to", "pdf" - ], - "bugs": { - "url": "https://github.com/rknell/url2pdf/issues" - }, - "homepage": "https://github.com/rknell/url2pdf#readme" + ] } diff --git a/readme.md b/readme.md index 2e0cd14..cea99c5 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,14 @@ -#Url2PDF +# Deprecated! Please use url2pdf3 which uses up to date technology and is mantained! +Get it here: https://www.npmjs.com/package/url2pdf3 + + +# Url2PDF --- Grab a URL and convert the HTML to PDF using PhantomJS. Phantom renders the printing version. Can be used for generating bills, protocols, lists, etc. from a website. - - ## Installation ## npm install url2pdf --save @@ -59,6 +61,20 @@ just for this purpose? Easy! Just do as below: } ``` +#### Join Pdfs + +```javascript + var url2pdf = require("url2pdf"); + + // read file in, delete file, return content + const jointPdfFile = url2pdf.join(filepaths, '/exportDir', 'onlyFile'); + + // alternative: leave the file and return url + const jointPdfUrl = url2pdf.join(filepaths); +``` + +This requires ghostscript on your system which is executed over command line. + #### Manual cleanup url2pdf comes with an auto cleanup function that will delete old files in your temp directory. For a manual cleanup disable the auto cleanup in the function call: @@ -82,7 +98,8 @@ You can set the configuration options globally by editing the `url2pdf.opts` obj saveDir: path.join(__dirname, "pdfTemp"), // path for temporary files idLength: 30 // file ID length; adjust to avoid conflicts or just get smaller filenames loadTimeout: 800, // in ms; time for rendering the page - autoCleanFileAgeInSec: 24 * 3600 // [s]; older files are removed; set to "-1" to disable remove + autoCleanFileAgeInSec: 24 * 3600; // [s]; older files are removed; set to "-1" to disable remove + debug: false // enable to log PhantomJS output in debugging mode + PhantomJS console messages on your console }; ```