| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- import logger from '@overleaf/logger'
- import Settings from '@overleaf/settings'
- import fs from 'node:fs/promises'
- import Path from 'node:path'
- import CommandRunner from './CommandRunner.js'
- import LockManager from './LockManager.js'
- import OError from '@overleaf/o-error'
- async function convertDocxToLaTeXWithLock(conversionId, inputPath) {
- const conversionDir = Path.join(Settings.path.compilesDir, conversionId)
- const lock = LockManager.acquire(conversionDir)
- try {
- return await convertDocxToLaTeX(conversionId, conversionDir, inputPath)
- } finally {
- lock.release()
- }
- }
- async function convertDocxToLaTeX(conversionId, conversionDir, inputPath) {
- await fs.mkdir(conversionDir, { recursive: true })
- const newSourcePath = Path.join(conversionDir, 'input.docx')
- await fs.copyFile(inputPath, newSourcePath)
- const outputName = crypto.randomUUID() + '.zip'
- try {
- const {
- stdout: stdoutPandoc,
- stderr: stderrPandoc,
- exitCode: exitCodePandoc,
- } = await CommandRunner.promises.run(
- conversionId,
- [
- 'pandoc',
- 'input.docx',
- '--output',
- 'main.tex',
- '--extract-media=.',
- '--from',
- 'docx+citations',
- '--to',
- 'latex',
- '--citeproc',
- '--standalone',
- ],
- conversionDir,
- Settings.pandocImage,
- Settings.conversionTimeoutSeconds * 1000,
- {},
- 'conversions'
- )
- if (exitCodePandoc !== 0) {
- throw new OError('Non-zero exit code from pandoc', {
- exitCode: exitCodePandoc,
- stderr: stderrPandoc,
- })
- }
- logger.debug(
- { stdout: stdoutPandoc, stderr: stderrPandoc, exitCode: exitCodePandoc },
- 'conversion command completed'
- )
- // Clean up the source document to leave only the conversion result
- await fs.unlink(newSourcePath).catch(() => {})
- const {
- stdout: stdoutZip,
- stderr: stderrZip,
- exitCode: exitCodeZip,
- } = await CommandRunner.promises.run(
- conversionId,
- ['zip', '-r', outputName, '.'],
- conversionDir,
- Settings.pandocImage,
- Settings.conversionTimeoutSeconds * 1000,
- {},
- 'conversions'
- )
- if (exitCodeZip !== 0) {
- throw new OError('Non-zero exit code from pandoc', {
- exitCode: exitCodeZip,
- stderr: stderrZip,
- })
- }
- logger.debug(
- { stdout: stdoutZip, stderr: stderrZip, exitCode: exitCodeZip },
- 'conversion output compressed'
- )
- } catch (error) {
- // Clean up the conversion directory on error to avoid leaving failed conversions around
- await fs.rm(conversionDir, { force: true, recursive: true }).catch(() => {})
- throw new OError('pandoc conversion failed').withCause(error)
- }
- return Path.join(conversionDir, outputName)
- }
- async function convertLaTeXToDocumentInDirWithLock(
- conversionId,
- compileDir,
- rootDocPath,
- type,
- extension
- ) {
- const lock = LockManager.acquire(compileDir)
- try {
- return await convertLaTeXToDocumentInDir(
- conversionId,
- compileDir,
- rootDocPath,
- type,
- extension
- )
- } finally {
- lock.release()
- }
- }
- async function convertLaTeXToDocumentInDir(
- conversionId,
- compileDir,
- rootDocPath = 'main.tex',
- type,
- extension
- ) {
- const outputName = crypto.randomUUID() + '.' + extension
- const timeoutMs = Settings.conversionTimeoutSeconds * 1000
- logger.debug(
- { compileDir, rootDocPath, type },
- 'running pandoc latex-to-document in compile dir'
- )
- const { exitCode, stdout, stderr } = await CommandRunner.promises.run(
- conversionId,
- [
- 'pandoc',
- rootDocPath,
- '--output',
- outputName,
- '--from',
- 'latex',
- '--to',
- type,
- '--resource-path=.',
- ],
- compileDir,
- Settings.pandocImage,
- timeoutMs,
- {},
- 'conversions'
- )
- if (exitCode !== 0) {
- throw new OError('pandoc latex-to-document conversion failed', {
- type,
- exitCode,
- stdout,
- stderr,
- })
- }
- return Path.join(compileDir, outputName)
- }
- export default {
- promises: {
- convertDocxToLaTeXWithLock,
- convertLaTeXToDocumentInDirWithLock,
- },
- }
|