Skip to content

Fix chr() deprecation in ASCII85 last-tuple decoding#793

Merged
k00ni merged 2 commits intosmalot:masterfrom
Ciki:fix/ascii85-chr-deprecation-last-tuple
Apr 17, 2026
Merged

Fix chr() deprecation in ASCII85 last-tuple decoding#793
k00ni merged 2 commits intosmalot:masterfrom
Ciki:fix/ascii85-chr-deprecation-last-tuple

Conversation

@Ciki
Copy link
Copy Markdown
Contributor

@Ciki Ciki commented Apr 7, 2026

Problem

PR #779 fixed the chr() deprecation warning in the main ASCII85 decoding loop but missed the "last tuple" switch/case block (handling incomplete groups at the end of encoded data).

The same issue applies — bit-shifted values ($tuple >> 16, $tuple >> 8) can exceed 255, triggering:

chr(): Providing a value not in-between 0 and 255 is deprecated, this is because a byte value must be in the [0, 255] interval.

Observed in production when parsing PDFs with ASCII85-encoded streams that have a partial trailing group.

Fix

Apply & 0xFF bitmask to extract individual bytes before passing to chr(), consistent with the approach used in the main loop fix from #779.

& 0xFF is the standard bitwise idiom for extracting a single byte from a multi-byte integer (equivalent to % 256 but more idiomatic).

Changes

// Before
$decoded .= \chr($tuple >> 24).\chr($tuple >> 16).\chr($tuple >> 8);

// After
$decoded .= \chr(($tuple >> 24) & 0xFF).\chr(($tuple >> 16) & 0xFF).\chr(($tuple >> 8) & 0xFF);

Same pattern applied to case 3 and case 2.

PR smalot#779 fixed the chr() deprecation warning in the main decoding loop
but missed the "last tuple" switch/case block (lines 233-248) which
handles incomplete groups at the end of the encoded data.

The same issue applies: bit-shifted values ($tuple >> 16, $tuple >> 8)
can exceed 255, triggering:
  chr(): Providing a value not in-between 0 and 255 is deprecated

Apply `& 0xFF` bitmask to extract individual bytes, consistent with
the fix in the main loop.
@k00ni k00ni added the fix label Apr 7, 2026
@k00ni
Copy link
Copy Markdown
Collaborator

k00ni commented Apr 8, 2026

@Ciki Thank you for the PR.

If I remember correctly, the changes in #779 where suggested somewhere to fix PHP 8.5 related deprecations. I have limited knowledge in this area so I had to check your information. The following two pages describe bit-wise operations quite a bit:

  1. https://medium.com/android-news/java-when-to-use-n-8-0xff-and-when-to-use-byte-n-8-2efd82ae7dd7
  2. https://oscarliang.com/what-s-the-use-of-and-0xff-in-programming-c-plus-p/

Code looks good. Could you provide one of the PDFs which triggered these so we can use it in our test suite?

I will keep this open for a few days in case someone else wants to comment. Or do you need it merged right away?

@Ciki
Copy link
Copy Markdown
Contributor Author

Ciki commented Apr 10, 2026

@k00ni Thanks for the review!

The original PDF that triggered this contained client data, so I generated a minimal reproduction PDF instead.

File: ascii85-last-tuple-overflow.pdf

It's a valid single-page PDF (657 bytes) with one ASCII85Decode-filtered content stream. The stream is crafted so that the last ASCII85 group (4-char incomplete tuple) produces values exceeding 255 when bit-shifted:

  • $tuple = 541414470
  • $tuple >> 24 = 32 (OK)
  • $tuple >> 16 = 8261 (> 255 → chr() deprecation)
  • $tuple >> 8 = 2114900 (> 255 → chr() deprecation)

With the fix (& 0xFF masking), these become chr(32), chr(69), chr(84) — all valid byte values.

No rush on merging — take your time. We're running a fork in the meantime.

…o PDF

Demo PDF would trigger the deprecation warnings without the fixes of
this PR.
@k00ni
Copy link
Copy Markdown
Collaborator

k00ni commented Apr 17, 2026

@Ciki I added your PDF to the test samples and a mini test which loads it. Without your fixes one would see the deprecations.

There were a few warnings (at least in PHP 8.5, example):

Runtime:       PHP 8.5.4
Configuration: /home/runner/work/pdfparser/pdfparser/phpunit.xml

...............................................................  63 / 192 ( 32%)
............................................................... 126 / 192 ( 65%)
.................W.........W................................... 189 / 192 ( 98%)
...                                                             192 / 192 (100%)

Time: 00:16.086, Memory: 605.75 MB

2 tests triggered 11 PHP warnings:

1) /home/runner/work/pdfparser/pdfparser/src/Smalot/PdfParser/Font.php:145
The float 1.013347617134381E+19 is not representable as an int, cast occurred

Triggered by:

* PHPUnitTests\Integration\ParserTest::testIssue621 (2 times)
  /home/runner/work/pdfparser/pdfparser/tests/PHPUnit/Integration/ParserTest.php:302

* PHPUnitTests\Integration\ParserTest::testParseFile (2 times)
  /home/runner/work/pdfparser/pdfparser/tests/PHPUnit/Integration/ParserTest.php:58

2) /home/runner/work/pdfparser/pdfparser/src/Smalot/PdfParser/Font.php:145
The float 9.039039706125167E+20 is not representable as an int, cast occurred

Triggered by:

* PHPUnitTests\Integration\ParserTest::testIssue621 (2 times)
  /home/runner/work/pdfparser/pdfparser/tests/PHPUnit/Integration/ParserTest.php:302

* PHPUnitTests\Integration\ParserTest::testParseFile (2 times)
  /home/runner/work/pdfparser/pdfparser/tests/PHPUnit/Integration/ParserTest.php:58

[...]

Those are not because of your changes as far as I can tell, therefore ignored for now.

Thank you for taking the time preparing the pull request. It was a joy not having to ask for every detail but to get it prepared and on point.

@k00ni k00ni merged commit 2cfa0d9 into smalot:master Apr 17, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants