use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\ImageToolkit\ImageToolkitBase;
use Drupal\Core\ImageToolkit\ImageToolkitOperationManagerInterface;
+use Drupal\Core\Link;
use Drupal\Core\Url;
+use Drupal\file_mdm\FileMetadataManagerInterface;
+use Drupal\imagemagick\ImagemagickExecArguments;
+use Drupal\imagemagick\ImagemagickExecManagerInterface;
use Drupal\imagemagick\ImagemagickFormatMapperInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ImagemagickToolkit extends ImageToolkitBase {
/**
- * Whether we are running on Windows OS.
+ * EXIF orientation not fetched.
*
- * @var bool
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * parseFileViaIdentify() to parse image files.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2941093
*/
- protected $isWindows;
+ const EXIF_ORIENTATION_NOT_FETCHED = -99;
/**
* The module handler service.
protected $formatMapper;
/**
- * The app root.
+ * The file metadata manager service.
*
- * @var string
+ * @var \Drupal\file_mdm\FileMetadataManagerInterface
*/
- protected $appRoot;
+ protected $fileMetadataManager;
/**
- * The array of command line arguments to be used by 'convert'.
+ * The ImageMagick execution manager service.
*
- * @var string[]
+ * @var \Drupal\imagemagick\ImagemagickExecManagerInterface
*/
- protected $arguments = [];
+ protected $execManager;
+
+ /**
+ * The execution arguments object.
+ *
+ * @var \Drupal\imagemagick\ImagemagickExecArguments
+ */
+ protected $arguments;
/**
* The width of the image.
protected $height;
/**
- * The number of frames of the image, for multi-frame images (e.g. GIF).
+ * The number of frames of the source image, for multi-frame images.
*
* @var int
*/
protected $frames;
/**
- * The local filesystem path to the source image file.
- *
- * @var string
- */
- protected $sourceLocalPath = '';
-
- /**
- * The source image format.
- *
- * @var string
- */
- protected $sourceFormat = '';
-
- /**
- * Keeps a copy of source image EXIF information.
+ * Image orientation retrieved from EXIF information.
*
- * @var array
- */
- protected $exifInfo = [];
-
- /**
- * The image destination URI/path on saving.
- *
- * @var string
+ * @var int
*/
- protected $destination = NULL;
+ protected $exifOrientation;
/**
- * The local filesystem path to the image destination.
+ * The source image colorspace.
*
* @var string
*/
- protected $destinationLocalPath = '';
+ protected $colorspace;
/**
- * The image destination format on saving.
+ * The source image profiles.
*
- * @var string
+ * @var string[]
*/
- protected $destinationFormat = '';
+ protected $profiles = [];
/**
* Constructs an ImagemagickToolkit object.
* The module handler service.
* @param \Drupal\imagemagick\ImagemagickFormatMapperInterface $format_mapper
* The format mapper service.
- * @param string $app_root
- * The app root.
+ * @param \Drupal\file_mdm\FileMetadataManagerInterface $file_metadata_manager
+ * The file metadata manager service.
+ * @param \Drupal\imagemagick\ImagemagickExecManagerInterface $exec_manager
+ * The ImageMagick execution manager service.
*/
- public function __construct(array $configuration, $plugin_id, array $plugin_definition, ImageToolkitOperationManagerInterface $operation_manager, LoggerInterface $logger, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ImagemagickFormatMapperInterface $format_mapper, $app_root) {
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, ImageToolkitOperationManagerInterface $operation_manager, LoggerInterface $logger, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ImagemagickFormatMapperInterface $format_mapper, FileMetadataManagerInterface $file_metadata_manager, ImagemagickExecManagerInterface $exec_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $operation_manager, $logger, $config_factory);
$this->moduleHandler = $module_handler;
$this->formatMapper = $format_mapper;
- $this->appRoot = $app_root;
- $this->isWindows = substr(PHP_OS, 0, 3) === 'WIN';
+ $this->fileMetadataManager = $file_metadata_manager;
+ $this->execManager = $exec_manager;
+ $this->arguments = new ImagemagickExecArguments($this->execManager);
}
/**
$container->get('config.factory'),
$container->get('module_handler'),
$container->get('imagemagick.format_mapper'),
- $container->get('app.root')
+ $container->get('file_metadata_manager'),
+ $container->get('imagemagick.exec_manager')
);
}
'#description' => $this->t('Define the image quality of processed images. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
];
+ // Settings tabs.
+ $form['imagemagick_settings'] = [
+ '#type' => 'vertical_tabs',
+ '#tree' => FALSE,
+ ];
+
// Graphics suite to use.
$form['suite'] = [
'#type' => 'details',
- '#open' => TRUE,
- '#collapsible' => FALSE,
'#title' => $this->t('Graphics package'),
+ '#group' => 'imagemagick_settings',
];
$options = [
- 'imagemagick' => $this->getPackageLabel('imagemagick'),
- 'graphicsmagick' => $this->getPackageLabel('graphicsmagick'),
+ 'imagemagick' => $this->getExecManager()->getPackageLabel('imagemagick'),
+ 'graphicsmagick' => $this->getExecManager()->getPackageLabel('graphicsmagick'),
];
$form['suite']['binaries'] = [
'#type' => 'radios',
'#title' => $this->t('Suite'),
- '#default_value' => $this->getPackage(),
+ '#default_value' => $this->getExecManager()->getPackage(),
'#options' => $options,
'#required' => TRUE,
'#description' => $this->t("Select the graphics package to use."),
'#description' => $this->t('If needed, the path to the package executables (<kbd>convert</kbd>, <kbd>identify</kbd>, <kbd>gm</kbd>, etc.), <b>including</b> the trailing slash/backslash. For example: <kbd>/usr/bin/</kbd> or <kbd>C:\Program Files\ImageMagick-6.3.4-Q16\</kbd>.'),
];
// Version information.
- $status = $this->checkPath($config->get('path_to_binaries'));
+ $status = $this->getExecManager()->checkPath($config->get('path_to_binaries'));
if (empty($status['errors'])) {
$version_info = explode("\n", preg_replace('/\r/', '', Html::escape($status['output'])));
}
$form['suite']['version'] = [
'#type' => 'details',
'#collapsible' => TRUE,
- '#collapsed' => TRUE,
+ '#open' => TRUE,
'#title' => $this->t('Version information'),
'#description' => '<pre>' . implode('<br />', $version_info) . '</pre>',
];
// Image formats.
$form['formats'] = [
'#type' => 'details',
- '#open' => TRUE,
- '#collapsible' => FALSE,
'#title' => $this->t('Image formats'),
- ];
- // Use 'identify' command.
- $form['formats']['use_identify'] = [
- '#type' => 'checkbox',
- '#title' => $this->t('Use "identify"'),
- '#default_value' => $config->get('use_identify'),
- '#description' => $this->t('Use the <kbd>identify</kbd> command to parse image files to determine image format and dimensions. If not selected, the PHP <kbd>getimagesize</kbd> function will be used, BUT this will limit the image formats supported by the toolkit.'),
+ '#group' => 'imagemagick_settings',
];
// Image formats enabled in the toolkit.
$form['formats']['enabled'] = [
'#type' => 'item',
- '#title' => $this->t('Enabled images'),
+ '#title' => $this->t('Currently enabled images'),
'#description' => $this->t("@suite formats: %formats<br />Image file extensions: %extensions", [
'%formats' => implode(', ', $this->formatMapper->getEnabledFormats()),
'%extensions' => Unicode::strtolower(implode(', ', static::getSupportedExtensions())),
- '@suite' => $this->getPackageLabel(),
+ '@suite' => $this->getExecManager()->getPackageLabel(),
]),
];
// Image formats map.
$form['formats']['mapping'] = [
'#type' => 'details',
'#collapsible' => TRUE,
- '#collapsed' => TRUE,
+ '#open' => TRUE,
'#title' => $this->t('Enable/disable image formats'),
'#description' => $this->t("Edit the map below to enable/disable image formats. Enabled image file extensions will be determined by the enabled formats, through their MIME types. More information in the module's README.txt"),
];
];
// Image formats supported by the package.
if (empty($status['errors'])) {
- $this->addArgument('-list format');
- $this->imagemagickExec('convert', $output);
- $this->resetArguments();
+ $this->arguments()->add('-list format', ImagemagickExecArguments::PRE_SOURCE);
+ $output = NULL;
+ $this->getExecManager()->execute('convert', $this->arguments(), $output);
+ $this->arguments()->reset();
$formats_info = implode('<br />', explode("\n", preg_replace('/\r/', '', Html::escape($output))));
$form['formats']['list'] = [
'#type' => 'details',
'#collapsible' => TRUE,
- '#collapsed' => TRUE,
+ '#open' => FALSE,
'#title' => $this->t('Format list'),
- '#description' => $this->t("Supported image formats returned by executing <kbd>'convert -list format'</kbd>. <b>Note:</b> these are the formats supported by the installed @suite executable, <b>not</b> by the toolkit.<br /><br />", ['@suite' => $this->getPackageLabel()]),
+ '#description' => $this->t("Supported image formats returned by executing <kbd>'convert -list format'</kbd>. <b>Note:</b> these are the formats supported by the installed @suite executable, <b>not</b> by the toolkit.<br /><br />", ['@suite' => $this->getExecManager()->getPackageLabel()]),
];
$form['formats']['list']['list'] = [
'#markup' => "<pre>" . $formats_info . "</pre>",
// Execution options.
$form['exec'] = [
'#type' => 'details',
- '#open' => TRUE,
- '#collapsible' => FALSE,
'#title' => $this->t('Execution options'),
+ '#group' => 'imagemagick_settings',
+ ];
+
+ // Use 'identify' command.
+ $form['exec']['use_identify'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Use "identify"'),
+ '#default_value' => $config->get('use_identify'),
+ '#description' => $this->t('<strong>This setting is deprecated and will be removed in the next major release of the Imagemagick module. Leave it enabled to ensure smooth transition.</strong>') . ' ' . $this->t('Use the <kbd>identify</kbd> command to parse image files to determine image format and dimensions. If not selected, the PHP <kbd>getimagesize</kbd> function will be used, BUT this will limit the image formats supported by the toolkit.'),
+ ];
+ // Cache metadata.
+ $configure_link = Link::fromTextAndUrl(
+ $this->t('Configure File Metadata Manager'),
+ Url::fromRoute('file_mdm.settings')
+ );
+ $form['exec']['metadata_caching'] = [
+ '#type' => 'item',
+ '#title' => $this->t("Cache image metadata"),
+ '#description' => $this->t("The File Metadata Manager module allows to cache image metadata. This reduces file I/O and <kbd>shell</kbd> calls. @configure.", [
+ '@configure' => $configure_link->toString(),
+ ]),
];
// Prepend arguments.
$form['exec']['prepend'] = [
- '#type' => 'textfield',
+ '#type' => 'details',
+ '#collapsible' => FALSE,
+ '#open' => TRUE,
'#title' => $this->t('Prepend arguments'),
+ '#description' => $this->t("Use this to add e.g. <kbd><a href=':limit-url'>-limit</a></kbd> or <kbd><a href=':debug-url'>-debug</a></kbd> arguments in front of the others when executing the <kbd>identify</kbd> and <kbd>convert</kbd> commands. Select 'Before source' to execute the arguments before loading the source image.", [
+ ':limit-url' => 'https://www.imagemagick.org/script/command-line-options.php#limit',
+ ':debug-url' => 'https://www.imagemagick.org/script/command-line-options.php#debug',
+ ]),
+ ];
+ $form['exec']['prepend']['container'] = [
+ '#type' => 'container',
+ '#attributes' => [
+ 'class' => ['container-inline'],
+ ],
+ ];
+ $form['exec']['prepend']['container']['prepend'] = [
+ '#type' => 'textfield',
+ '#title' => $this->t('Arguments'),
'#default_value' => $config->get('prepend'),
'#required' => FALSE,
- '#description' => $this->t('Use this to add e.g. <kbd>-limit</kbd> or <kbd>-debug</kbd> arguments in front of the others when executing the <kbd>identify</kbd> and <kbd>convert</kbd> commands.'),
];
+ $form['exec']['prepend']['container']['prepend_pre_source'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Before source'),
+ '#default_value' => $config->get('prepend_pre_source'),
+ ];
+
// Locale.
$form['exec']['locale'] = [
'#type' => 'textfield',
'#title' => $this->t('Locale'),
'#default_value' => $config->get('locale'),
'#required' => FALSE,
- '#description' => $this->t("The locale to be used to prepare the command passed to executables. The default, <kbd>'en_US.UTF-8'</kbd>, should work in most cases. If that is not available on the server, enter another locale. On *nix servers, type <kbd>'locale -a'</kbd> in a shell window to see a list of all locales available."),
+ '#description' => $this->t("The locale to be used to prepare the command passed to executables. The default, <kbd>'en_US.UTF-8'</kbd>, should work in most cases. If that is not available on the server, enter another locale. 'Installed Locales' below provides a list of locales installed on the server."),
+ ];
+ // Installed locales.
+ $locales = $this->getExecManager()->getInstalledLocales();
+ $locales_info = implode('<br />', explode("\n", preg_replace('/\r/', '', Html::escape($locales))));
+ $form['exec']['installed_locales'] = [
+ '#type' => 'details',
+ '#collapsible' => TRUE,
+ '#open' => FALSE,
+ '#title' => $this->t('Installed locales'),
+ '#description' => $this->t("This is the list of all locales available on this server. It is the output of executing <kbd>'locale -a'</kbd> on the operating system."),
+ ];
+ $form['exec']['installed_locales']['list'] = [
+ '#markup' => "<pre>" . $locales_info . "</pre>",
];
// Log warnings.
$form['exec']['log_warnings'] = [
// Advanced image settings.
$form['advanced'] = [
'#type' => 'details',
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
'#title' => $this->t('Advanced image settings'),
+ '#group' => 'imagemagick_settings',
];
$form['advanced']['density'] = [
'#type' => 'checkbox',
return $form;
}
+ /**
+ * Returns the ImageMagick execution manager service.
+ *
+ * @return \Drupal\imagemagick\ImagemagickExecManagerInterface
+ * The ImageMagick execution manager service.
+ */
+ public function getExecManager() {
+ return $this->execManager;
+ }
+
/**
* Gets the binaries package in use.
*
* @return string
* The default package ('imagemagick'|'graphicsmagick'), or the $package
* argument.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecManagerInterface::getPackage() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function getPackage($package = NULL) {
- if ($package === NULL) {
- $package = $this->configFactory->get('imagemagick.settings')->get('binaries');
- }
- return $package;
+ @trigger_error('getPackage() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecManagerInterface::getPackage() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->getExecManager()->getPackage($package);
}
/**
* @return string
* A translated label of the binaries package in use, or the $package
* argument.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecManagerInterface::getPackageLabel() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function getPackageLabel($package = NULL) {
- switch ($this->getPackage($package)) {
- case 'imagemagick':
- return $this->t('ImageMagick');
-
- case 'graphicsmagick':
- return $this->t('GraphicsMagick');
-
- default:
- return $package;
-
- }
+ @trigger_error('getPackageLabel() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecManagerInterface::getPackageLabel() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->getExecManager()->getPackageLabel($package);
}
/**
* - output: The shell output of 'convert -version', if any.
* - errors: A list of error messages indicating if the executable could
* not be found or executed.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecManagerInterface::checkPath() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function checkPath($path, $package = NULL) {
- $status = [
- 'output' => '',
- 'errors' => [],
- ];
-
- // Execute gm or convert based on settings.
- $package = $package ?: $this->getPackage();
- $binary = $package === 'imagemagick' ? 'convert' : 'gm';
- $executable = $this->getExecutable($binary, $path);
-
- // If a path is given, we check whether the binary exists and can be
- // invoked.
- if (!empty($path)) {
- // Check whether the given file exists.
- if (!is_file($executable)) {
- $status['errors'][] = $this->t('The @suite executable %file does not exist.', ['@suite' => $this->getPackageLabel($package), '%file' => $executable]);
- }
- // If it exists, check whether we can execute it.
- elseif (!is_executable($executable)) {
- $status['errors'][] = $this->t('The @suite file %file is not executable.', ['@suite' => $this->getPackageLabel($package), '%file' => $executable]);
- }
- }
-
- // In case of errors, check for open_basedir restrictions.
- if ($status['errors'] && ($open_basedir = ini_get('open_basedir'))) {
- $status['errors'][] = $this->t('The PHP <a href=":php-url">open_basedir</a> security restriction is set to %open-basedir, which may prevent to locate the @suite executable.', [
- '@suite' => $this->getPackageLabel($package),
- '%open-basedir' => $open_basedir,
- ':php-url' => 'http://php.net/manual/en/ini.core.php#ini.open-basedir',
- ]);
- }
-
- // Unless we had errors so far, try to invoke convert.
- if (!$status['errors']) {
- $error = NULL;
- $this->runOsShell($executable, '-version', $package, $status['output'], $error);
- if ($error !== '') {
- // $error normally needs check_plain(), but file system errors on
- // Windows use a unknown encoding. check_plain() would eliminate the
- // entire string.
- $status['errors'][] = $error;
- }
- }
-
- return $status;
+ @trigger_error('checkPath() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecManagerInterface::checkPath() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->getExecManager()->checkPath($path, $package);
}
/**
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
try {
// Check that the format map contains valid YAML.
- $image_formats = Yaml::decode($form_state->getValue(['imagemagick', 'formats', 'mapping', 'image_formats']));
+ $image_formats = Yaml::decode($form_state->getValue([
+ 'imagemagick', 'formats', 'mapping', 'image_formats',
+ ]));
// Validate the enabled image formats.
$errors = $this->formatMapper->validateMap($image_formats);
if ($errors) {
// it will prevent the entire image toolkit selection form from being
// submitted.
if ($form_state->getValue(['image_toolkit']) === 'imagemagick') {
- $status = $this->checkPath($form_state->getValue(['imagemagick', 'suite', 'path_to_binaries']), $form_state->getValue(['imagemagick', 'suite', 'binaries']));
+ $status = $this->getExecManager()->checkPath($form_state->getValue([
+ 'imagemagick', 'suite', 'path_to_binaries',
+ ]), $form_state->getValue(['imagemagick', 'suite', 'binaries']));
if ($status['errors']) {
$form_state->setErrorByName('imagemagick][suite][path_to_binaries', new FormattableMarkup(implode('<br />', $status['errors']), []));
}
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
- $this->configFactory->getEditable('imagemagick.settings')
- ->set('quality', $form_state->getValue(['imagemagick', 'quality']))
- ->set('binaries', $form_state->getValue(['imagemagick', 'suite', 'binaries']))
- ->set('path_to_binaries', $form_state->getValue(['imagemagick', 'suite', 'path_to_binaries']))
- ->set('use_identify', $form_state->getValue(['imagemagick', 'formats', 'use_identify']))
- ->set('image_formats', Yaml::decode($form_state->getValue(['imagemagick', 'formats', 'mapping', 'image_formats'])))
- ->set('prepend', $form_state->getValue(['imagemagick', 'exec', 'prepend']))
- ->set('locale', $form_state->getValue(['imagemagick', 'exec', 'locale']))
- ->set('log_warnings', (bool) $form_state->getValue(['imagemagick', 'exec', 'log_warnings']))
- ->set('debug', $form_state->getValue(['imagemagick', 'exec', 'debug']))
- ->set('advanced.density', $form_state->getValue(['imagemagick', 'advanced', 'density']))
- ->set('advanced.colorspace', $form_state->getValue(['imagemagick', 'advanced', 'colorspace']))
- ->set('advanced.profile', $form_state->getValue(['imagemagick', 'advanced', 'profile']))
- ->save();
+ $config = $this->configFactory->getEditable('imagemagick.settings');
+ $config
+ ->set('quality', (int) $form_state->getValue([
+ 'imagemagick', 'quality',
+ ]))
+ ->set('binaries', (string) $form_state->getValue([
+ 'imagemagick', 'suite', 'binaries',
+ ]))
+ ->set('path_to_binaries', (string) $form_state->getValue([
+ 'imagemagick', 'suite', 'path_to_binaries',
+ ]))
+ ->set('use_identify', (bool) $form_state->getValue([
+ 'imagemagick', 'exec', 'use_identify',
+ ]))
+ ->set('image_formats', Yaml::decode($form_state->getValue([
+ 'imagemagick', 'formats', 'mapping', 'image_formats',
+ ])))
+ ->set('prepend', (string) $form_state->getValue([
+ 'imagemagick', 'exec', 'prepend', 'container', 'prepend',
+ ]))
+ ->set('prepend_pre_source', (bool) $form_state->getValue([
+ 'imagemagick', 'exec', 'prepend', 'container', 'prepend_pre_source',
+ ]))
+ ->set('locale', (string) $form_state->getValue([
+ 'imagemagick', 'exec', 'locale',
+ ]))
+ ->set('log_warnings', (bool) $form_state->getValue([
+ 'imagemagick', 'exec', 'log_warnings',
+ ]))
+ ->set('debug', (bool) $form_state->getValue([
+ 'imagemagick', 'exec', 'debug',
+ ]))
+ ->set('advanced.density', (int) $form_state->getValue([
+ 'imagemagick', 'advanced', 'density',
+ ]))
+ ->set('advanced.colorspace', (string) $form_state->getValue([
+ 'imagemagick', 'advanced', 'colorspace',
+ ]))
+ ->set('advanced.profile', (string) $form_state->getValue([
+ 'imagemagick', 'advanced', 'profile',
+ ]));
+ $config->save();
}
/**
return ((bool) $this->getMimeType());
}
+ /**
+ * {@inheritdoc}
+ */
+ public function setSource($source) {
+ parent::setSource($source);
+ $this->arguments()->setSource($source);
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSource() {
+ return $this->arguments()->getSource();
+ }
+
/**
* Gets the local filesystem path to the image file.
*
* @return string
* A filesystem path.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ::ensureSourceLocalPath() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function getSourceLocalPath() {
- return $this->sourceLocalPath;
+ @trigger_error('getSourceLocalPath() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ::ensureSourceLocalPath() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->ensureSourceLocalPath();
+ }
+
+ /**
+ * Ensures that the local filesystem path to the image file exists.
+ *
+ * @return string
+ * A filesystem path.
+ */
+ public function ensureSourceLocalPath() {
+ // If sourceLocalPath is NULL, then ensure it is prepared. This can
+ // happen if image was identified via cached metadata: the cached data are
+ // available, but the temp file path is not resolved, or even the temp file
+ // could be missing if it was copied locally from a remote file system.
+ if (!$this->arguments()->getSourceLocalPath() && $this->getSource()) {
+ $this->moduleHandler->alter('imagemagick_pre_parse_file', $this->arguments);
+ }
+ return $this->arguments()->getSourceLocalPath();
}
/**
* A filesystem path.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::setSourceLocalPath() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function setSourceLocalPath($path) {
- $this->sourceLocalPath = $path;
+ @trigger_error('setSourceLocalPath() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::setSourceLocalPath() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ $this->arguments()->setSourceLocalPath($path);
return $this;
}
*
* @return string
* The source image format.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::getSourceFormat() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function getSourceFormat() {
- return $this->sourceFormat;
+ @trigger_error('getSourceFormat() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::getSourceFormat() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->arguments()->getSourceFormat();
}
/**
* The image format.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::setSourceFormat() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function setSourceFormat($format) {
- $this->sourceFormat = $this->formatMapper->isFormatEnabled($format) ? $format : '';
+ @trigger_error('setSourceFormat() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::setSourceFormat() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ $this->arguments()->setSourceFormat($format);
return $this;
}
* The image file extension.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::setSourceFormatFromExtension() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function setSourceFormatFromExtension($extension) {
- $format = $this->formatMapper->getFormatFromExtension($extension);
- $this->sourceFormat = $format ?: '';
+ @trigger_error('setSourceFormatFromExtension() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::setSourceFormatFromExtension() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ $this->arguments()->setSourceFormatFromExtension($extension);
return $this;
}
/**
* Gets the source EXIF orientation.
*
- * @return integer
+ * @return int
* The source EXIF orientation.
*/
public function getExifOrientation() {
- if (empty($this->exifInfo)) {
- $this->parseExifData();
+ if ($this->exifOrientation === static::EXIF_ORIENTATION_NOT_FETCHED) {
+ if ($this->getSource() !== NULL) {
+ $file_md = $this->fileMetadataManager->uri($this->getSource());
+ if ($file_md->getLocalTempPath() === NULL) {
+ $file_md->setLocalTempPath($this->ensureSourceLocalPath());
+ }
+ $orientation = $file_md->getMetadata('exif', 'Orientation');
+ $this->setExifOrientation(isset($orientation['value']) ? $orientation['value'] : NULL);
+ }
+ else {
+ $this->setExifOrientation(NULL);
+ }
}
- return isset($this->exifInfo['Orientation']) ? $this->exifInfo['Orientation'] : NULL;
+ return $this->exifOrientation;
}
/**
* Sets the source EXIF orientation.
*
- * @param integer|null $exif_orientation
+ * @param int|null $exif_orientation
* The EXIF orientation.
*
* @return $this
*/
public function setExifOrientation($exif_orientation) {
- $this->exifInfo['Orientation'] = !empty($exif_orientation) ? ((int) $exif_orientation !== 0 ? (int) $exif_orientation : NULL) : NULL;
+ $this->exifOrientation = $exif_orientation ? (int) $exif_orientation : NULL;
+ return $this;
+ }
+
+ /**
+ * Gets the source colorspace.
+ *
+ * @return string
+ * The source colorspace.
+ */
+ public function getColorspace() {
+ return $this->colorspace;
+ }
+
+ /**
+ * Sets the source colorspace.
+ *
+ * @param string $colorspace
+ * The image colorspace.
+ *
+ * @return $this
+ */
+ public function setColorspace($colorspace) {
+ $this->colorspace = Unicode::strtoupper($colorspace);
+ return $this;
+ }
+
+ /**
+ * Gets the source profiles.
+ *
+ * @return string[]
+ * The source profiles.
+ */
+ public function getProfiles() {
+ return $this->profiles;
+ }
+
+ /**
+ * Sets the source profiles.
+ *
+ * @param array $profiles
+ * The image profiles.
+ *
+ * @return $this
+ */
+ public function setProfiles(array $profiles) {
+ $this->profiles = $profiles;
return $this;
}
/**
* Gets the source image number of frames.
*
- * @return integer
+ * @return int
* The number of frames of the image.
*/
public function getFrames() {
/**
* Sets the source image number of frames.
*
- * @param integer|null $frames
+ * @param int|null $frames
* The number of frames of the image.
*
* @return $this
*
* @return string
* The image destination URI/path.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::getDestination() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function getDestination() {
- return $this->destination;
+ @trigger_error('getDestination() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::getDestination() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->arguments()->getDestination();
}
/**
* The image destination URI/path.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::setDestination() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function setDestination($destination) {
- $this->destination = $destination;
+ @trigger_error('setDestination() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::setDestination() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ $this->arguments()->setDestination($destination);
return $this;
}
*
* @return string
* A filesystem path.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::getDestinationLocalPath() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function getDestinationLocalPath() {
- return $this->destinationLocalPath;
+ @trigger_error('getDestinationLocalPath() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::getDestinationLocalPath() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->arguments()->getDestinationLocalPath();
}
/**
* A filesystem path.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::setDestinationLocalPath() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function setDestinationLocalPath($path) {
- $this->destinationLocalPath = $path;
+ @trigger_error('setDestinationLocalPath() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::setDestinationLocalPath() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ $this->arguments()->setDestinationLocalPath($path);
return $this;
}
*
* @return string
* The image destination format.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::getDestinationFormat() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function getDestinationFormat() {
- return $this->destinationFormat;
+ @trigger_error('getDestinationFormat() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::getDestinationFormat() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ return $this->arguments()->getDestinationFormat();
}
/**
* The image destination format.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::setDestinationFormat() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function setDestinationFormat($format) {
- $this->destinationFormat = $this->formatMapper->isFormatEnabled($format) ? $format : '';
+ @trigger_error('setDestinationFormat() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::setDestinationFormat() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ $this->arguments()->setDestinationFormat($this->formatMapper->isFormatEnabled($format) ? $format : '');
return $this;
}
* The destination image file extension.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImagemagickExecArguments::setDestinationFormatFromExtension() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2938375
*/
public function setDestinationFormatFromExtension($extension) {
- $format = $this->formatMapper->getFormatFromExtension($extension);
- $this->destinationFormat = $format ?: '';
+ @trigger_error('setDestinationFormatFromExtension() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImagemagickExecArguments::setDestinationFormatFromExtension() instead. See https://www.drupal.org/project/imagemagick/issues/2938375.', E_USER_DEPRECATED);
+ $this->arguments()->setDestinationFormatFromExtension($extension);
return $this;
}
* {@inheritdoc}
*/
public function getMimeType() {
- return $this->formatMapper->getMimeTypeFromFormat($this->getSourceFormat());
+ return $this->formatMapper->getMimeTypeFromFormat($this->arguments()->getSourceFormat());
+ }
+
+ /**
+ * Returns the current ImagemagickExecArguments object.
+ *
+ * @return \Drupal\imagemagick\ImagemagickExecArguments
+ * The current ImagemagickExecArguments object.
+ */
+ public function arguments() {
+ return $this->arguments;
}
/**
*
* @return string[]
* The array of command line arguments.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ::arguments()
+ * instead, using ImagemagickExecArguments methods to manipulate arguments.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2925780
*/
public function getArguments() {
- return $this->arguments ?: [];
+ @trigger_error('getArguments() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ::arguments() instead, using ImagemagickExecArguments methods to manipulate arguments. See https://www.drupal.org/project/imagemagick/issues/2925780.', E_USER_DEPRECATED);
+ return $this->arguments()->getArguments();
+ }
+
+ /**
+ * Gets the command line arguments string for the binary.
+ *
+ * Removes any argument used internally within the toolkit.
+ *
+ * @return string
+ * The string of command line arguments.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::toString() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2925780
+ */
+ public function getStringForBinary() {
+ @trigger_error('getStringForBinary() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::toString() instead. See https://www.drupal.org/project/imagemagick/issues/2925780.', E_USER_DEPRECATED);
+ return $this->arguments()->getStringForBinary();
}
/**
* The command line argument to be added.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::add() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2925780
*/
public function addArgument($arg) {
- $this->arguments[] = $arg;
+ @trigger_error('addArgument() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::add() instead. See https://www.drupal.org/project/imagemagick/issues/2925780.', E_USER_DEPRECATED);
+ $this->arguments()->addArgument($arg);
return $this;
}
* The command line argument to be prepended.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::add() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2925780
*/
public function prependArgument($arg) {
- array_unshift($this->arguments, $arg);
+ @trigger_error('prependArgument() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::add() instead. See https://www.drupal.org/project/imagemagick/issues/2925780.', E_USER_DEPRECATED);
+ $this->arguments()->prependArgument($arg);
return $this;
}
* @return bool
* Returns the array key for the argument if it is found in the array,
* FALSE otherwise.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::find() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2925780
*/
public function findArgument($arg) {
- foreach ($this->getArguments() as $i => $a) {
- if (strpos($a, $arg) === 0) {
- return $i;
- }
- }
- return FALSE;
+ @trigger_error('findArgument() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::find() instead. See https://www.drupal.org/project/imagemagick/issues/2925780.', E_USER_DEPRECATED);
+ return $this->arguments()->findArgument($arg);
}
/**
* The index of the command line argument to be removed.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::remove() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2936615
*/
public function removeArgument($index) {
- if (isset($this->arguments[$index])) {
- unset($this->arguments[$index]);
- }
+ @trigger_error('removeArgument() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::remove() instead. See https://www.drupal.org/project/imagemagick/issues/2936615.', E_USER_DEPRECATED);
+ $this->arguments()->removeArgument($index);
return $this;
}
* Resets the command line arguments.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::reset() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2936615
*/
public function resetArguments() {
- $this->arguments = [];
+ @trigger_error('resetArguments() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::reset() instead. See https://www.drupal.org/project/imagemagick/issues/2936615.', E_USER_DEPRECATED);
+ $this->arguments()->resetArguments();
return $this;
}
* Returns the count of command line arguments.
*
* @return $this
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::find() instead, then count the result.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2936615
*/
public function countArguments() {
- return count($this->arguments);
+ @trigger_error('countArguments() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::find() instead, then count the result. See https://www.drupal.org/project/imagemagick/issues/2936615.', E_USER_DEPRECATED);
+ return $this->arguments()->countArguments();
}
/**
* Escapes a string.
*
- * PHP escapeshellarg() drops non-ascii characters, this is a replacement.
- *
- * Stop-gap replacement until core issue #1561214 has been solved. Solution
- * proposed in #1502924-8.
- *
- * PHP escapeshellarg() on Windows also drops % (percentage sign) characters.
- * We prevent this by replacing it with a pattern that should be highly
- * unlikely to appear in the string itself and does not contain any
- * "dangerous" character at all (very wide definition of dangerous). After
- * escaping we replace that pattern back with a % character.
- *
* @param string $arg
* The string to escape.
*
* @return string
- * An escaped string for use in the ::imagemagickExec method.
+ * An escaped string for use in the
+ * ImagemagickExecManagerInterface::execute method.
+ *
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * ImageMagickExecArguments::escape() instead.
+ *
+ * @see https://www.drupal.org/project/imagemagick/issues/2936680
*/
public function escapeShellArg($arg) {
- static $percentage_sign_replace_pattern = '1357902468IMAGEMAGICKPERCENTSIGNPATTERN8642097531';
-
- // Put the configured locale in a static to avoid multiple config get calls
- // in the same request.
- static $config_locale;
-
- if (!isset($config_locale)) {
- $config_locale = $this->configFactory->get('imagemagick.settings')->get('locale');
- if (empty($config_locale)) {
- $config_locale = FALSE;
- }
- }
-
- if ($this->isWindows) {
- // Temporarily replace % characters.
- $arg = str_replace('%', $percentage_sign_replace_pattern, $arg);
- }
-
- // If no locale specified in config, return with standard.
- if ($config_locale === FALSE) {
- $arg_escaped = escapeshellarg($arg);
- }
- else {
- // Get the current locale.
- $current_locale = setlocale(LC_CTYPE, 0);
- if ($current_locale != $config_locale) {
- // Temporarily swap the current locale with the configured one.
- setlocale(LC_CTYPE, $config_locale);
- $arg_escaped = escapeshellarg($arg);
- setlocale(LC_CTYPE, $current_locale);
- }
- else {
- $arg_escaped = escapeshellarg($arg);
- }
- }
-
- // Get our % characters back.
- if ($this->isWindows) {
- $arg_escaped = str_replace($percentage_sign_replace_pattern, '%', $arg_escaped);
- }
-
- return $arg_escaped;
+ @trigger_error('escapeShellArg() is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use ImageMagickExecArguments::escape() instead. See https://www.drupal.org/project/imagemagick/issues/2936680.', E_USER_DEPRECATED);
+ return $this->getExecManager()->escapeShellArg($arg);
}
/**
* {@inheritdoc}
*/
public function save($destination) {
- $this->setDestination($destination);
+ $this->arguments()->setDestination($destination);
if ($ret = $this->convert()) {
// Allow modules to alter the destination file.
- $this->moduleHandler->alter('imagemagick_post_save', $this);
+ $this->moduleHandler->alter('imagemagick_post_save', $this->arguments);
// Reset local path to allow saving to other file.
- $this->setDestinationLocalPath('');
+ $this->arguments()->setDestinationLocalPath('');
}
return $ret;
}
* {@inheritdoc}
*/
public function parseFile() {
- // Allow modules to alter the source file.
- $this->moduleHandler->alter('imagemagick_pre_parse_file', $this);
if ($this->configFactory->get('imagemagick.settings')->get('use_identify')) {
return $this->parseFileViaIdentify();
}
* TRUE if the file could be found and is an image, FALSE otherwise.
*/
protected function parseFileViaIdentify() {
- // Prepare the -format argument according to the graphics package in use.
- switch ($this->getPackage()) {
- case 'imagemagick':
- $this->addArgument('-format ' . $this->escapeShellArg("format:%[magick]|width:%[width]|height:%[height]|exif_orientation:%[EXIF:Orientation]\\n"));
- break;
-
- case 'graphicsmagick':
- $this->addArgument('-format ' . $this->escapeShellArg("format:%m|width:%w|height:%h|exif_orientation:%[EXIF:Orientation]\\n"));
- break;
-
+ // Get 'imagemagick_identify' metadata for this image. The file metadata
+ // plugin will fetch it from the file via the ::identify() method if data
+ // is not already available.
+ $file_md = $this->fileMetadataManager->uri($this->getSource());
+ $data = $file_md->getMetadata('imagemagick_identify');
+
+ // No data, return.
+ if (!$data) {
+ return FALSE;
}
- if ($identify_output = $this->identify()) {
- $frames = explode("\n", $identify_output);
-
- // Remove empty items at the end of the array.
- while (empty($frames[count($frames) - 1])) {
- array_pop($frames);
- }
-
- // If remaining items are more than one, we have a multi-frame image.
- if (count($frames) > 1) {
- $this->setFrames(count($frames));
- }
-
- // Take information from the first frame.
- $info = explode('|', $frames[0]);
- $data = [];
- foreach ($info as $item) {
- list($key, $value) = explode(':', $item);
- $data[trim($key)] = trim($value);
- }
- $format = isset($data['format']) ? $data['format'] : NULL;
- if ($this->formatMapper->isFormatEnabled($format)) {
- $this
- ->setSourceFormat($format)
- ->setWidth((int) $data['width'])
- ->setHeight((int) $data['height'])
- ->setExifOrientation($data['exif_orientation']);
- return TRUE;
- }
+ // Sets the local file path to the one retrieved by identify if available.
+ if ($source_local_path = $file_md->getMetadata('imagemagick_identify', 'source_local_path')) {
+ $this->arguments()->setSourceLocalPath($source_local_path);
}
- return FALSE;
- }
- /**
- * Parses the image file using the PHP getimagesize() function.
- *
- * @return bool
- * TRUE if the file could be found and is an image, FALSE otherwise.
- */
- protected function parseFileViaGetImageSize() {
- if ($data = @getimagesize($this->getSourceLocalPath())) {
- $format = $this->formatMapper->getFormatFromExtension(image_type_to_extension($data[2], FALSE));
- if ($format) {
- $this
- ->setSourceFormat($format)
- ->setWidth($data[0])
- ->setHeight($data[1]);
- return TRUE;
+ // Process parsed data from the first frame.
+ $format = $file_md->getMetadata('imagemagick_identify', 'format');
+ if ($this->formatMapper->isFormatEnabled($format)) {
+ $this
+ ->setWidth((int) $file_md->getMetadata('imagemagick_identify', 'width'))
+ ->setHeight((int) $file_md->getMetadata('imagemagick_identify', 'height'))
+ ->setExifOrientation($file_md->getMetadata('imagemagick_identify', 'exif_orientation'))
+ ->setFrames($file_md->getMetadata('imagemagick_identify', 'frames_count'));
+ $this->arguments()
+ ->setSourceFormat($format);
+ // Only Imagemagick allows to get colorspace and profiles information
+ // via 'identify'.
+ if ($this->getExecManager()->getPackage() === 'imagemagick') {
+ $this->setColorspace($file_md->getMetadata('imagemagick_identify', 'colorspace'));
+ $this->setProfiles($file_md->getMetadata('imagemagick_identify', 'profiles'));
}
- };
- return FALSE;
- }
-
- /**
- * Parses the image file EXIF data using the PHP read_exif_data() function.
- *
- * @return $this
- */
- protected function parseExifData() {
- $continue = TRUE;
- // Test to see if EXIF is supported by the image format.
- $mime_type = $this->getMimeType();
- if (!in_array($mime_type, ['image/jpeg', 'image/tiff'])) {
- // Not an EXIF enabled image.
- $continue = FALSE;
- }
- $local_path = $this->getSourceLocalPath();
- if ($continue && empty($local_path)) {
- // No file path available. Most likely a new image from scratch.
- $continue = FALSE;
- }
- if ($continue && !function_exists('exif_read_data')) {
- // No PHP EXIF extension enabled, return.
- $this->logger->error('The PHP EXIF extension is not installed. The \'imagemagick\' toolkit is unable to automatically determine image orientation.');
- $continue = FALSE;
- }
- if ($continue && ($exif_data = @exif_read_data($this->getSourceLocalPath()))) {
- $this->exifInfo = $exif_data;
- return $this;
+ return TRUE;
}
- $this->setExifOrientation(NULL);
- return $this;
- }
- /**
- * Calls the identify executable on the specified file.
- *
- * @return bool
- * TRUE if the file could be identified, FALSE otherwise.
- */
- protected function identify() {
- // Allow modules to alter the command line parameters.
- $command = 'identify';
- $this->moduleHandler->alter('imagemagick_arguments', $this, $command);
-
- // Executes the command.
- $output = NULL;
- $ret = $this->imagemagickExec($command, $output);
- $this->resetArguments();
- return ($ret === TRUE) ? $output : FALSE;
+ return FALSE;
}
/**
- * Calls the convert executable with the specified arguments.
+ * Parses the image file using the file metadata 'getimagesize' plugin.
*
* @return bool
- * TRUE if the file could be converted, FALSE otherwise.
- */
- protected function convert() {
- // Allow modules to alter the command line parameters.
- $command = 'convert';
- $this->moduleHandler->alter('imagemagick_arguments', $this, $command);
-
- // Executes the command.
- return $this->imagemagickExec($command) === TRUE ? file_exists($this->getDestinationLocalPath()) : FALSE;
- }
-
- /**
- * Executes the convert executable as shell command.
+ * TRUE if the file could be found and is an image, FALSE otherwise.
*
- * @param string $command
- * The executable to run.
- * @param string $command_args
- * A string containing arguments to pass to the command, which must have
- * been passed through $this->escapeShellArg() already.
- * @param string &$output
- * (optional) A variable to assign the shell stdout to, passed by reference.
- * @param string &$error
- * (optional) A variable to assign the shell stderr to, passed by reference.
- * @param string $path
- * (optional) A custom file path to the executable binary.
+ * @deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use
+ * parseFileViaIdentify() instead.
*
- * @return mixed
- * The return value depends on the shell command result:
- * - Boolean TRUE if the command succeeded.
- * - Boolean FALSE if the shell process could not be executed.
- * - Error exit status code integer returned by the executable.
+ * @see https://www.drupal.org/project/imagemagick/issues/2938377
*/
- protected function imagemagickExec($command, &$output = NULL, &$error = NULL, $path = NULL) {
- switch ($command) {
- case 'convert':
- $binary = $this->getPackage() === 'imagemagick' ? 'convert' : 'gm';
- break;
-
- case 'identify':
- $binary = $this->getPackage() === 'imagemagick' ? 'identify' : 'gm';
- break;
-
- }
- $cmd = $this->getExecutable($binary, $path);
-
- if ($source_path = $this->getSourceLocalPath()) {
- $source_path = $this->escapeShellArg($source_path);
- }
-
- if ($destination_path = $this->getDestinationLocalPath()) {
- $destination_path = $this->escapeShellArg($destination_path);
- // If the format of the derivative image has to be changed, concatenate
- // the new image format and the destination path, delimited by a colon.
- // @see http://www.imagemagick.org/script/command-line-processing.php#output
- if (($format = $this->getDestinationFormat()) !== '') {
- $destination_path = $format . ':' . $destination_path;
- }
- }
-
- switch($command) {
- case 'identify':
- switch($this->getPackage()) {
- case 'imagemagick':
- // ImageMagick syntax:
- // identify [arguments] source
- $cmdline = implode(' ', $this->getArguments()) . ' ' . $source_path;
- break;
-
- case 'graphicsmagick':
- // GraphicsMagick syntax:
- // gm identify [arguments] source
- $cmdline = 'identify ' . implode(' ', $this->getArguments()) . ' ' . $source_path;
- break;
-
- }
- break;
-
- case 'convert':
- switch($this->getPackage()) {
- case 'imagemagick':
- // ImageMagick syntax:
- // convert input [arguments] output
- // @see http://www.imagemagick.org/Usage/basics/#cmdline
- $cmdline = $source_path . ' ' . implode(' ', $this->getArguments()) . ' ' . $destination_path;
- break;
-
- case 'graphicsmagick':
- // GraphicsMagick syntax:
- // gm convert [arguments] input output
- // @see http://www.graphicsmagick.org/GraphicsMagick.html
- $cmdline = 'convert ' . implode(' ', $this->getArguments()) . ' ' . $source_path . ' ' . $destination_path;
- break;
+ protected function parseFileViaGetImageSize() {
+ @trigger_error('Image file parsing via \'getimagesize\' is deprecated in 8.x-2.3, will be removed in 8.x-3.0. Use parsing via \'identify\' instead. See https://www.drupal.org/project/imagemagick/issues/2938377.', E_USER_DEPRECATED);
+ // Allow modules to alter the source file.
+ $this->moduleHandler->alter('imagemagick_pre_parse_file', $this->arguments);
- }
- break;
+ // Get 'getimagesize' metadata for this image.
+ $file_md = $this->fileMetadataManager->uri($this->getSource());
+ $data = $file_md->getMetadata('getimagesize');
+ // No data, return.
+ if (!$data) {
+ return FALSE;
}
- $return_code = $this->runOsShell($cmd, $cmdline, $this->getPackage(), $output, $error);
-
- if ($return_code !== FALSE) {
- // If the executable returned a non-zero code, log to the watchdog.
- if ($return_code != 0) {
- if ($error === '') {
- // If there is no error message, and allowed in config, log a
- // warning.
- if ($this->configFactory->get('imagemagick.settings')->get('log_warnings') === TRUE) {
- $this->logger->warning("@suite returned with code @code [command: @command @cmdline]", [
- '@suite' => $this->getPackageLabel(),
- '@code' => $return_code,
- '@command' => $cmd,
- '@cmdline' => $cmdline,
- ]);
- }
- }
- else {
- // Log $error with context information.
- $this->logger->error("@suite error @code: @error [command: @command @cmdline]", [
- '@suite' => $this->getPackageLabel(),
- '@code' => $return_code,
- '@error' => $error,
- '@command' => $cmd,
- '@cmdline' => $cmdline,
- ]);
- }
- // Executable exited with an error code, return it.
- return $return_code;
- }
-
- // The shell command was executed successfully.
+ // Process parsed data.
+ $format = $this->formatMapper->getFormatFromExtension(image_type_to_extension($data[2], FALSE));
+ if ($format) {
+ $this
+ ->setWidth($data[0])
+ ->setHeight($data[1])
+ // 'getimagesize' cannot provide information on number of frames in an
+ // image and EXIF orientation, so set to defaults.
+ ->setExifOrientation(static::EXIF_ORIENTATION_NOT_FETCHED)
+ ->setFrames(NULL);
+ $this->arguments()
+ ->setSourceFormat($format);
return TRUE;
}
- // The shell command could not be executed.
- return FALSE;
- }
-
- /**
- * Executes a command on the operating system.
- *
- * @param string $command
- * The command to run.
- * @param string $arguments
- * The arguments of the command to run.
- * @param string $id
- * An identifier for the process to be spawned on the operating system.
- * @param string &$output
- * (optional) A variable to assign the shell stdout to, passed by
- * reference.
- * @param string &$error
- * (optional) A variable to assign the shell stderr to, passed by
- * reference.
- *
- * @return int|bool
- * The operating system returned code, or FALSE if it was not possible to
- * execute the command.
- */
- protected function runOsShell($command, $arguments, $id, &$output = NULL, &$error = NULL) {
- if ($this->isWindows) {
- // Use Window's start command with the /B flag to make the process run in
- // the background and avoid a shell command line window from showing up.
- // @see http://us3.php.net/manual/en/function.exec.php#56599
- // Use /D to run the command from PHP's current working directory so the
- // file paths don't have to be absolute.
- $command = 'start "' . $id . '" /D ' . $this->escapeShellArg($this->appRoot) . ' /B ' . $this->escapeShellArg($command);
- }
- $command_line = $command . ' ' . $arguments;
-
- // Executes the command on the OS via proc_open().
- $descriptors = [
- // This is stdin.
- 0 => ['pipe', 'r'],
- // This is stdout.
- 1 => ['pipe', 'w'],
- // This is stderr.
- 2 => ['pipe', 'w'],
- ];
-
- if ($h = proc_open($command_line, $descriptors, $pipes, $this->appRoot)) {
- $output = '';
- while (!feof($pipes[1])) {
- $output .= fgets($pipes[1]);
- }
- $output = utf8_encode($output);
- $error = '';
- while (!feof($pipes[2])) {
- $error .= fgets($pipes[2]);
- }
- $error = utf8_encode($error);
- fclose($pipes[0]);
- fclose($pipes[1]);
- fclose($pipes[2]);
- $return_code = proc_close($h);
- }
- else {
- $return_code = FALSE;
- }
- // Process debugging information if required.
- if ($this->configFactory->get('imagemagick.settings')->get('debug')) {
- $this->debugMessage('@suite command: <pre>@raw</pre>', [
- '@suite' => $this->getPackageLabel($id),
- '@raw' => print_r($command_line, TRUE),
- ]);
- if ($output !== '') {
- $this->debugMessage('@suite output: <pre>@raw</pre>', [
- '@suite' => $this->getPackageLabel($id),
- '@raw' => print_r($output, TRUE),
- ]);
- }
- if ($error !== '') {
- $this->debugMessage('@suite error @return_code: <pre>@raw</pre>', [
- '@suite' => $this->getPackageLabel($id),
- '@return_code' => $return_code,
- '@raw' => print_r($error, TRUE),
- ]);
- }
- }
-
- return $return_code;
+ return FALSE;
}
/**
- * Logs a debug message, and shows it on the screen for authorized users.
+ * Calls the convert executable with the specified arguments.
*
- * @param string $message
- * The debug message.
- * @param string[] $context
- * Context information.
+ * @return bool
+ * TRUE if the file could be converted, FALSE otherwise.
*/
- public function debugMessage($message, array $context) {
- $this->logger->debug($message, $context);
- if (\Drupal::currentUser()->hasPermission('administer site configuration')) {
- // Strips raw text longer than 10 lines to optimize displaying.
- if (isset($context['@raw'])) {
- $raw = explode("\n", $context['@raw']);
- if (count($raw) > 10) {
- $tmp = [];
- for ($i = 0; $i < 9; $i++) {
- $tmp[] = $raw[$i];
- }
- $tmp[] = (string) $this->t('[Further text stripped. The watchdog log has the full text.]');
- $context['@raw'] = implode("\n", $tmp);
- }
- }
- drupal_set_message($this->t($message, $context), 'status', TRUE);
- }
- }
+ protected function convert() {
+ $config = $this->configFactory->get('imagemagick.settings');
- /**
- * Returns the full path to the executable.
- *
- * @param string $binary
- * The program to execute, typically 'convert', 'identify' or 'gm'.
- * @param string $path
- * (optional) A custom path to the folder of the executable. When left
- * empty, the setting imagemagick.settings.path_to_binaries is taken.
- *
- * @return string
- * The full path to the executable.
- */
- public function getExecutable($binary, $path = NULL) {
- // $path is only passed from the validation of the image toolkit form, on
- // which the path to convert is configured. @see ::checkPath()
- if (!isset($path)) {
- $path = $this->configFactory->get('imagemagick.settings')->get('path_to_binaries');
- }
+ // Ensure sourceLocalPath is prepared.
+ $this->ensureSourceLocalPath();
- $executable = $binary;
- if ($this->isWindows) {
- $executable .= '.exe';
+ // Allow modules to alter the command line parameters.
+ $command = 'convert';
+ $this->moduleHandler->alter('imagemagick_arguments', $this->arguments, $command);
+
+ // Delete any cached file metadata for the destination image file, before
+ // creating a new one, and release the URI from the manager so that
+ // metadata will not stick in the same request.
+ $this->fileMetadataManager->deleteCachedMetadata($this->arguments()->getDestination());
+ $this->fileMetadataManager->release($this->arguments()->getDestination());
+
+ // When destination format differs from source format, and source image
+ // is multi-frame, convert only the first frame.
+ $destination_format = $this->arguments()->getDestinationFormat() ?: $this->arguments()->getSourceFormat();
+ if ($this->arguments()->getSourceFormat() !== $destination_format && ($this->getFrames() === NULL || $this->getFrames() > 1)) {
+ $this->arguments()->setSourceFrames('[0]');
}
- return $path . $executable;
+ // Execute the command and return.
+ return $this->getExecManager()->execute($command, $this->arguments) && file_exists($this->arguments()->getDestinationLocalPath());
}
/**
]);
}
else {
- $status = $this->checkPath($this->configFactory->get('imagemagick.settings')->get('path_to_binaries'));
+ $status = $this->getExecManager()->checkPath($this->configFactory->get('imagemagick.settings')->get('path_to_binaries'));
if (!empty($status['errors'])) {
// Can not execute 'convert'.
$severity = REQUIREMENT_ERROR;
// No errors, report the version information.
$severity = REQUIREMENT_INFO;
$version_info = explode("\n", preg_replace('/\r/', '', Html::escape($status['output'])));
+ $value = array_shift($version_info);
$more_info_available = FALSE;
foreach ($version_info as $key => $item) {
- if (stripos($item, 'feature') !== FALSE || $key > 4) {
+ if (stripos($item, 'feature') !== FALSE || $key > 3) {
$more_info_available = TRUE;
break;
]);
}
}
- return [
+ $requirements = [
'imagemagick' => [
'title' => $this->t('ImageMagick'),
+ 'value' => isset($value) ? $value : NULL,
'description' => [
'#markup' => implode('<br />', $reported_info),
],
'severity' => $severity,
],
];
+
+ // Warn if parsing via 'getimagesize'.
+ // @todo remove in 8.x-3.0.
+ if ($this->configFactory->getEditable('imagemagick.settings')->get('use_identify') === FALSE) {
+ $requirements['imagemagick_getimagesize'] = [
+ 'title' => $this->t('ImageMagick'),
+ 'value' => $this->t('Use "identify" to parse image files'),
+ 'description' => $this->t('The toolkit is set to use the <kbd>getimagesize</kbd> PHP function to parse image files. This functionality will be dropped in the next major release of the Imagemagick module. Go to the <a href=":url">Image toolkit</a> settings page, and ensure that the \'Use "identify"\' flag in the \'Execution options\' tab is selected.', [
+ ':url' => Url::fromRoute('system.image_toolkit_settings')->toString(),
+ ]),
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ }
+
+ return $requirements;
}
/**