Răsfoiți Sursa

[filestore] add `pdftocairo` conversion option (#31627)

Includes patches for Server Pro/CE 5.x and 6.x

GitOrigin-RevId: 67e387c96421b681339dbc1d89a8af0c34a163ef
Miguel Serrano 5 luni în urmă
părinte
comite
f4dd04e110

+ 1 - 1
server-ce/Dockerfile-base

@@ -23,7 +23,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
 &&  apt-get update \
 &&  apt-get install -y \
       unattended-upgrades \
-      build-essential wget net-tools unzip time imagemagick optipng strace nginx git python3 python-is-python3 zlib1g-dev libpcre3-dev gettext-base libwww-perl ca-certificates curl gnupg \
+      build-essential wget net-tools unzip time poppler-utils optipng strace nginx git python3 python-is-python3 zlib1g-dev libpcre3-dev gettext-base libwww-perl ca-certificates curl gnupg \
       qpdf \
 # upgrade base-image, batch all the upgrades together, rather than installing them on-by-one (which is slow!)
 &&  unattended-upgrade --verbose --no-minimal-upgrade-steps \

+ 2 - 0
server-ce/config/settings.js

@@ -468,6 +468,8 @@ switch (process.env.OVERLEAF_FILESTORE_BACKEND) {
     }
 }
 
