Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

140 řádky
4.5 KiB

před 3 roky
  1. /**
  2. * Controls the communication with LibreOffice
  3. */
  4. /*
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS-IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. const async = require('async');
  18. const fs = require('fs');
  19. const log4js = require('log4js');
  20. const os = require('os');
  21. const path = require('path');
  22. const settings = require('./Settings');
  23. const spawn = require('child_process').spawn;
  24. // Conversion tasks will be queued up, so we don't overload the system
  25. const queue = async.queue(doConvertTask, 1);
  26. const libreOfficeLogger = log4js.getLogger('LibreOffice');
  27. /**
  28. * Convert a file from one type to another
  29. *
  30. * @param {String} srcFile The path on disk to convert
  31. * @param {String} destFile The path on disk where the converted file should be stored
  32. * @param {String} type The type to convert into
  33. * @param {Function} callback Standard callback function
  34. */
  35. exports.convertFile = function (srcFile, destFile, type, callback) {
  36. // Used for the moving of the file, not the conversion
  37. const fileExtension = type;
  38. if (type === 'html') {
  39. // "html:XHTML Writer File:UTF8" does a better job than normal html exports
  40. if (path.extname(srcFile).toLowerCase() === '.doc') {
  41. type = 'html';
  42. }
  43. // PDF files need to be converted with LO Draw ref https://github.com/ether/etherpad-lite/issues/4151
  44. if (path.extname(srcFile).toLowerCase() === '.pdf') {
  45. type = 'html:XHTML Draw File';
  46. }
  47. }
  48. // soffice can't convert from html to doc directly (verified with LO 5 and 6)
  49. // we need to convert to odt first, then to doc
  50. // to avoid `Error: no export filter for /tmp/xxxx.doc` error
  51. if (type === 'doc') {
  52. queue.push({
  53. srcFile,
  54. destFile: destFile.replace(/\.doc$/, '.odt'),
  55. type: 'odt',
  56. callback() {
  57. queue.push({srcFile: srcFile.replace(/\.html$/, '.odt'), destFile, type, callback, fileExtension});
  58. },
  59. });
  60. } else {
  61. queue.push({srcFile, destFile, type, callback, fileExtension});
  62. }
  63. };
  64. function doConvertTask(task, callback) {
  65. const tmpDir = os.tmpdir();
  66. async.series([
  67. /*
  68. * use LibreOffice to convert task.srcFile to another format, given in
  69. * task.type
  70. */
  71. function (callback) {
  72. libreOfficeLogger.debug(`Converting ${task.srcFile} to format ${task.type}. The result will be put in ${tmpDir}`);
  73. const soffice = spawn(settings.soffice, [
  74. '--headless',
  75. '--invisible',
  76. '--nologo',
  77. '--nolockcheck',
  78. '--writer',
  79. '--convert-to',
  80. task.type,
  81. task.srcFile,
  82. '--outdir',
  83. tmpDir,
  84. ]);
  85. // Soffice/libreoffice is buggy and often hangs.
  86. // To remedy this we kill the spawned process after a while.
  87. const hangTimeout = setTimeout(() => {
  88. soffice.stdin.pause(); // required to kill hanging threads
  89. soffice.kill();
  90. }, 120000);
  91. let stdoutBuffer = '';
  92. // Delegate the processing of stdout to another function
  93. soffice.stdout.on('data', (data) => {
  94. stdoutBuffer += data.toString();
  95. });
  96. // Append error messages to the buffer
  97. soffice.stderr.on('data', (data) => {
  98. stdoutBuffer += data.toString();
  99. });
  100. soffice.on('exit', (code) => {
  101. clearTimeout(hangTimeout);
  102. if (code != 0) {
  103. // Throw an exception if libreoffice failed
  104. return callback(`LibreOffice died with exit code ${code} and message: ${stdoutBuffer}`);
  105. }
  106. // if LibreOffice exited succesfully, go on with processing
  107. callback();
  108. });
  109. },
  110. // Move the converted file to the correct place
  111. function (callback) {
  112. const filename = path.basename(task.srcFile);
  113. const sourceFilename = `${filename.substr(0, filename.lastIndexOf('.'))}.${task.fileExtension}`;
  114. const sourcePath = path.join(tmpDir, sourceFilename);
  115. libreOfficeLogger.debug(`Renaming ${sourcePath} to ${task.destFile}`);
  116. fs.rename(sourcePath, task.destFile, callback);
  117. },
  118. ], (err) => {
  119. // Invoke the callback for the local queue
  120. callback();
  121. // Invoke the callback for the task
  122. task.callback(err);
  123. });
  124. }