mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 13:13:45 +01:00
* Add png to pdf converter for optimising image loading in pdftex * refactor * move fixtures * refactor and add unit tests * Remove gamma correction from PNG processing and update related tests * Add crc32fast dependency and enhance PNG processing tests * Add fixture test cases for PNG processing * fix alpha channel to match pdftex pdftex always uses an 8 bit alpha channel in the output even for 16 bit images * Refactor PNG processing to use Option for alpha stream and update related logic * Enhance PDF writing by adding DecodeParms for alpha stream and optimize PNG encoding process * Add pixel dimensions support in PNG processing and PDF writing * [copybara] move clsi excludes * [clsi] move gitignore rules * [png2pdf] move into dockerfiles folder * [png2pdf] use atomic writes * [clsi] integrate png2pdf * [saas-e2e] add coverage for png2pdf * [png2pdf] make compatible with seccomp profile in clsi * [copybara] remove exclude following move of png2pdf * [copybara] remove spurious excludes * fix broken detection of malformed chunk headers Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * [project-history] add build script flag for depending on doc-updater types * [png2pdf] run tests as part of clsi pipeline * [png2pdf] fix calculation of PDF page size * [web] put png2pdf behind feature flag and make it opt-in * [web] fix ClsiManager.promises.sendRequest invocation after merge * extend is_copy_safe function to follow pdftex see the conditions in pdftex's writepng.c https://svn.tug.org:8369/texlive/trunk/Build/source/texk/web2c/pdftexdir/writepng.c?revision=78081&view=markup#l575 the criteria are not an exact match because the png crate does not support all the png chunk types https://github.com/image-rs/image-png/issues/116 if the criteria result in a png being converted or skipped the final result should be the same. in one case the conversion will be done by pdftex (at runtime) in the other case the conversion will be done by png2pdf - both conversions should give the same result. * include tests in png2pdf Dockerfile * [clsi] explicitly disable png2pdf conversion for project conversion * [clsi] png2pdf: count converted files * [png2pdf] bump date in docker image tag * [web] make draft-setting option wider when png2pdf is available --------- Co-authored-by: Brian Gough <brian.gough@overleaf.com> GitOrigin-RevId: d0d952ee009bf93c72bba2ffe763ae0160d396e4
97 lines
3.2 KiB
JavaScript
97 lines
3.2 KiB
JavaScript
import logger from '@overleaf/logger'
|
|
import Settings from '@overleaf/settings'
|
|
import Metrics from '@overleaf/metrics'
|
|
import OError from '@overleaf/o-error'
|
|
import CommandRunner from './CommandRunner.js'
|
|
|
|
// Convert "slow" PNGs (e.g. with transparency) to PDF ahead of the compile, so
|
|
// that pdflatex can include them via the fast native path instead of paying the
|
|
// conversion cost on every run. The png2pdf tool overwrites each file in place
|
|
// with PDF bytes (pdflatex looks at file content, not the extension); PNGs that
|
|
// are already fast-includable are left unchanged.
|
|
//
|
|
// The conversion runs in an isolated, network-disabled container built FROM
|
|
// scratch (see dockerfiles/png2pdf), with the project's cache dir bind-mounted.
|
|
|
|
/**
|
|
* Whether png-to-pdf conversion is available in this deployment. It requires
|
|
* sandboxed (docker) compiles, the host path of the cache dir (so the sibling
|
|
* container can bind-mount it) and a configured image.
|
|
*
|
|
* @return {boolean}
|
|
*/
|
|
export function isEnabled() {
|
|
return Boolean(
|
|
Settings.enablePng2pdfConversions &&
|
|
Settings.clsi?.dockerRunner === true &&
|
|
Settings.path.sandboxedCompilesHostDirCache &&
|
|
Settings.png2pdfImage
|
|
)
|
|
}
|
|
|
|
// The tool prints one "Converted <file> to PDF" line per file it actually
|
|
// converts; already-fast PNGs are skipped silently.
|
|
const CONVERTED_LINE = /^Converted .+ to PDF$/gm
|
|
|
|
/**
|
|
* Run the png2pdf binary over a list of files living directly inside a project's
|
|
* cache dir, converting them in place. Files are processed sequentially by the
|
|
* binary in a single container invocation.
|
|
*
|
|
* @param {string} projectId
|
|
* @param {string} cacheProjectDir absolute path to the project's cache dir
|
|
* @param {string[]} relativePaths file names within cacheProjectDir to convert
|
|
* @param {Record<string, number>} stats
|
|
* @param {Record<string, number>} timings
|
|
* @return {Promise<void>}
|
|
*/
|
|
export async function convertPngFilesInCacheDir(
|
|
projectId,
|
|
cacheProjectDir,
|
|
relativePaths,
|
|
stats,
|
|
timings
|
|
) {
|
|
if (!isEnabled()) return
|
|
|
|
const timer = new Metrics.Timer('png2pdf')
|
|
try {
|
|
const { stdout, stderr, exitCode } = await CommandRunner.promises.run(
|
|
projectId,
|
|
// `--` terminates option parsing: the cache file names start with a `-`
|
|
// (the leading `/` of the blob URL path), which clap would otherwise treat
|
|
// as a flag.
|
|
['--in-place', '--', ...relativePaths],
|
|
cacheProjectDir,
|
|
Settings.png2pdfImage,
|
|
Settings.conversionTimeoutSeconds * 1000,
|
|
{},
|
|
'png2pdf',
|
|
null
|
|
)
|
|
if (exitCode !== 0) {
|
|
throw new OError('non-zero exit code from png2pdf', {
|
|
exitCode,
|
|
stdout,
|
|
stderr,
|
|
})
|
|
}
|
|
const converted = (stdout.match(CONVERTED_LINE) || []).length
|
|
logger.debug(
|
|
{ projectId, attempted: relativePaths.length, converted, stdout, stderr },
|
|
'png2pdf conversion completed'
|
|
)
|
|
Metrics.count('png2pdf-converted', converted)
|
|
stats.png2pdf = converted
|
|
timings.png2pdf = timer.done({ status: 'success' })
|
|
} catch (err) {
|
|
timings.png2pdf = timer.done({ status: 'error' })
|
|
throw OError.tag(err, 'png2pdf conversion failed', {
|
|
projectId,
|
|
count: relativePaths.length,
|
|
})
|
|
}
|
|
}
|
|
|
|
export default { isEnabled, convertPngFilesInCacheDir }
|