+settings.converter = process.env.CONVERTER || 'pdftocairo'
+
 if (
   !settings.trustedProxyIps.includes('loopback') &&
   !settings.trustedProxyIps.includes('localhost') &&

+ 13 - 0
server-ce/hotfix/5.5.8/Dockerfile

@@ -0,0 +1,13 @@
+FROM sharelatex/sharelatex:5.5.7
+
+# Apply security updates to base image
+RUN apt update && apt install -y linux-libc-dev \
+  && unattended-upgrade --verbose --no-minimal-upgrade-steps \
+  && apt purge -y imagemagick \
+  && apt autoremove -y \
+  && apt install -y poppler-utils \
+  && rm -rf /var/lib/apt/lists/*
+
+# Update converter
+COPY issue_31527.patch .
+RUN patch -p0 < issue_31527.patch && rm issue_31527.patch

+ 90 - 0
server-ce/hotfix/5.5.8/issue_31527.patch

@@ -0,0 +1,90 @@
+--- services/filestore/app/js/FileConverter.js
++++ services/filestore/app/js/FileConverter.js
+@@ -21,49 +21,44 @@
+ }
+ 
+ async function convert(sourcePath, requestedFormat) {
+-  const width = '600x'
++  const width = 1500
+   return await _convert(sourcePath, requestedFormat, [
+-    'convert',
+-    '-define',
+-    `pdf:fit-page=${width}`,
+-    '-flatten',
+-    '-density',
+-    '300',
+-    `${sourcePath}[0]`,
++    'pdftocairo',
++    '-png',
++    '-singlefile',
++    '-scale-to-x',
++    width.toString(),
++    '-scale-to-y',
++    '-1',
++    sourcePath,
+   ])
+ }
+ 
+ async function thumbnail(sourcePath) {
+-  const width = '260x'
+-  return await convert(sourcePath, 'png', [
+-    'convert',
+-    '-flatten',
+-    '-background',
+-    'white',
+-    '-density',
+-    '300',
+-    '-define',
+-    `pdf:fit-page=${width}`,
+-    `${sourcePath}[0]`,
+-    '-resize',
+-    width,
++  const width = 700
++  return await _convert(sourcePath, 'png', [
++    'pdftocairo',
++    '-png',
++    '-singlefile',
++    '-scale-to-x',
++    width.toString(),
++    '-scale-to-y',
++    '-1',
++    sourcePath,
+   ])
+ }
+ 
+ async function preview(sourcePath) {
+-  const width = '548x'
+-  return await convert(sourcePath, 'png', [
+-    'convert',
+-    '-flatten',
+-    '-background',
+-    'white',
+-    '-density',
+-    '300',
+-    '-define',
+-    `pdf:fit-page=${width}`,
+-    `${sourcePath}[0]`,
+-    '-resize',
+-    width,
++  const width = 1000
++  return await _convert(sourcePath, 'png', [
++    'pdftocairo',
++    '-png',
++    '-singlefile',
++    '-scale-to-x',
++    width.toString(),
++    '-scale-to-y',
++    '-1',
++    sourcePath,
+   ])
+ }
+ 
+@@ -77,7 +72,7 @@
+   const timer = new metrics.Timer('imageConvert')
+   const destPath = `${sourcePath}.${requestedFormat}`
+ 
+-  command.push(destPath)
++  command.push(sourcePath)
+   command = Settings.commands.convertCommandPrefix.concat(command)
+ 
+   try {

+ 13 - 0
server-ce/hotfix/6.1.2/Dockerfile

@@ -0,0 +1,13 @@
+FROM sharelatex/sharelatex:6.1.1
+
+# Apply security updates to base image
+RUN apt update && apt install -y linux-libc-dev \
+  && unattended-upgrade --verbose --no-minimal-upgrade-steps \
+  && apt purge -y imagemagick \
+  && apt autoremove -y \
+  && apt install -y poppler-utils \
+  && rm -rf /var/lib/apt/lists/*
+
+# Update converter
+COPY issue_31527.patch .
+RUN patch -p0 < issue_31527.patch && rm issue_31527.patch

+ 91 - 0
server-ce/hotfix/6.1.2/issue_31527.patch

@@ -0,0 +1,91 @@
+--- services/filestore/app/js/FileConverter.js
++++ services/filestore/app/js/FileConverter.js
+@@ -22,49 +22,44 @@ export default {
+ }
+ 
+ async function convert(sourcePath, requestedFormat) {
+-  const width = '600x'
++  const width = 1500
+   return await _convert(sourcePath, requestedFormat, [
+-    'convert',
+-    '-define',
+-    `pdf:fit-page=${width}`,
+-    '-flatten',
+-    '-density',
+-    '300',
+-    `${sourcePath}[0]`,
++    'pdftocairo',
++    '-png',
++    '-singlefile',
++    '-scale-to-x',
++    width.toString(),
++    '-scale-to-y',
++    '-1',
++    sourcePath,
+   ])
+ }
+ 
+ async function thumbnail(sourcePath) {
+-  const width = '260x'
+-  return await convert(sourcePath, 'png', [
+-    'convert',
+-    '-flatten',
+-    '-background',
+-    'white',
+-    '-density',
+-    '300',
+-    '-define',
+-    `pdf:fit-page=${width}`,
+-    `${sourcePath}[0]`,
+-    '-resize',
+-    width,
++  const width = 700
++  return await _convert(sourcePath, 'png', [
++    'pdftocairo',
++    '-png',
++    '-singlefile',
++    '-scale-to-x',
++    width.toString(),
++    '-scale-to-y',
++    '-1',
++    sourcePath,
+   ])
+ }
+ 
+ async function preview(sourcePath) {
+-  const width = '548x'
+-  return await convert(sourcePath, 'png', [
+-    'convert',
+-    '-flatten',
+-    '-background',
+-    'white',
+-    '-density',
+-    '300',
+-    '-define',
+-    `pdf:fit-page=${width}`,
+-    `${sourcePath}[0]`,
+-    '-resize',
+-    width,
++  const width = 1000
++  return await _convert(sourcePath, 'png', [
++    'pdftocairo',
++    '-png',
++    '-singlefile',
++    '-scale-to-x',
++    width.toString(),
++    '-scale-to-y',
++    '-1',
++    sourcePath,
+   ])
+ }
+ 
+@@ -78,7 +73,8 @@ async function _convert(sourcePath, requestedFormat, command) {
+   const timer = new metrics.Timer('imageConvert')
+   const destPath = `${sourcePath}.${requestedFormat}`
+ 
+-  command.push(destPath)
++  const outputBaseName = sourcePath
++  command.push(outputBaseName)
+   command = Settings.commands.convertCommandPrefix.concat(command)
+ 
+   try {

+ 80 - 39
services/filestore/app/js/FileConverter.js

@@ -22,50 +22,91 @@ export default {
 }
 
 async function convert(sourcePath, requestedFormat) {
-  const width = '600x'
-  return await _convert(sourcePath, requestedFormat, [
-    'convert',
-    '-define',
-    `pdf:fit-page=${width}`,
-    '-flatten',
-    '-density',
-    '300',
-    `${sourcePath}[0]`,
-  ])
+  if (Settings.converter === 'pdftocairo') {
+    const width = 1500
+    return await _convert(sourcePath, requestedFormat, [
+      'pdftocairo',
+      '-png',
+      '-singlefile',
+      '-scale-to-x',
+      width.toString(),
+      '-scale-to-y',
+      '-1', // maintain aspect ratio
+      sourcePath,
+    ])
+  } else {
+    const width = '600x'
+    return await _convert(sourcePath, requestedFormat, [
+      'convert',
+      '-define',
+      `pdf:fit-page=${width}`,
+      '-flatten',
+      '-density',
+      '300',
+      `${sourcePath}[0]`,
+    ])
+  }
 }
 
 async function thumbnail(sourcePath) {
-  const width = '260x'
-  return await convert(sourcePath, 'png', [
-    'convert',
-    '-flatten',
-    '-background',
-    'white',
-    '-density',
-    '300',
-    '-define',
-    `pdf:fit-page=${width}`,
-    `${sourcePath}[0]`,
-    '-resize',
-    width,
-  ])
+  if (Settings.converter === 'pdftocairo') {
+    const width = 700
+    return await _convert(sourcePath, 'png', [
+      'pdftocairo',
+      '-png',
+      '-singlefile',
+      '-scale-to-x',
+      width.toString(),
+      '-scale-to-y',
+      '-1', // maintain aspect ratio
+      sourcePath,
+    ])
+  } else {
+    const width = '260x'
+    return await convert(sourcePath, 'png', [
+      'convert',
+      '-flatten',
+      '-background',
+      'white',
+      '-density',
+      '300',
+      '-define',
+      `pdf:fit-page=${width}`,
+      `${sourcePath}[0]`,
+      '-resize',
+      width,
+    ])
+  }
 }
 
 async function preview(sourcePath) {
-  const width = '548x'
-  return await convert(sourcePath, 'png', [
-    'convert',
-    '-flatten',
-    '-background',
-    'white',
-    '-density',
-    '300',
-    '-define',
-    `pdf:fit-page=${width}`,
-    `${sourcePath}[0]`,
-    '-resize',
-    width,
-  ])
+  const width = 1000
+  if (Settings.converter === 'pdftocairo') {
+    return await _convert(sourcePath, 'png', [
+      'pdftocairo',
+      '-png',
+      '-singlefile',
+      '-scale-to-x',
+      width.toString(),
+      '-scale-to-y',
+      '-1', // maintain aspect ratio
+      sourcePath,
+    ])
+  } else {
+    return await convert(sourcePath, 'png', [
+      'convert',
+      '-flatten',
+      '-background',
+      'white',
+      '-density',
+      '300',
+      '-define',
+      `pdf:fit-page=${width}`,
+      `${sourcePath}[0]`,
+      '-resize',
+      width,
+    ])
+  }
 }
 
 async function _convert(sourcePath, requestedFormat, command) {
@@ -78,7 +119,7 @@ async function _convert(sourcePath, requestedFormat, command) {
   const timer = new metrics.Timer('imageConvert')
   const destPath = `${sourcePath}.${requestedFormat}`
 
-  command.push(destPath)
+  command.push(Settings.converter === 'pdftocairo' ? sourcePath : destPath)
   command = Settings.commands.convertCommandPrefix.concat(command)
 
   try {

+ 2 - 0
services/filestore/config/settings.defaults.cjs

@@ -99,6 +99,8 @@ const settings = {
 
   enableConversions: process.env.ENABLE_CONVERSIONS === 'true',
 
+  converter: process.env.CONVERTER || 'imagemagick',
+
   gracefulShutdownDelayInMs:
     parseInt(process.env.GRACEFUL_SHUTDOWN_DELAY_SECONDS ?? '30', 10) * 1000,
 }