Problem/Motivation
PHP bugfix https://github.com/php/php-src/commit/22c487616f132ee7ebfa838bce9d14c924... has changed the logic to calculate image dimensions for rotated images, causing Drupal's critical #2918570: Drupal\KernelTests\Core\Image\ToolkitGdTest fails on PHP 7.1.x-dev and 7.0.x-dev following a PHP bugfix.
See also:
libgd fix - https://github.com/libgd/libgd/issues/225
Proposed resolution
Fix the Rectangle class to adapt the same calculation logic as GD in libgd 2.2.2 and above. This way images rotated in Drupal will always take the same dimensions, regardless of the libgd library actually linked to the PHP instance in use.
Remaining tasks
User interface changes
None
API changes
None
Data model changes
None
| Comment | File | Size | Author |
|---|---|---|---|
| #73 | 2921123-73.patch | 221.76 KB | ravi.shankar |
Issue fork drupal-2921123
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 2921123-adjust-rectangle-class
changes, plain diff MR !267
Comments
Comment #2
mondrakeLet's start by getting a new data sample for RectangleTest.
This was obtained by re-running the calculation routine from http://cgit.drupalcode.org/drupal/tree/core/tests/Drupal/Tests/Component..., reading as input the test sample http://cgit.drupalcode.org/drupal/tree/core/tests/Drupal/Tests/Component... and rewriting the width/height results, on a PHP 7.0.25 compiled with GD library 2.1.1-dev.
The patch is test-only and gives us the target to get to in adjusting the calculation for Rectangle.
Comment #3
mondrakeComment #5
mondrakeTurns out fixing this is easier than expected: just implemented some PHP equivalents of the GD library affine transormation functions seem to do the trick.
This will now fail on ToolkitGdTest and ImageDimensionsTest, because the expected dimensions coded in the tests are still the PHP 5.5 ones.
So I'd postpone this on #2918570: Drupal\KernelTests\Core\Image\ToolkitGdTest fails on PHP 7.1.x-dev and 7.0.x-dev following a PHP bugfix.
Comment #7
mondrakeComment #11
mondrakeReroll.
Comment #12
mondrakeThe hotfix in #2918570-11: Drupal\KernelTests\Core\Image\ToolkitGdTest fails on PHP 7.1.x-dev and 7.0.x-dev following a PHP bugfix skipped the image dimensions assertions, and the code is still commented out, making the patch of this issue actually pass. So this issue is independent from the other and can be unpostponed.
Reparenting to #3053363: Remove support for PHP 5 in Drupal 8.8.
Comment #13
mondrakeWe do not need to touch the Rotate GD image toolkit operation here.
Comment #17
alexpott@mondrake do we need to provide a rectangle class anymore? There are no usages in core. Can't we just deprecate it and be done.
Comment #18
mondrake@alexpott it is used in core in https://git.drupalcode.org/project/drupal/-/blob/9.2.x/core/modules/imag..., and the ImageMagick module relies on it.
Comment #19
alexpott@mondrake ah yeah you're right I though #13 was removing it due to the interdiff but I got RotateImageEffect and Rotate confused.
That's a shame because the change here is not being done in BC compatible manor. The protected methods on \Drupal\Component\Utility\Rectangle should have been private. This issue can't remove them as it is doing.
Comment #20
alexpottThis issue needs to prove it works by removing the version compare in
\Drupal\KernelTests\Core\Image\ToolkitGdTest::testManipulations
Comment #21
mondrakeLet's see this... the no longer used protected methods in Rectangle will have to be deprecated properly, then,
Comment #22
alexpott#21 looks better from a BC pov - and yeah we need to deprecate those old methods.
Can now use PHP 7.3 typehints and return types. And +1 for private here. There should be no protected API for anything that wants to extend Rectangle.
Comment #24
mondrakeAddressed #22 and added deprecation, left for todo the CR. Are deprecation test needed here given that there's no way now to get the methods being executed from public? Will need reflection.
Comment #25
mondrakeComment #27
mondrakeThis should pass on PHP 7.4. However, it looks like GD has changed *again* the algo for rotation leading to different resulting dimensions again. So #2, that was obtained with PHP 7.0.25, should be resampled again with 7.3+.
Comment #28
tanubansal commented@mondrake : Please suggest if this can be tested on 9.2. Do we need to have PHP 7.4 configured?
Comment #29
mondrake#27 so the problem here is with the libgd linked into the PHP executable, not with PHP itself.
GD rotate functions were overhauled in libgd 2.2.2, https://github.com/libgd/libgd/releases/tag/gd-2.2.2
The new algo in Rectangle matches that logic of calculation. I just re-extracted the sample data with PHP 7.3.19 and libgd 2.2.5 and it matches exactly with the one in the patch.
However, if PHP links lower versions of libgd (it's the case on TravisCI for instance, that is 2.1.0 compatible; I'll post a test patch here for checking DrupalCI), gd images will be rotated according to old logic. This leads to the weirdness below
i.e. that
transformDimensionscalculates a different set of dimensions than the one actually returned by therotateoperation.IMHO we need to go back to the concept in #2918570: Drupal\KernelTests\Core\Image\ToolkitGdTest fails on PHP 7.1.x-dev and 7.0.x-dev following a PHP bugfix and adjust the gd rotate plugin to adjust the image to the dimensions that we calculate in Rectangle - so that we have one version of 'our' truth across the board and do not have to play catch up.
Comment #30
mondrakeA patch to sort out what libgd versions are in use in the DrupalCI containers.
Comment #32
mondrakeSo DrupalCI are on libgd 2.1.0, too.
Adding a test that checks the validity of the sample data against the actual GD calculation, that is only applicable if libgd is 2.2.2+. It would be good to have a DrupalCI container with a PHP with that version.
Comment #33
mondrakeAdding hunks from #2918570: Drupal\KernelTests\Core\Image\ToolkitGdTest fails on PHP 7.1.x-dev and 7.0.x-dev following a PHP bugfix to make the GDToolkit rotate operation abide to the dimensions calculated by Rectangle.
Comment #36
mondrakeFixing failure and improving the GD toolkit test.
Comment #37
mondrakeNote to self: it's actually the other way round, https://www.php.net/manual/en/function.imagerotate.php
Comment #38
mondrakeComment #39
mondrakeChange to MR workflow.
Comment #41
mondrakeRerolled
Comment #42
mondrake#37 still open
Comment #43
mondrakeRerolled and addressed #37.
Comment #44
daffie commentedComment #45
mondrake@daffie I am maintaining and, in part, developed the Image Effects module. Some of the testing code and the image overlying code in the Rotate operation in case of images not fitting the expected dimensions is from there. The changes in the Rectangle class are the PHP equivalent of C++ code from libgd, see the @see annotations in the code.
#44.2 in ImageStyle::transformDimensions we calculate image dimensions 'in abstract' i.e. without a real image being processed, to speed up the determination of the width and height attributes of
<img>tags. This calculation is done in the Rectangle class, https://www.drupal.org/node/2645822. When the Rectangle class and the 'real' gd library on a site produce different results, we end up with the problem here. #2918570: Drupal\KernelTests\Core\Image\ToolkitGdTest fails on PHP 7.1.x-dev and 7.0.x-dev following a PHP bugfix happened to be critical at a point in time because a change of PHP version started failing all tests on DrupalCI.edit - right now in HEAD the Rectangle class calculates dimensions as PHP was doing in PHP 5.5. This is way outdated and ALL
<img>with rotations have tags with height/width attributes different than real... with tests that were catching that now disabled. #2918570-42: Drupal\KernelTests\Core\Image\ToolkitGdTest fails on PHP 7.1.x-dev and 7.0.x-dev following a PHP bugfix.Comment #46
mondrakeComment #47
mondrakeComment #48
daffie commentedBack to needs work for the unresolved threads.
Comment #49
mondrakeComment #50
daffie commentedSo if I understand it correctly, we can update the class Drupal\Component\Utility\Rectangle, when every PHP version the Drupal core supports has the libgd with version 2.2.2 or higher. Could we add @todo in the patch for that and maybe create a followup issue for it.
Comment #51
mondrake@daffie re #50, no actually that is the real point: we do not want to make vodoo dances every time the logic in PHP or GD changes. So we set 'our own' version of the rotation calculation algo (incidentally, with this patch, equal to the algo used in libgd 2.2.2), and (a) use that in transformDimensions, and (b) adjust the result of a real rotation in the Rotate gd image toolkit operation to comply to that if the dimensions are different from our calculation. So that we only change the Rectangle class when we want, and any Drupal version in any PHP version with any libgd version will behave the same way.
BTW. DrupalCI on PHP 7.3 and 7.4 still has an old version of libgd (2.1.0), see #30. This results in
testGdRotatebeing actually skipped, even if it is reported successful.Comment #52
mondrakeComment #53
daffie commentedAll code changes look good to me.
The deprecation messages have no testing, because the methods are all protected methods.
There is testing that the implementation of class Drupal\Component\Utility\Rectangle has the same result as the implementation of libgd version 2.2.2 or higher.
There is already testing in core for the rotation of images.
For me it is RTBC.
Comment #54
mondrakeThank you for your reviews, @daffie @andypost and @alexpott, really appreciated.
Comment #55
andypostIs there a way to run PHP 8 CI on the MR? It could have fresher libgd
Comment #56
mondrakeNo it still has 2.1.0, see #30
Comment #57
alexpottI think this issue is blocked by not having any test coverage on DrupalCI because there's no bot with a high enough libgd.
If I run the core/tests/Drupal/Tests/Component/Utility/RectangleTest.php locally I get 3484 fails!
^^ is the last one.
Here's the output of my gd_info()...
Has the calculation changed again in 2.3.0? If we commit the patch as is it feels like something that will cause fails in the future when the PHP builds are updated.
Comment #58
mondrakeIt's a never ending story :(... I will check again with 2.2.5.
Comment #59
alexpott@mondrake I keep on wondering if trying to normalise this is really core's job...
Comment #60
mondrakeif not, then what? Revert #1551686: Image rotate dimensions callback can calculate new dimensions for every angle?
Comment #61
mondrakeWorks for me...
on
Comment #62
daffie commented@mondrake: Does it also pass with libgd 2.3.0 or higher. When @alexpott tested it with that version it failed.
Comment #63
mondrakeI tested D9.2 on PHP 7.3, 7.4 and 8.0 with libgd 2.3.0 on Github Actions and I have full passes in all cases, https://github.com/mondrake/d8-unit/actions/runs/569429199
Comment #64
andypostneeds re-roll after #3193955: Swap assertEqual arguments in preparation to replace with assertEquals
used to run test on Ubuntu 20.10
The final result of
SIMPLETEST_DB=sqlite://./db/tests.db SIMPLETEST_BASE_URL=http://nginx BROWSERTEST_OUTPUT_DIRECTORY=db vendor/bin/phpunit -c core/phpunit.xml.dist --colors=always -v core/tests/Drupal/Tests/Component/Utility/RectangleTest.phpisResults are the same as #57 (last 3 cases)
Comment #65
andypostMoreover
- the bundled (shipped with PHP) version is 2.0.35 https://github.com/php/php-src/blob/master/ext/gd/libgd/gd.h#L14-L18
- last time I used to run php tests using
--with-external-gdoption it has a lot of failures, will check again tonight- 2.3.0 is security release and has a lot of changes https://github.com/libgd/libgd/blob/gd-2.3.1/CHANGELOG.md
Comment #66
mondrake#64 I do not understand what reroll is needed, can you please clarify.
It would be great if someone having the failure with 2.3.0 could test with 2.2.2. Git blamed the code and looked at the changelog, no apprent changes in the logic for rotate after 2.2.2. Maybe something else influences results, no clue.
Comment #67
andypostNot sure about re-roll because I got failures with
$ curl https://git.drupalcode.org/project/drupal/-/merge_requests/267.patch |patch -p1but using diff (
curl https://git.drupalcode.org/project/drupal/-/merge_requests/267.diff |patch -p1) it applies cleanly@mondrake as your Github tests shows it requires to rebuild PHP itself with externally linked
libgdwhich is very trickyEDIT using external GD shows 21 failed test in Alpinelinux (latest 2.3.1 version) https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/18321#note...
One of failed tests is
Bug #65148 (imagerotate may alter image dimensions) [ext/gd/tests/bug65148.phpt]Comment #68
andypostThe failure caused by https://bugs.php.net/bug.php?id=65148 still depends on how PHP is build
Comment #72
andypostThe MR needs rebase to 10.x or 9.5.x at least
Comment #73
ravi.shankar commentedAdded a patch for Drupal 9.5.x.
Comment #75
mondrakeNote #3240390: [policy, no patch] Move image rotation code from core into contrib.
Comment #78
mstrelan commentedI came here from #2829040: [meta] Known intermittent, random, and environment-specific test failures and I'm struggling to see where this is up to. There is discussion on whether we should do this at all, then there is an MR, but then the MR was closed and a related issue was linked. The related issue is now closed (won't fix) so where does that leave this issue? Setting to PMNMI so we can get clarification on what's required.
Comment #81
mondrakeYeah this is messy now, probably better close this and start with a new issue aiming at fixing the test failure (if we want to, this is probably a rather edge case).
The original intent here was to refresh the Rectangle class with an algo that is more aligned with PHP 8. The calculations done currently are mirroring PHP 5 - this means that TransformDimensions consistently produce results that differ from the actual results of processing images via GD.
The main problem is that it’s difficult to target a ‘standard’ algo because PHP relies on the GD library to perform the actual processing, different versions return different results, and you cannot predict which version of GD is used by the PHP instance used for testing.
Comment #82
mondrakeNR for #81
Comment #83
andypostIIRC this change was done in PHP 7.1 so it's safe enough to use new approach, moreover there's not a lot of distros which shipping old GD library.
Moreover the most of PHP builds using bundled GD library
Comment #84
mondrakeSo let's close this for now, also based on feedback in https://drupal.slack.com/archives/C079NQPQUEN/p1742553484074889
Comment #85
mondrakeFiled #3514699: [consistent test failure] ImageDimensionsTest::testImageDimensions() as a replacement of this one.
Comment #86
andypostRelated in terms of PHP 8.5 #3540531: Fix ToolkitGdTest for PHP 8.5