├── .gitignore ├── package.json ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unoconv", 3 | "version": "0.1.2", 4 | "description": "Wrapper for converting documents with unoconv.", 5 | "homepage": "https://github.com/gfloyd/node-unoconv", 6 | "author": { 7 | "name": "Graham Floyd", 8 | "email": "grahamf@gmail.com" 9 | }, 10 | "main": "./index", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/gfloyd/node-unoconv.git" 14 | }, 15 | "keywords": [ 16 | "unoconv", 17 | "document", 18 | "conversion" 19 | ], 20 | "dependencies": { 21 | "underscore": "1.4.x", 22 | "mime": "1.2.x" 23 | }, 24 | "license": "MIT" 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-unoconv 2 | 3 | A node.js wrapper for converting documents with [unoconv](http://dag.wieers.com/home-made/unoconv/). 4 | 5 | ## Requirements 6 | 7 | [Unoconv](http://dag.wieers.com/home-made/unoconv/) is required, which requires [LibreOffice](http://www.libreoffice.org/) (or OpenOffice.) 8 | 9 | ## Install 10 | 11 | Install with: 12 | 13 | npm install unoconv 14 | 15 | ## Converting documents 16 | 17 | var unoconv = require('unoconv'); 18 | 19 | unoconv.convert('document.docx', 'pdf', function (err, result) { 20 | // result is returned as a Buffer 21 | fs.writeFile('converted.pdf', result); 22 | }); 23 | 24 | ## Starting a listener 25 | 26 | You can also start a unoconv listener to avoid launching Libre/OpenOffice on every conversion: 27 | 28 | unoconv.listen(); 29 | 30 | ## API 31 | 32 | ### unoconv.convert(file, outputFormat, [options], callback) 33 | 34 | Converts `file` to the specified `outputFormat`. `options` is an object with the following properties: 35 | 36 | * `bin` Path to the unoconv binary 37 | * `port` Unoconv listener port to connect to 38 | 39 | `callback` gets the arguments `err` and `result`. `result` is returned as a Buffer object. 40 | 41 | 42 | ### unoconv.listen([options]) 43 | 44 | Starts a new unoconv listener. `options` accepts the same parameters as `convert()`. 45 | 46 | Returns a `ChildProcess` object. You can handle errors by listening to the `stderr` property: 47 | 48 | var listener = unoconv.listen({ port: 2002 }); 49 | 50 | listener.stderr.on('data', function (data) { 51 | console.log('stderr: ' + data.toString('utf8')); 52 | }); 53 | 54 | ### unoconv.detectSupportedFormats([options], callback) 55 | 56 | This function parses the output of `unoconv --show` to attempt to detect supported output formats. 57 | 58 | `options` is an object with the following properties: 59 | 60 | * `bin` Path to the unoconv binary 61 | 62 | `callback` gets the arguments `err` and `result`. `result` is an object containing a collection of supported document types and output formats. 63 | 64 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('underscore'), 4 | childProcess = require('child_process'), 5 | mime = require('mime'); 6 | 7 | var unoconv = exports = module.exports = {}; 8 | 9 | /** 10 | * Convert a document. 11 | * 12 | * @param {String} file 13 | * @param {String} outputFormat 14 | * @param {Object|Function} options 15 | * @param {Function} callback 16 | * @api public 17 | */ 18 | unoconv.convert = function(file, outputFormat, options, callback) { 19 | var self = this, 20 | args, 21 | bin = 'unoconv', 22 | child, 23 | stdout = [], 24 | stderr = []; 25 | 26 | if (_.isFunction(options)) { 27 | callback = options; 28 | options = null; 29 | } 30 | 31 | args = [ 32 | '-f' + outputFormat, 33 | '--stdout' 34 | ]; 35 | 36 | if (options && options.port) { 37 | args.push('-p' + options.port) 38 | } 39 | 40 | args.push(file); 41 | 42 | if (options && options.bin) { 43 | bin = options.bin; 44 | } 45 | 46 | child = childProcess.spawn(bin, args, function (err, stdout, stderr) { 47 | if (err) { 48 | return callback(err); 49 | } 50 | 51 | if (stderr) { 52 | return callback(new Error(stderr.toString())); 53 | } 54 | 55 | callback(null, stdout); 56 | }); 57 | 58 | child.stdout.on('data', function (data) { 59 | stdout.push(data); 60 | }); 61 | 62 | child.stderr.on('data', function (data) { 63 | stderr.push(data); 64 | }); 65 | 66 | child.on('exit', function () { 67 | if (stderr.length) { 68 | return callback(new Error(Buffer.concat(stderr).toString())); 69 | } 70 | 71 | callback(null, Buffer.concat(stdout)); 72 | }); 73 | }; 74 | 75 | /** 76 | * Start a listener. 77 | * 78 | * @param {Object} options 79 | * @return {ChildProcess} 80 | * @api public 81 | */ 82 | unoconv.listen = function (options) { 83 | var self = this, 84 | args, 85 | bin = 'unoconv'; 86 | 87 | args = [ '--listener' ]; 88 | 89 | if (options && options.port) { 90 | args.push('-p' + options.port); 91 | } 92 | 93 | if (options && options.bin) { 94 | bin = options.bin; 95 | } 96 | 97 | return childProcess.spawn(bin, args); 98 | }; 99 | 100 | /** 101 | * Detect supported conversion formats. 102 | * 103 | * @param {Object|Function} options 104 | * @param {Function} callback 105 | */ 106 | unoconv.detectSupportedFormats = function (options, callback) { 107 | var self = this, 108 | docType, 109 | detectedFormats = { 110 | document: [], 111 | graphics: [], 112 | presentation: [], 113 | spreadsheet: [] 114 | }, 115 | bin = 'unoconv'; 116 | 117 | if (_.isFunction(options)) { 118 | callback = options; 119 | options = null; 120 | } 121 | 122 | if (options && options.bin) { 123 | bin = options.bin; 124 | } 125 | 126 | childProcess.execFile(bin, [ '--show' ], function (err, stdout, stderr) { 127 | if (err) { 128 | return callback(err); 129 | } 130 | 131 | // For some reason --show outputs to stderr instead of stdout 132 | var lines = stderr.split('\n'); 133 | 134 | lines.forEach(function (line) { 135 | if (line === 'The following list of document formats are currently available:') { 136 | docType = 'document'; 137 | } else if (line === 'The following list of graphics formats are currently available:') { 138 | docType = 'graphics'; 139 | } else if (line === 'The following list of presentation formats are currently available:') { 140 | docType = 'presentation'; 141 | } else if (line === 'The following list of spreadsheet formats are currently available:') { 142 | docType = 'spreadsheet'; 143 | } else { 144 | var format = line.match(/^(.*)-/); 145 | 146 | if (format) { 147 | format = format[1].trim(); 148 | } 149 | 150 | var extension = line.match(/\[(.*)\]/); 151 | 152 | if (extension) { 153 | extension = extension[1].trim().replace('.', ''); 154 | } 155 | 156 | var description = line.match(/-(.*)\[/); 157 | 158 | if (description) { 159 | description = description[1].trim(); 160 | } 161 | 162 | if (format && extension && description) { 163 | detectedFormats[docType].push({ 164 | 'format': format, 165 | 'extension': extension, 166 | 'description': description, 167 | 'mime': mime.lookup(extension) 168 | }); 169 | } 170 | } 171 | }); 172 | 173 | if (detectedFormats.document.length < 1 && 174 | detectedFormats.graphics.length < 1 && 175 | detectedFormats.presentation.length < 1 && 176 | detectedFormats.spreadsheet.length < 1) { 177 | return callback(new Error('Unable to detect supported formats')); 178 | } 179 | 180 | callback(null, detectedFormats); 181 | }); 182 | }; 183 | --------------------------------------------------------------------------------