FileConverter.js 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import metrics from '@overleaf/metrics'
  2. import Settings from '@overleaf/settings'
  3. import { callbackify } from 'node:util'
  4. import SafeExec from './SafeExec.js'
  5. import Errors from './Errors.js'
  6. const { ConversionError } = Errors
  7. const APPROVED_FORMATS = ['png']
  8. const FOURTY_SECONDS = 40 * 1000
  9. const KILL_SIGNAL = 'SIGTERM'
  10. export default {
  11. convert: callbackify(convert),
  12. thumbnail: callbackify(thumbnail),
  13. preview: callbackify(preview),
  14. promises: {
  15. convert,
  16. thumbnail,
  17. preview,
  18. },
  19. }
  20. async function convert(sourcePath, requestedFormat) {
  21. const width = '600x'
  22. return await _convert(sourcePath, requestedFormat, [
  23. 'convert',
  24. '-define',
  25. `pdf:fit-page=${width}`,
  26. '-flatten',
  27. '-density',
  28. '300',
  29. `${sourcePath}[0]`,
  30. ])
  31. }
  32. async function thumbnail(sourcePath) {
  33. const width = '260x'
  34. return await convert(sourcePath, 'png', [
  35. 'convert',
  36. '-flatten',
  37. '-background',
  38. 'white',
  39. '-density',
  40. '300',
  41. '-define',
  42. `pdf:fit-page=${width}`,
  43. `${sourcePath}[0]`,
  44. '-resize',
  45. width,
  46. ])
  47. }
  48. async function preview(sourcePath) {
  49. const width = '548x'
  50. return await convert(sourcePath, 'png', [
  51. 'convert',
  52. '-flatten',
  53. '-background',
  54. 'white',
  55. '-density',
  56. '300',
  57. '-define',
  58. `pdf:fit-page=${width}`,
  59. `${sourcePath}[0]`,
  60. '-resize',
  61. width,
  62. ])
  63. }
  64. async function _convert(sourcePath, requestedFormat, command) {
  65. if (!APPROVED_FORMATS.includes(requestedFormat)) {
  66. throw new ConversionError('invalid format requested', {
  67. format: requestedFormat,
  68. })
  69. }
  70. const timer = new metrics.Timer('imageConvert')
  71. const destPath = `${sourcePath}.${requestedFormat}`
  72. command.push(destPath)
  73. command = Settings.commands.convertCommandPrefix.concat(command)
  74. try {
  75. await SafeExec.promises(command, {
  76. killSignal: KILL_SIGNAL,
  77. timeout: FOURTY_SECONDS,
  78. })
  79. } catch (err) {
  80. throw new ConversionError(
  81. 'something went wrong converting file',
  82. { stderr: err.stderr, sourcePath, requestedFormat, destPath },
  83. err
  84. )
  85. }
  86. timer.done()
  87. return destPath
  88. }