epub_reader_framework-2.0.0-alpha2/src/Epub/ReaderEpubZipExtractor.php
src/Epub/ReaderEpubZipExtractor.php
<?php
namespace Drupal\epub_reader_framework\Epub;
use Drupal\Core\Archiver\Zip;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\epub_reader_framework\EpubReaderFrameworkHelpers;
use Drupal\file\FileInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Class ReaderEpubZipExtractor.
*/
class ReaderEpubZipExtractor {
use StringTranslationTrait;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* The extracted files.
*
* @var array
*/
protected $files;
/**
* The stream wrapper.
*
* @var Drupal\Core\StreamWrapper\StreamWrapperManager
*/
protected $streamWrapper;
/**
* The serializer.
*
* @var Symfony\Component\Serializer\SerializerInterface
*/
protected $serializer;
/**
* Constructor.
*
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file handler.
* @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper
* The stream wrapper.
* @param \Symfony\Component\Serializer\SerializerInterface $serializer
* The serializer.
*/
public function __construct(
FileSystemInterface $file_system,
StreamWrapperManager $stream_wrapper,
SerializerInterface $serializer
) {
$this->fileSystem = $file_system;
$this->streamWrapper = $stream_wrapper;
$this->serializer = $serializer;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('file_system'),
$container->get('stream_wrapper_manager'),
$container->get('serializer')
);
}
/**
* Callback implementation of hook_form_FORM_ID_alter().
*
* @param \Drupal\node\NodeInterface $reader_publication
* The reader publication node.
*
* @throws \Drupal\Core\Archiver\ArchiverException
*/
public function extractEpubFiles(NodeInterface $reader_publication) {
if ($reader_publication->hasField('field_reader_file') && $media_file = $reader_publication->field_reader_file->entity) {
/** @var \Drupal\media\MediaInterface $media_file */
if ($media_file->hasField('field_media_file') && $file = $media_file->field_media_file->entity) {
/** @var \Drupal\file\FileInterface $file */
$real_path = $this->fileSystem->realpath($file->getFileUri());
$zip = new Zip($real_path);
// Build the path per publication, per file.
$to_path_uri = EpubReaderFrameworkHelpers::getEpubExtractedFilesPath($reader_publication, $file);
// Create the directory if not yet existing.
$result = $this->fileSystem->prepareDirectory($to_path_uri, FileSystemInterface::CREATE_DIRECTORY);
if ($result) {
// Extract the content.
$zip->extract($to_path_uri);
}
else {
\Drupal::messenger()->addWarning($this->t('Failed to prepare a directory to store EPUB files in.'));
}
}
}
}
/**
* Get extracted files.
*
* @param \Drupal\node\NodeInterface $reader_publication
* The reader publication node.
* @param bool|\Drupal\file\FileInterface $file
* The epub file or false.
*
* @return array|bool
* An array of files or false.
*/
public function getExtractedFiles(
NodeInterface $reader_publication,
$file = FALSE
) {
if ($this->files) {
return $this->files;
}
if (!$file) {
if ($reader_publication->hasField('field_reader_file') && $media_file = $reader_publication->field_reader_file->entity) {
/** @var \Drupal\media\MediaInterface $media_file */
if ($media_file->hasField('field_media_file')) {
/** @var \Drupal\file\FileInterface $file */
$file = $media_file->field_media_file->entity;
}
}
}
if ($file instanceof FileInterface) {
$directory = EpubReaderFrameworkHelpers::getEpubExtractedFilesPath($reader_publication, $file);
$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
$this->files = $this->fileSystem->scanDirectory(
$directory,
'/.*\.(png|jpg|jpeg|gif|xhtml|ncx|opf)$/'
);
return $this->files;
}
\Drupal::messenger()->addWarning($this->t('Failed to get any extracted files from the EPUB.'));
return FALSE;
}
/**
* Get extracted files by type.
*
* @param \Drupal\node\NodeInterface $reader_publication
* The publication reader.
* @param bool|\Drupal\file\FileInterface $file
* The epub file or false.
* @param array $types
* An array of extensions.
*
* @return array
* The files.
*/
public function getExtractedFilesByTypes(
NodeInterface $reader_publication,
$file,
array $types
) {
$return = [];
if ($files = $this->getExtractedFiles($reader_publication, $file)) {
foreach ($files as $key => $file) {
$filename_parts = explode('.', $file->filename);
$extension = end($filename_parts);
if (in_array($extension, $types)) {
$return[$key] = $file;
}
}
}
return $return;
}
/**
* Get extracted files by type.
*
* @param \Drupal\node\NodeInterface $reader_publication
* The publication reader.
* @param bool|\Drupal\file\FileInterface $file
* The epub file or false.
*
* @return array
* The files.
*/
public function getSpineContents(
NodeInterface $reader_publication,
$file = FALSE
) {
$return = [];
if ($files = $this->getExtractedFilesByTypes(
$reader_publication,
$file,
['opf']
)) {
$opf = reset($files);
$data = file_get_contents($opf->uri);
/** @var \Symfony\Component\Serializer\SerializerInterface $xml_encoder */
$xml_encoder = $this->serializer;
$opf_data = $xml_encoder->decode($data, 'xml');
if ($opf_data && isset($opf_data['spine']['itemref'])) {
// Find all spine items.
foreach ($opf_data['spine']['itemref'] as $spine_item) {
foreach ($opf_data['manifest']['item'] as $manifest_item) {
// Skip if this is not the matching manifest item.
if ($manifest_item['@id'] != $spine_item['@idref']) {
continue;
}
// Skip if non-linear content as we cannot use that.
if (isset($spine_item['@linear']) && $spine_item['@linear'] === 'no') {
continue;
}
// Add spine item details to the return array.
$file_details = $this->getFileDetailsByFilename(
$reader_publication,
$manifest_item['@href']
);
$return[] = array_merge(
$manifest_item,
$spine_item,
$file_details
);
break;
}
}
}
}
if (!$return) {
\Drupal::messenger()->addWarning($this->t('Failed to determine EPUB spine contents.'));
}
return $return;
}
/**
* Get the file details by filename.
*
* @param \Drupal\node\NodeInterface $reader_publication
* The publication reader.
* @param string $filename
* The filename.
*
* @return array
* The file details.
*/
protected function getFileDetailsByFilename(
NodeInterface $reader_publication,
$filename
) {
if ($files = $this->getExtractedFiles($reader_publication)) {
foreach ($files as $file) {
if ($file->filename == $filename) {
return (array) $file;
}
}
}
return [];
}
}
