UNPKG

libreoffice-file-converter

Version:

Simple NodeJS wrapper for libreoffice CLI for converting office documents to different formats

276 lines (269 loc) 9.98 kB
import { createReadStream, createWriteStream } from 'node:fs'; import { readFile, writeFile, access } from 'node:fs/promises'; import { setGracefulCleanup, dir } from 'tmp-promise'; import { Buffer } from 'node:buffer'; import { execFile } from 'node:child_process'; import process from 'node:process'; import { inspect } from 'node:util'; import { pathToFileURL } from 'node:url'; import { join, sep } from 'node:path'; import { pipeline } from 'node:stream'; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value); // src/constants/env.ts var PROGRAM_FILES = "PROGRAMFILES"; var PROGRAM_FILES_86 = "PROGRAMFILES(X86)"; var DARWIN_PATHS = /* @__PURE__ */ __name(() => [ "/Applications/LibreOffice.app/Contents/MacOS/soffice" ], "DARWIN_PATHS"); var LINUX_PATHS = /* @__PURE__ */ __name(() => [ "/usr/bin/libreoffice", "/usr/bin/soffice", "/snap/bin/libreoffice", "/opt/libreoffice/program/soffice", "/opt/libreoffice7.6/program/soffice" ], "LINUX_PATHS"); var WIN32_PATHS = /* @__PURE__ */ __name(() => [ join(process.env[PROGRAM_FILES_86] || "", "LIBREO~1/program/soffice.exe"), join(process.env[PROGRAM_FILES_86] || "", "LibreOffice/program/soffice.exe"), join(process.env[PROGRAM_FILES] || "", "LibreOffice/program/soffice.exe") ], "WIN32_PATHS"); // src/helpers/libreoffice.ts var getLibreOfficeExecutablePaths = /* @__PURE__ */ __name(() => { if (process.platform === "darwin") { return DARWIN_PATHS(); } if (process.platform === "linux") { return LINUX_PATHS(); } if (process.platform === "win32") { return WIN32_PATHS(); } return []; }, "getLibreOfficeExecutablePaths"); var getLibreOfficeExecutablePath = /* @__PURE__ */ __name(async (binaryPaths = []) => { const paths = [ ...binaryPaths, ...getLibreOfficeExecutablePaths() ]; const existingPaths = await Promise.all(paths.map(async (path2) => { try { await access(path2); } catch { return false; } return path2; })); const [path] = existingPaths.filter(Boolean); if (!path) { throw new Error("Could not find soffice binary"); } return path; }, "getLibreOfficeExecutablePath"); var getLibreOfficeCommandArgs = /* @__PURE__ */ __name((installationDir, inputPath, outputDir, format, inputFilter, outputFilter) => { const filterSegment = outputFilter && outputFilter.length > 0 ? `:${outputFilter}` : ""; const args = [ `-env:UserInstallation=${pathToFileURL(installationDir).toString()}`, "--headless" ]; if (inputFilter && inputFilter.length > 0) { args.push(`--infilter=${inputFilter}`); } args.push("--convert-to", `${format}${filterSegment}`, "--outdir", outputDir, inputPath); return args; }, "getLibreOfficeCommandArgs"); var hasLibreOfficeError = /* @__PURE__ */ __name((stderr) => { return stderr?.toLowerCase()?.includes("error:"); }, "hasLibreOfficeError"); // src/helpers/child-process.ts var isBuffer = /* @__PURE__ */ __name((value) => { return Buffer.isBuffer(value); }, "isBuffer"); var processOutputToString = /* @__PURE__ */ __name((processOutput) => { return isBuffer(processOutput) ? processOutput.toString("utf-8") : processOutput; }, "processOutputToString"); var execFileAsync = /* @__PURE__ */ __name((path, args, options, debug) => { return new Promise((resolve, reject) => { execFile(path, args, options, (error, stdout, stderr) => { const stderrString = processOutputToString(stderr); const stdoutString = processOutputToString(stdout); if (debug) { const debugInfo = inspect({ args, path, stderr: stderrString, stdout: stdoutString }, { colors: true, sorted: true }); process.stdout.write(`LibreOffice debug output: ${debugInfo} `); } const libreOfficeError = hasLibreOfficeError(stderrString); if (error || libreOfficeError) { return reject(error || new Error(stderrString)); } return resolve(); }); }); }, "execFileAsync"); var getTemporaryFilePath = /* @__PURE__ */ __name((temporaryDir) => { return join(temporaryDir, "source"); }, "getTemporaryFilePath"); var getProcessedFilePath = /* @__PURE__ */ __name((temporaryDirPath, inputPath, format) => { const insideTemporaryDir = inputPath.startsWith(temporaryDirPath); if (insideTemporaryDir) { return `${inputPath}.${format}`; } const inputPathSegments = inputPath.split(sep); const inputFileNameSegment = inputPathSegments.length > 0 ? inputPathSegments[inputPathSegments.length - 1] : inputPathSegments[0]; const inputFileNameSegments = inputFileNameSegment?.split("."); const inputFileName = inputFileNameSegments?.length && inputFileNameSegments.length > 1 ? inputFileNameSegments?.slice(0, -1).join(".") : inputFileNameSegments?.[0]; return `${temporaryDirPath}${sep}${inputFileName}.${format}`; }, "getProcessedFilePath"); var writeStream = /* @__PURE__ */ __name((path, readStream) => { return new Promise((resolve, reject) => { const writeStream2 = createWriteStream(path); pipeline(readStream, writeStream2, (error) => { if (error) { return reject(error); } return resolve(); }); }); }, "writeStream"); // src/helpers/primitives.ts var isObject = /* @__PURE__ */ __name((item) => { return Boolean(item && typeof item === "object" && !Array.isArray(item)); }, "isObject"); var deepMerge = /* @__PURE__ */ __name((...objects) => { return objects.reduce((target, source) => { Object.keys(source).forEach((key) => { const targetValue = target[key]; const sourceValue = source[key]; if (Array.isArray(targetValue) && Array.isArray(sourceValue)) { target[key] = [ ...targetValue, ...sourceValue ]; return; } if (isObject(targetValue) && isObject(sourceValue)) { target[key] = deepMerge(targetValue, sourceValue); return; } target[key] = sourceValue; }); return target; }, {}); }, "deepMerge"); // src/libreoffice-file-converter.ts var _LibreOfficeFileConverter = class _LibreOfficeFileConverter { /** * Create an instance of the LibreOfficeFileConverter. * * @example * ```ts * const libreOfficeFileConverter = new LibreOfficeFileConverter({ childProcessOptions: { timeout: 60 * 1000 } }); * ``` * * @param options The LibreOfficeFileConverter options. * * @constructor * @public */ constructor(options = {}) { __publicField(this, "_options"); this._options = options; setGracefulCleanup(); } /** * Converts provided input to the requested format. * * @example * ```ts * const outputBuffer = await libreOfficeFileConverter.convert({ * buffer: inputBuffer, * format: 'pdf', * input: 'buffer', * output: 'buffer', * }); * ``` * * @param options Convert options: input and output type, format, filter, converter options. * * @returns Buffer, readable stream or void depending on the convert options. * * @public */ async convert(options) { const { filter, format, inputFilter, options: callOptions = {}, outputFilter } = options; const mergedOptions = this.mergeOptions(callOptions); const { tmpOptions } = mergedOptions; const temporaryDir = await dir({ prefix: "libreoffice-file-converter", unsafeCleanup: true, ...tmpOptions }); const inputPath = await this.write(options, temporaryDir.path); await this.process(inputPath, temporaryDir.path, format, inputFilter || filter, outputFilter, mergedOptions); return this.read(options, temporaryDir.path, inputPath); } mergeOptions(options = {}) { return deepMerge(this._options, options); } async process(inputPath, outputDir, format, inputFilter, outputFilter, options = {}) { const { binaryPaths, childProcessOptions, debug, tmpOptions } = options; const libreOfficeExecutablePath = await getLibreOfficeExecutablePath(binaryPaths); const installationDir = await dir({ prefix: "soffice", unsafeCleanup: true, ...tmpOptions }); const libreOfficeCommandArgs = getLibreOfficeCommandArgs(installationDir.path, inputPath, outputDir, format, inputFilter, outputFilter); await execFileAsync(libreOfficeExecutablePath, libreOfficeCommandArgs, childProcessOptions, debug); } async read(options, temporaryDirPath, inputPath) { const { format, output } = options; const processedFilePath = getProcessedFilePath(temporaryDirPath, inputPath, format); if (output === "buffer") { return readFile(processedFilePath); } if (output === "file") { const { outputPath } = options; const stream = createReadStream(processedFilePath); return writeStream(outputPath, stream); } if (output === "stream") { return createReadStream(processedFilePath); } } async write(options, temporaryDirPath) { const { input } = options; const temporaryFilePath = getTemporaryFilePath(temporaryDirPath); if (input === "buffer") { const { buffer } = options; await writeFile(temporaryFilePath, buffer); return temporaryFilePath; } if (input === "file") { const { inputPath } = options; return inputPath; } if (input === "stream") { const { stream } = options; await writeStream(temporaryFilePath, stream); return temporaryFilePath; } return temporaryFilePath; } }; __name(_LibreOfficeFileConverter, "LibreOfficeFileConverter"); var LibreOfficeFileConverter = _LibreOfficeFileConverter; export { LibreOfficeFileConverter }; //# sourceMappingURL=index.mjs.map //# sourceMappingURL=index.mjs.map