pdf_to_imagefield-1.0.x-dev/src/PDFToImageManager.php
src/PDFToImageManager.php
<?php
namespace Drupal\pdf_to_imagefield;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\file\FileInterface;
use Spatie\PdfToImage\Pdf;
/**
* Provides helper class of PDFToImageManager.
*/
class PDFToImageManager implements PDFToImageManagerInterface {
use StringTranslationTrait;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManager
*/
protected EntityFieldManager $entityFieldManager;
/**
* The entity display repository.
*
* @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
*/
protected EntityDisplayRepositoryInterface $entityDisplayRepository;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* The stream wrapper manager.
*
* @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
*/
protected StreamWrapperManagerInterface $streamWrapperManager;
/**
* The module logger.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
private LoggerChannelInterface $logger;
/**
* Construct PDFToImageManager.
*/
public function __construct(
EntityFieldManager $entityFieldManager,
EntityDisplayRepositoryInterface $entityDisplayRepository,
EntityTypeManagerInterface $entityTypeManager,
StreamWrapperManagerInterface $streamWrapperManager,
LoggerChannelInterface $logger,
) {
$this->logger = $logger;
$this->streamWrapperManager = $streamWrapperManager;
$this->entityTypeManager = $entityTypeManager;
$this->entityDisplayRepository = $entityDisplayRepository;
$this->entityFieldManager = $entityFieldManager;
}
/**
* {@inheritDoc}
*/
public function getImageTargetFields(string $entity_type_id, string $bundle): array {
$fields = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle);
$field_options = [];
foreach ($fields as $field_name => $field_definition) {
if (in_array($field_definition->getType(), ['file', 'image'])) {
$field_options[$field_name] = $field_definition->getLabel();
}
}
return $field_options;
}
/**
* {@inheritDoc}
*/
public function getPdfToImageSourceFields(string $entity_type_id, string $bundle): array {
$fields = $this->entityDisplayRepository->getFormDisplay($entity_type_id, $bundle)->getComponents();
$source_fields = [];
foreach ($fields as $field_name => $field) {
if (isset($field['type']) && $field['type'] === 'pdf_to_imagefield') {
$source_fields[$field_name] = $field;
}
}
return $source_fields;
}
/**
* {@inheritDoc}
*/
public function initConversion(ContentEntityInterface $entity, string $field_id, array $field): void {
$settings = $field['settings'];
/** @var \Drupal\file\Plugin\Field\FieldType\FileFieldItemList $file_field */
$file_field = $entity->get($field_id);
/** @var \Drupal\file\FileInterface[] $files */
$files = $file_field->referencedEntities();
if (!($target_field = $settings['target_field'] ?? NULL)) {
return;
}
if (empty($files)) {
// Updating entity object to be sure that the target field is empty
// before batch work.
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->load($entity->id());
$entity->set($target_field, []);
$entity->save();
return;
}
$operations = [];
$other_files = [];
$pdf_files = [];
$limit = $settings['batch_limit'];
$base_batch_data = [
'entity' => $entity,
'image' => [
'field' => $this->entityTypeManager->getStorage('field_storage_config')->loadByProperties([
'entity_type' => $entity->getEntityTypeId(),
'field_name' => $target_field,
]),
'instance' => $this->entityTypeManager->getStorage('field_config')->loadByProperties([
'entity_type' => $entity->getEntityTypeId(),
'bundle' => $entity->bundle(),
'field_name' => $target_field,
]),
],
'pdf_settings' => $settings,
];
foreach ($files as $file) {
if ($file->getMimeType() !== 'application/pdf') {
if (!$settings['only_pdf']) {
$other_files[] = $file;
}
continue;
}
// Check if PDF has been already converted.
if ($pdf_pages = $this->getPdfPages($file)) {
$other_files = array_merge($other_files, $pdf_pages);
continue;
}
// Prepare data for PDF conversion.
$uri = $file->getFileUri();
if ($uri === NULL) {
continue;
}
$stream_wrapper = $this->streamWrapperManager->getViaUri($uri);
if (!$stream_wrapper instanceof StreamWrapperInterface) {
continue;
}
$full_path = $stream_wrapper->realpath();
if (!file_exists($full_path)) {
$this->logger->error('Invalid file given to convert. Could not read %file (%source_file)',
[
'%file' => $file->getFileUri(),
'%source_file' => $full_path,
]);
continue;
}
$pdf = new Pdf($full_path);
$count = $pdf->getNumberOfPages();
// Skip PDF if it doesn't have any pages.
if (!$count) {
continue;
}
// Prepare PDF settings.
$pdf->setOutputFormat($settings['image_format'] ?? 'png');
$pdf->setResolution($settings['resolution'] ?? 144);
$pdf->setCompressionQuality($settings['compression'] ?? 100);
// Prepare batch operations according to page limit per operation.
$start_page = 1;
do {
$pdf_files[] = [
$file,
$pdf,
$start_page,
min($start_page + $limit, $count),
];
$start_page += $limit + 1;
} while ($start_page < $count);
}
if ($other_files) {
$operations[] = [self::HELPER_CLASS . '::moveNonPdfFiles',
[$base_batch_data, $other_files],
];
}
if ($pdf_files) {
foreach ($pdf_files as $pdf_file) {
$operations[] = [self::HELPER_CLASS . '::generateProcessPage',
[$base_batch_data, ...$pdf_file],
];
}
}
if (!$operations) {
$this->logger->info('There are no files to process.');
}
$this->logger->info('Starting a process to convert attached PDF to image previews');
// Inits batch.
$batch = [
'title' => $this->t('Processing PDF to Image.'),
'operations' => $operations,
'finished' => self::HELPER_CLASS . '::generateProcessAttach',
'progress_message' => $this->t('Processed @current out of @total.'),
];
batch_set($batch);
$this->logger->notice('Batch operations end.');
$this->logger->info('Update batch operations end.');
}
/**
* Get PDF pages that have been already converted.
*
* @param \Drupal\file\FileInterface $file
* PDF file.
*
* @return \Drupal\file\FileInterface[]
* PDF pages.
*/
protected function getPdfPages(FileInterface $file): array {
$file_storage = $this->entityTypeManager->getStorage('file');
$pdf_pages = $file_storage->getQuery()
->accessCheck(FALSE)
->condition('filename', $file->id(), 'STARTS_WITH')
->execute();
if ($pdf_pages) {
return $file_storage->loadMultiple($pdf_pages);
}
return [];
}
}
