[filestore] add pdftocairo conversion option (#31627)

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

GitOrigin-RevId: 67e387c96421b681339dbc1d89a8af0c34a163ef
This commit is contained in:
Miguel Serrano
2026-02-20 10:34:10 +01:00
committed by Copybot
parent 820adf568e
commit f4dd04e110
8 changed files with 292 additions and 40 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
&& apt-get update \ && apt-get update \
&& apt-get install -y \ && apt-get install -y \
unattended-upgrades \ 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 \ qpdf \
# upgrade base-image, batch all the upgrades together, rather than installing them on-by-one (which is slow!) # 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 \ && unattended-upgrade --verbose --no-minimal-upgrade-steps \
+2
View File
@@ -468,6 +468,8 @@ switch (process.env.OVERLEAF_FILESTORE_BACKEND) {
} }
} }
settings.converter = process.env.CONVERTER || 'pdftocairo'
if ( if (
!settings.trustedProxyIps.includes('loopback') && !settings.trustedProxyIps.includes('loopback') &&
!settings.trustedProxyIps.includes('localhost') && !settings.trustedProxyIps.includes('localhost') &&
+13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -22,50 +22,91 @@ export default {
} }
async function convert(sourcePath, requestedFormat) { async function convert(sourcePath, requestedFormat) {
const width = '600x' if (Settings.converter === 'pdftocairo') {
return await _convert(sourcePath, requestedFormat, [ const width = 1500
'convert', return await _convert(sourcePath, requestedFormat, [
'-define', 'pdftocairo',
`pdf:fit-page=${width}`, '-png',
'-flatten', '-singlefile',
'-density', '-scale-to-x',
'300', width.toString(),
`${sourcePath}[0]`, '-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) { async function thumbnail(sourcePath) {
const width = '260x' if (Settings.converter === 'pdftocairo') {
return await convert(sourcePath, 'png', [ const width = 700
'convert', return await _convert(sourcePath, 'png', [
'-flatten', 'pdftocairo',
'-background', '-png',
'white', '-singlefile',
'-density', '-scale-to-x',
'300', width.toString(),
'-define', '-scale-to-y',
`pdf:fit-page=${width}`, '-1', // maintain aspect ratio
`${sourcePath}[0]`, sourcePath,
'-resize', ])
width, } 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) { async function preview(sourcePath) {
const width = '548x' const width = 1000
return await convert(sourcePath, 'png', [ if (Settings.converter === 'pdftocairo') {
'convert', return await _convert(sourcePath, 'png', [
'-flatten', 'pdftocairo',
'-background', '-png',
'white', '-singlefile',
'-density', '-scale-to-x',
'300', width.toString(),
'-define', '-scale-to-y',
`pdf:fit-page=${width}`, '-1', // maintain aspect ratio
`${sourcePath}[0]`, sourcePath,
'-resize', ])
width, } 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) { async function _convert(sourcePath, requestedFormat, command) {
@@ -78,7 +119,7 @@ async function _convert(sourcePath, requestedFormat, command) {
const timer = new metrics.Timer('imageConvert') const timer = new metrics.Timer('imageConvert')
const destPath = `${sourcePath}.${requestedFormat}` const destPath = `${sourcePath}.${requestedFormat}`
command.push(destPath) command.push(Settings.converter === 'pdftocairo' ? sourcePath : destPath)
command = Settings.commands.convertCommandPrefix.concat(command) command = Settings.commands.convertCommandPrefix.concat(command)
try { try {
@@ -99,6 +99,8 @@ const settings = {
enableConversions: process.env.ENABLE_CONVERSIONS === 'true', enableConversions: process.env.ENABLE_CONVERSIONS === 'true',
converter: process.env.CONVERTER || 'imagemagick',
gracefulShutdownDelayInMs: gracefulShutdownDelayInMs:
parseInt(process.env.GRACEFUL_SHUTDOWN_DELAY_SECONDS ?? '30', 10) * 1000, parseInt(process.env.GRACEFUL_SHUTDOWN_DELAY_SECONDS ?? '30', 10) * 1000,
} }