diff options
-rw-r--r-- | modules/pdfrotate/editor.html | 17 | ||||
-rw-r--r-- | modules/pdfrotate/index.html | 65 | ||||
-rw-r--r-- | modules/pdfrotate/pdfrotate.js | 106 | ||||
-rw-r--r-- | package-lock.json | 196 | ||||
-rw-r--r-- | package.json | 2 |
5 files changed, 386 insertions, 0 deletions
diff --git a/modules/pdfrotate/editor.html b/modules/pdfrotate/editor.html new file mode 100644 index 0000000..6be03a8 --- /dev/null +++ b/modules/pdfrotate/editor.html @@ -0,0 +1,17 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>PDF rotate</title> +<script> +var sessionid=(function(){ + var spl=location.href.split("/"); + return spl[spl.length-1]; +})(); +</script> +</head> +<body> +Editor... +</body> +<script>alert(sessionid);</script> +</html> diff --git a/modules/pdfrotate/index.html b/modules/pdfrotate/index.html new file mode 100644 index 0000000..ae63865 --- /dev/null +++ b/modules/pdfrotate/index.html @@ -0,0 +1,65 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>PDF rotate</title> +<script> +window.addEventListener("load",function(){ + var errc=location.href.split("?err=")[1]; + if(errc){ + if(errc=="nopdf")alert("That doesn't seem to be a PDF document."); + else if(errc=="rot0")alert("Haha your document was already upright. Maybe just use that instead?"); + else if(errc=="invrot")alert("Don't bug my form, that rotation was invalid!"); + else if(errc=="pdfjamerr")alert("An error occurred trying to convert that document. Maybe it isn't a PDF document?"); + else alert("Unknown error occurred ('"+errc+"')"); + } +}); +</script> +<style> +.pagediagram { + display: inline-block; + border: 1px black solid; + width: 40px; + height: 56px; + font-family: sans-serif; + font-size: 40px; + text-align: center; + margin-left: 9px; + margin-right: 9px; + line-height: 62px; +} +</style> +</head> +<body> +<h1>PDF rotate</h1> +<p>Select a PDF file below.</p> +<form method="POST" action="/pdfrotate/upload" enctype="multipart/form-data"> + <input type="file" id="file" name="file" required><br> + <p>Click the button that looks most like the document you have. We will turn it upright.</p> + <table><tbody><tr> + <!-- CSS and pdfjam rotation are conveniently each other's inverses. --> + <td style="text-align: center;"> + <label for="rot0"><div class="pagediagram" style="transform:rotate(0deg);">A</div></label><br> + <input type="radio" id="rot0" name="rotgroup" value="rot0" required> + </td> + <td style="text-align: center;"> + <label for="rot90"><div class="pagediagram" style="transform:rotate(90deg);">A</div></label><br> + <input type="radio" id="rot90" name="rotgroup" value="rot90" required> + </td> + <td style="text-align: center;"> + <label for="rot180"><div class="pagediagram" style="transform:rotate(180deg);">A</div></label><br> + <input type="radio" id="rot180" name="rotgroup" value="rot180" required> + </td> + <td style="text-align: center;"> + <label for="rot270"><div class="pagediagram" style="transform:rotate(270deg);">A</div></label><br> + <input type="radio" id="rot270" name="rotgroup" value="rot270" required> + </td> + </tr></tbody></table> + <p>Now click the button below to finish the process!</p> + <input type="submit" value="Upload, turn and download result!"> +</form> +<br> +<br> +<i><small>This uses <a href="https://warwick.ac.uk/fac/sci/statistics/staff/academic-research/firth/software/pdfjam/">pdfjam</a> to perform the actual hard work. Credits to me only for the slick rotated A's above.</small></i> +</body> +</html> diff --git a/modules/pdfrotate/pdfrotate.js b/modules/pdfrotate/pdfrotate.js new file mode 100644 index 0000000..a41241d --- /dev/null +++ b/modules/pdfrotate/pdfrotate.js @@ -0,0 +1,106 @@ +var cmn=require("../$common.js"), + fs=require("fs"), + child_process=require("child_process"), + multer=require("multer"), + mkdirp=require("mkdirp"); + +var moddir; + +var uniqid=(function(){ + var id=0; + return function uniqid(){return id++;}; +})(); + +function clearDirectory(path){ + var entries=fs.readdirSync(path); + for(var i=0;i<entries.length;i++){ + fs.unlinkSync(path+"/"+entries[i]); + } +} + +function cleanUpDir(path){ + var nowtime=new Date().getTime(); + var entries=fs.readdirSync(path); + for(var i=0;i<entries.length;i++){ + var stat=fs.statSync(path+"/"+entries[i]); + if(stat.mtime-nowtime>30*60*1000){ + fs.unlink(path+"/"+entries[i],function(err){}); + } + } +} + +// Clear files older than 30 mins every 30 mins +setInterval(function(){ + process.nextTick(cleanUpDir(moddir+"/uploads")); + process.nextTick(cleanUpDir(moddir+"/converted")); +},30*60*1000); + +module.exports=function(app,io,_moddir){ + moddir=_moddir; + + mkdirp.sync(moddir+"/uploads"); + mkdirp.sync(moddir+"/converted"); + clearDirectory(moddir+"/uploads"); + + var multerupload=multer({ + storage: multer.diskStorage({ + destination: function(req,file,cb){ + cb(null,moddir+"/uploads/"); + }, + filename: function(req,file,cb){ + cb(null,uniqid()+".pdf"); + } + }), + limits: { + files: 1, + fileSize: 100*1024*1024 + } + }); + + app.get("/pdfrotate",function(req,res){ + res.sendFile(moddir+"/index.html"); + }); + app.post("/pdfrotate/upload",multerupload.single("file"),function(req,res){ + if(!req.file){ + res.sendFile(moddir+"/index.html"); + return; + } + var origname=req.file.originalname; + var path=req.file.path; + var rot=req.body.rotgroup+""; + if(rot=="rot0"){ + fs.unlink(path,function(err){}); + res.redirect("/pdfrotate?err=rot0"); + return; + } + + var cmd; + switch(rot){ + case "rot90": cmd="pdf90"; break; + case "rot180": cmd="pdf180"; break; + case "rot270": cmd="pdf270"; break; + default: + fs.unlink(path,function(err){}); + res.redirect("/pdfrotate?err=invrot"); + return; + } + + var outfile=moddir+"/converted/"+uniqid()+".pdf"; + + child_process.execFile(cmd,[path,"-o",outfile],function(err,stdout,stderr){ + if(err){ + console.log(err); + res.redirect("/pdfrotate?err=pdfjamerr"); + return; + } + var newname=origname.replace(/\.pdf$/,"_rotated.pdf"); + res.download(outfile,newname,function(err){ + fs.unlink(path,function(err){}); + fs.unlink(outfile,function(err){}); + }); + }); + }); + app.get("/pdfrotate/upload",function(req,res){ + res.redirect("/pdfrotate"); + }); +}; diff --git a/package-lock.json b/package-lock.json index 3b7a4ec..6efa6ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "append-field": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz", + "integrity": "sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo=" + }, "basic-auth": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", @@ -168,6 +173,73 @@ } } }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.14" + } + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.14", + "streamsearch": "0.1.2" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, "express": { "version": "4.15.4", "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", @@ -472,6 +544,11 @@ "resolved": "https://registry.npmjs.org/express-subdomain/-/express-subdomain-1.0.5.tgz", "integrity": "sha1-mQ75eUC39MKCPZWTZIt5voWKY4s=" }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, "is-absolute": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", @@ -502,6 +579,57 @@ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=" }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "multer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.3.0.tgz", + "integrity": "sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=", + "requires": { + "append-field": "0.1.0", + "busboy": "0.2.14", + "concat-stream": "1.6.0", + "mkdirp": "0.5.1", + "object-assign": "3.0.0", + "on-finished": "2.3.0", + "type-is": "1.6.15", + "xtend": "4.0.1" + } + }, "naampje": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/naampje/-/naampje-1.0.1.tgz", @@ -537,6 +665,40 @@ } } }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, "socket.io": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.4.tgz", @@ -878,10 +1040,44 @@ } } }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" } } } diff --git a/package.json b/package.json index 2b8d5f5..2518305 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "body-parser": "^1.15.2", "express": "^4.14.0", "express-subdomain": "^1.0.5", + "mkdirp": "^0.5.1", + "multer": "^1.3.0", "naampje": "^1.0.1", "node-persist": "^2.0.2", "socket.io": "^1.4.8" |