-
Notifications
You must be signed in to change notification settings - Fork 149
Add imagick extension support #2194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: qa/2.x
Are you sure you want to change the base?
Add imagick extension support #2194
Conversation
sbreker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Daniel - thank you so much for this code contribution! I have added some additional feedback regarding the memcache change, and the logic to fall back to using ImageMagick via exec(). Please let me know if there is anything I should be considering with respect to these requests. Interested to hear your thoughts.
lib/model/QubitDigitalObject.php
Outdated
| $pages = sfImageMagickAdapter::getPdfinfoPageCount($filename); | ||
| } else { | ||
| $command = 'identify '.$filename; | ||
| } elseif (sfImageMagickAdapter::isImageMagickAvailable()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been thinking about the logic to fall back to using ImageMagick in the event that the Imagick PHP extension is not present. I think that while this may provide some convenience with respect to AtoM upgrade requirements, I think it is more important to completely remove the original way of calling ImageMagick with exec() calls and rely solely on your new work to introduce Imagick. Removing the calls to exec() entirely will avoid triggering alerts in future security scans and remove the risk associated with these calls. In this case, if we remove all dependency on sfImageMagickAdapter.class.php, then we might be able to delete sfImageMagickAdapter.class.php entirely (I have not checked if there are other locations where it is referenced - but removing it would have implications for the PDF page count methods in there).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would certainly simplify things in the code. One issue I can see with that change is that we'll want to ensure the upgrade instructions are up to date for whichever version of AtoM this ends up in since php8.3-imagick will be a dependency requirement , and imagemagick will no longer be required.
The imagick adapter handles PDF page counts without the use of Poppler (i.e., pdfinfo), having spent some time with this part of the code, I think completely removing sfImageMagickAdapter shouldn't be an issue at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/model/QubitDigitalObject.php
Outdated
| $pages = null; | ||
|
|
||
| if ($this->canThumbnail()) { | ||
| if (extension_loaded('imagick')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gets checked a number of times to ensure if imagick is available. Perhaps this should be removed to be a method in sfImagickAdapter like sfImageMagickAdapter does. I think Imagick also requires ImageMagick to be installed, so perhaps a a method like sfImagickAdapter::isImagickAvailable() could encapsulate the logic to check that both Imagick and ImageMagick are present. Let me know what you think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two places I have this are in those methods that fall back to using sfImageMagickAdapter when the extension isn't loaded - if we completely remove the old adapter, then there'll be no need to call extension_loaded('imagick').
And Imagick doesn't require ImageMagick to be installed, the ImageMagick C code is directly compiled into the imagick extension when it's built. See here, the MagickWand C API is included and that's how the C gets compiled into the resulting PHP extension. The only thing that's needed is the imagemagick-dev package for building the extension. In Ubuntu, that won't be needed since there is already a pre-built package available: php8.3-imagick.
EDIT: You do still need imagemagick installed, I misunderstood how it works! The extension fails to be loaded if imagemagick is not installed.
lib/model/om/BaseDigitalObject.php
Outdated
| try | ||
| { | ||
| return call_user_func_array('QubitObject::__get', $args); | ||
| return parent::__get(...$args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this file is generated by Symfony. Was this change the result of running a php symfony commands?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't remember why I made that change initially, but things are still working with that change reverted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $command .= $path; | ||
| $command .= ' '.$filenameMinusExtension.'_%02d.'.self::THUMB_EXTENSION; | ||
| exec($command, $output, $status); | ||
| $imagick = new Imagick(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should check that extension_loaded('imagick') is true before and issue some kind of error if it is not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function will only attempt to instantiate an Imagick instance now if the imagick extension is loaded: 4a92bf2
| exec($command, $output, $status); | ||
| $pages = count($output); | ||
| } | ||
| $im = new Imagick(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should check that extension_loaded('imagick') is true before and issue some kind of error if it is not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function will only attempt to instantiate an Imagick instance now if the imagick extension is loaded: 4a92bf2
| if (!$adapterClass) { | ||
| if (extension_loaded('gd')) { | ||
| $adapterClass = 'sfGDAdapter'; | ||
| } elseif (extension_loaded('imagick')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above in QubitDigitalObject.php we preferred Imagick over 'gd' but here we prefer 'gd' which could result in strange behaviour. It might be better to try loading first using Imagick here .
See:
#2194 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d6de81e
lib/model/QubitDigitalObject.php
Outdated
| $path = $this->getAbsolutePath(); | ||
| } | ||
|
|
||
| $filenameMinusExtension = preg_replace('/\.[a-zA-Z]{2,3}$/', '', $path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to your change but this parsing of extensions seems brittle. We should consider changing to use pathinfo($path, PATHINFO_DIRNAME|PATHINFO_FILENAME|etc).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this line to use pathinfo: c721411
| public function freeSource() | ||
| { | ||
| if (null !== $this->source) { | ||
| $this->source->clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should perhaps also $this->source = null after calling clear() to ensure the PHP clears the memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d6de81e
This is based on the sfImageMagickAdapter. I opted not to use pdfinfo so that there is no exec() going on in this new adapter.
This has to be set *before* reading an image.
I needed to remove it from three places:
1. From the digital object derivatives setting action. imagick handles
PDF page extraction, not pdfinfo any more. So I changed the error
message to reflect that.
2. From the QubitDigitalObject class. I was using the
sfImageMagickAdapter as a fallback for sfImagickAdapter, so I removed
those fallback cases.
3. From the lime unit tests in the sfThumbnailPlugin.
The unit tests do run successfully in the sfThumbnailPlugin class, but
it's tedious to run them.
In case you want to run them, you need to run these commands in the
Docker container to install gd.
apk add libpng-dev libjpeg-turbo-dev
docker-php-ext-configure gd --with-jpeg
docker-php-ext-install gd
This is because the tests *require* you to have gd installed, and gd is
not installed in the base AtoM image.
And then to run the tests, you invoke the test file directly:
php plugins/sfThumbnailPlugin/test/unit/sfThumbnailTest.php
You'll see this when the tests run:
# Looks like everything went fine.
# Looks like everything went fine.
It's no longer needed! Also, added a note to the README of the plugin since the way the plugin works has changed a lot.
f413650 to
49302a5
Compare

Closes #2163
sfImagickAdapterthat can be used with thesfThumbnailclass.pdfinfo,convert, oridentifywithsfImageMagickAdapter. The old adapter still works, but the new adapter is used ifimagickis installed.imagickif it's installed.Things that are not complete:
Unit tests.(Tests added)Copyright block at the top of the new adapter? I added a new file to a plugin that was not developed at Artefactual, not sure what sort of copyright case that falls under. I'd guess the regular AtoM copyright block but I'm not sure whether the licenses are compatible.(Added the default AtoM copyright block based on CODE_OF_CONDUCT.md)Also of note, I did not implement the fancy thumbnail method that
sfImageMagickAdapterdoes since I couldn't see any place where those custom methods were being used.