X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=web%2Fmodules%2Fcontrib%2Fpdf_to_imagefield%2Fpdf_to_imagefield.module;fp=web%2Fmodules%2Fcontrib%2Fpdf_to_imagefield%2Fpdf_to_imagefield.module;h=1802320a24b7640e9bdde939727d2fb62d2c2d04;hp=0000000000000000000000000000000000000000;hb=1fed477e46533140ff15ce8064f4fbf354419c1c;hpb=58360fba03c880fb30fb0670e485a7a07d028597 diff --git a/web/modules/contrib/pdf_to_imagefield/pdf_to_imagefield.module b/web/modules/contrib/pdf_to_imagefield/pdf_to_imagefield.module new file mode 100644 index 000000000..1802320a2 --- /dev/null +++ b/web/modules/contrib/pdf_to_imagefield/pdf_to_imagefield.module @@ -0,0 +1,269 @@ +getFieldDefinitions() as $field_definition) { + if ($field_definition->getType() == 'file' && $field_definition->getThirdPartySetting('pdf_to_imagefield', 'enable')) { + $image_style = \Drupal::entityTypeManager()->getStorage('image_style')->load($field_definition->getThirdPartySetting('pdf_to_imagefield', 'image_style')); + $target_field = $entity->getFieldDefinition($field_definition->getThirdPartySetting('pdf_to_imagefield', 'target_field')); + $force_image_toolkit = $field_definition->getThirdPartySetting('pdf_to_imagefield', 'force_image_toolkit_enable') && $field_definition->getThirdPartySetting('pdf_to_imagefield', 'force_image_toolkit') ? $field_definition->getThirdPartySetting('pdf_to_imagefield', 'force_image_toolkit') : FALSE; + + if ($image_style && $target_field) { + $files = array_map(function ($item) { + return $item['target_id']; + }, $entity->get($field_definition->getName())->getValue()); + + $file_entities = !empty($files) ? \Drupal::entityTypeManager()->getStorage($field_definition->getFieldStorageDefinition()->getSetting('target_type'))->loadMultiple($files) : []; + // Now we remap the file entities array from being keyed by fid to + // being keyed by delta. + $files = []; + foreach ($entity->get($field_definition->getName())->getValue() as $delta => $item) { + $files[$delta] = $file_entities[$item['target_id']]; + } + + $original_entity = isset($entity->original) ? $entity->original : NULL; + if (!$entity->isNew() && $entity->isNewRevision()) { + // If it is a new revision, the $entity->original will have a + // pointer the "current" revision. While we need the just previous + // revision in order to see if files and previews have changed. At + // least paragraphs module does not promote its new revisions as + // "current", so we need this hack in order to recognize truly + // "latest" revision when running on paragraph entities. + // TODO: is there any better way to find the latest (not current) + // revision of an entity than using its entity query? + $query = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId())->getQuery(); + $query->allRevisions(); + $query->condition($entity->getEntityType()->getKey('id'), $entity->id()); + if ($entity->isTranslatable()) { + $query->condition($entity->getEntityType()->getKey('langcode'), $entity->language()->getId()); + } + $revision_id = max(array_keys($query->execute())); + + // The revision might be not available, if that's a translatable + // entity whose new translation is being saved right now. + if ($revision_id) { + $original_entity = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId())->loadRevision($revision_id); + } + } + + if ($entity->isTranslatable() && $original_entity && $original_entity->hasTranslation($entity->language()->getId())) { + $original_entity = $original_entity->getTranslation($entity->language()->getId()); + } + + $old_files = isset($original_entity) ? $original_entity->get($field_definition->getName())->getValue() : []; + + // We only override missing previews, this way there is still a + // possibility to specify the preview images through other means (be + // it programmatically or via UI). + $previews = array_slice($entity->get($target_field->getName())->getValue(), 0, count($files)); + $old_previews = isset($original_entity) ? $original_entity->get($target_field->getName())->getValue() : []; + + foreach ($files as $delta => $file) { + // We only change preview if it is missing or the underlying file + // has changed while its preview has not been updated. + $is_file_changed = (isset($files[$delta]) xor isset($old_files[$delta])) || $files[$delta]->id() != $old_files[$delta]['target_id']; + + $is_preview_changed = (isset($previews[$delta]) xor isset($old_previews[$delta])); + if (isset($previews[$delta]['target_id']) && isset($old_previews[$delta]['target_id'])) { + $is_preview_changed = $is_preview_changed || $previews[$delta]['target_id'] != $old_previews[$delta]['target_id']; + } + + if ((!isset($previews[$delta]) || ($is_file_changed && !$is_preview_changed)) && $file->getMimeType() == 'application/pdf') { + $target_uri = $image_style->buildUri($file->getFileUri()); + if ($force_image_toolkit) { + // Unfortunately, ImageStyle::createDerivative does not use + // dependency injection correctly. It accesses + // \Drupal::service() directly instead of injecting its + // dependencies in constructor. + // So we just outsmart it by temporarily swapping the active + // image toolkit. + $image_factory = \Drupal::service('image.factory'); + $real_image_toolkit = $image_factory->getToolkitId(); + $image_factory->setToolkitId('imagemagick'); + } + $image_style->flush($file->getFileUri()); + $image_style->createDerivative($file->getFileUri(), $target_uri); + + if ($force_image_toolkit) { + // Now revert back the toolkit in the global image factory. + $image_factory->setToolkitId($real_image_toolkit); + } + + $new_preview = \Drupal::entityTypeManager()->getStorage($target_field->getFieldStorageDefinition()->getSetting('target_type'))->create([ + 'uid' => \Drupal::currentUser()->id(), + 'uri' => $target_uri, + ]); + $new_preview->setPermanent(); + $new_preview->save(); + $new_preview = $new_preview->id(); + + $previews[$delta] = [ + 'target_id' => $new_preview, + 'alt' => t('Preview of @file_name', [ + '@file_name' => $file->getFilename(), + ]), + ]; + } + } + + $entity->set($target_field->getName(), $previews); + } + } + } + } +} + +/** + * Implements hook_imagemagick_arguments_alter(). + */ +function pdf_to_imagefield_imagemagick_arguments_alter(\Drupal\imagemagick\Plugin\ImageToolkit\ImagemagickToolkit $toolkit, $command) { + switch ($command) { + case 'convert': + // TODO: For some reason some versions/setups of ImageMagick do not + // correctly recognize PDF mime-type, so we additionally check for + // extension. So far no side effects have been reported regarding this + // intrusion. + if ($toolkit->getSourceFormat() == 'PDF' || preg_match('#\.pdf$#i', $toolkit->getSource())) { + // Insert the -flatten argument right after reading the PDF file. + // Otherwise the resulting images may have issues with background + // colors. + $toolkit->prependArgument('-flatten'); + } + break; + } +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function pdf_to_imagefield_form_field_config_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) { + $field_config = $form_state->getBuildInfo()['callback_object']->getEntity(); + + if (pdf_to_imagefield_is_eligible($field_config)) { + // Since this file field accepts PDFs, we can hook in with our stuff. + $form['third_party_settings']['pdf_to_imagefield'] = array( + '#type' => 'fieldset', + '#title' => t('PDF preview autogeneration'), + ); + + $form['third_party_settings']['pdf_to_imagefield']['enable'] = array( + '#type' => 'checkbox', + '#title' => t('Generate 1st page preview'), + '#description' => t('Automatically generate 1st page PDF preview and save it into an arbitrary image field on the same entity.'), + '#default_value' => $field_config->getThirdPartySetting('pdf_to_imagefield', 'enable'), + ); + + // The rest of our settings should be visible only if the feature is + // actually enabled. + $states = [ + 'visible' => [ + ':input[name$="[pdf_to_imagefield][enable]"]' => ['checked' => TRUE], + ], + ]; + + $form['third_party_settings']['pdf_to_imagefield']['target_field'] = array( + '#type' => 'select', + '#title' => t('Store 1st page image preview in'), + '#description' => t('Specify an image field where the 1st page preview of the PDF should be saved.'), + '#options' => pdf_to_imagefield_allowed_image_fields($field_config->getTargetEntityTypeId(), $field_config->getTargetBundle()), + '#states' => $states, + '#default_value' => $field_config->getThirdPartySetting('pdf_to_imagefield', 'target_field'), + ); + + $form['third_party_settings']['pdf_to_imagefield']['image_style'] = array( + '#type' => 'select', + '#title' => t('Image style'), + '#description' => t('Specify image style to use for PDF to image conversion.'), + '#options' => pdf_to_imagefield_allowed_image_styles(), + '#states' => $states, + '#default_value' => $field_config->getThirdPartySetting('pdf_to_imagefield', 'image_style'), + ); + + $form['third_party_settings']['pdf_to_imagefield']['force_image_toolkit_enable'] = array( + '#type' => 'checkbox', + '#title' => t('Force a specific image toolkit'), + '#description' => t('Force to use a specific image toolkit for PDF to image conversion instead of the default image toolkit.'), + '#default_value' => $field_config->getThirdPartySetting('pdf_to_imagefield', 'force_image_toolkit_enable'), + '#states' => $states, + ); + + $image_toolkit_options = []; + foreach (\Drupal::service('image.toolkit.manager')->getAvailableToolkits() as $id => $definition) { + $image_toolkit_options[$id] = $definition['title']; + } + + $form['third_party_settings']['pdf_to_imagefield']['force_image_toolkit'] = array( + '#type' => 'radios', + '#title' => t('Image toolkit to force'), + '#options' => $image_toolkit_options, + '#default_value' => $field_config->getThirdPartySetting('pdf_to_imagefield', 'force_image_toolkit'), + '#states' => array_merge_recursive($states, ['visible' => [ + ':input[name$="[pdf_to_imagefield][force_image_toolkit_enable]"]' => ['checked' => TRUE], + ]]), + ); + } +} + +/** + * Determine whether the field config is eligible for PDF to image manipulation. + * + * @param \Drupal\Core\Field\FieldConfigInterface $field_config + * Field config entity object whose eligibility to determine + * + * @return bool + * Whether the provided field config entity is eligible for PDF to image + * manipulation + */ +function pdf_to_imagefield_is_eligible(\Drupal\Core\Field\FieldConfigInterface $field_config) { + return $field_config->getType() == 'file' && preg_match('/(^|\s|,)pdf($|\s|,)/i', $field_config->getSetting('file_extensions')); +} + +/** + * Fetch a list of image fields where the preview could be saved. + * + * @param string $entity_type + * Entity type where to conduct the search of allowed image fields + * @param string $bundle + * Bundle where to conduct the search of allowed image fields + * + * @return array + * Array of possible field names where PDF preview could be saved. Keys are + * field names whereas values are their human labels + */ +function pdf_to_imagefield_allowed_image_fields($entity_type, $bundle) { + $allowed_values = []; + + foreach (\Drupal::service('entity_field.manager')->getFieldDefinitions($entity_type, $bundle) as $field_definition) { + if ($field_definition->getType() == 'image') { + $allowed_values[$field_definition->getName()] = $field_definition->getLabel(); + } + } + + return $allowed_values; +} + +/** + * Fetch a list of eligible image styles that can be used for PDF conversion. + * + * @return array + * Array of possible image styles that can be used for PDF preview + * generation. Keys are image style machine-names whereas values are their + * human labels + */ +function pdf_to_imagefield_allowed_image_styles() { + $allowed_values = []; + + foreach (\Drupal::entityTypeManager()->getStorage('image_style')->loadMultiple() as $image_style) { + $allowed_values[$image_style->id()] = $image_style->label(); + } + + return $allowed_values; +}