Skip to content

Comments

Handle Encrypted PDF files#119

Merged
RandyCupic merged 6 commits into3.0from
SPLAT-4180-password-protected-pdf
Feb 16, 2026
Merged

Handle Encrypted PDF files#119
RandyCupic merged 6 commits into3.0from
SPLAT-4180-password-protected-pdf

Conversation

@petarjakopec
Copy link

@petarjakopec petarjakopec commented Feb 3, 2026

The function first checks the file extension. If it's not a .pdf (case-insensitive), it returns false immediately. This avoids unnecessary file reading for images or other documents. After that, it ensures the temporary file uploaded by Symfony actually exists on the disk and is readable by the PHP process.
For small PDFs, head and tail reads can overlap and even fully duplicate the file contents.
This can re-introduce false positives (e.g. /Encrypt in a trailing comment after %%EOF), in that case, we scan the full content once.

`if ($fileSize !== false && $fileSize <= 20480) {
$content = (string) fread($fp, $fileSize);
} else {
$head = (string) fread($fp, 4096);

        @fseek($fp, -16384, SEEK_END);
        $tail = (string) fread($fp, 16384);

        $content = $head . $tail;
    }`

Otherwise, nstead of loading the entire PDF into memory (which could be hundreds of megabytes), the function reads only the specific parts of the file where encryption markers are typically stored:
The Head (first 4KB):
$head = (string) fread($fp, 4096);
This captures the PDF header and initial metadata.

We ignore anything after the last EOF marker (some tools append non-PDF comments/metadata).
$eofPos = strrpos($content, '%%EOF'); if ($eofPos !== false) { $content = substr($content, 0, $eofPos + 5); }

Finally, we detect presence of encryption dictionary reference in PDF object context.
This avoids false positives where /Encrypt appears in metadata or trailing comments.
return (bool) preg_match('/\/(?:Encrypt)\s+(\d+|<<)/m', $content);

This is the core logic. It searches for the /Encrypt token within the combined head and tail buffers.
In the PDF specification, the /Encrypt key in the document trailer dictionary indicates that the file is encrypted (password-protected).
If this token is found, the function returns true, signaling the controller to use resourceType: 'raw' for the Cloudinary upload.

@petarjakopec petarjakopec force-pushed the SPLAT-4180-password-protected-pdf branch from f1b48d9 to 24eb4e7 Compare February 12, 2026 08:02
@RandyCupic RandyCupic merged commit 60444cf into 3.0 Feb 16, 2026
5 of 6 checks passed
@RandyCupic RandyCupic deleted the SPLAT-4180-password-protected-pdf branch February 16, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants