View source
<?php
namespace Drupal\fillpdf\Plugin\PdfBackend;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystem;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\fillpdf\Component\Utility\FillPdf;
use Drupal\fillpdf\FieldMapping\TextFieldMapping;
use Drupal\fillpdf\FillPdfBackendPluginInterface;
use Drupal\fillpdf\FillPdfFormInterface;
use Drupal\fillpdf\Plugin\PdfBackendBase;
use Drupal\fillpdf\ShellManager;
use InvalidArgumentException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\File\FileSystemInterface;
class PdftkPdfBackend extends PdfBackendBase implements ContainerFactoryPluginInterface {
protected $fileSystem;
protected $shellManager;
protected $entityTypeManager;
public function __construct(array $configuration, $plugin_id, array $plugin_definition, FileSystem $file_system, ShellManager $shell_manager, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->fileSystem = $file_system;
$this->shellManager = $shell_manager;
$this->entityTypeManager = $entity_type_manager;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('file_system'), $container
->get('fillpdf.shell_manager'), $container
->get('entity_type.manager'));
}
public function parseStream($pdf_content) {
$template_file = file_save_data($pdf_content);
return $this
->parseFile($template_file);
}
public function parseFile(FileInterface $template_file) {
$template_uri = $template_file
->getFileUri();
$pdftk_path = $this
->getPdftkPath();
$status = FillPdf::checkPdftkPath($pdftk_path);
if ($status === FALSE) {
$this
->messenger()
->addError($this
->t('pdftk not properly installed.'));
return [];
}
$template_path = $this->shellManager
->escapeShellArg($this->fileSystem
->realpath($template_uri));
$output = [];
exec("{$pdftk_path} {$template_path} dump_data_fields_utf8", $output, $status);
if (count($output) === 0) {
$this
->messenger()
->addWarning($this
->t('PDF does not contain fillable fields.'));
return [];
}
$data_fields_map = [
'FieldType' => 'type',
'FieldName' => 'name',
'FieldFlags' => 'flags',
'FieldValue' => 'value',
'FieldJustification' => 'justification',
];
$fields = [];
$fieldindex = -1;
foreach ($output as $lineitem) {
if ($lineitem == '---') {
$fieldindex++;
continue;
}
list($key, $value) = explode(':', $lineitem);
if (in_array($key, array_keys($data_fields_map), NULL)) {
$fields[$fieldindex][$data_fields_map[$key]] = trim($value);
}
}
return $fields;
}
protected function getPdftkPath() {
return isset($this->configuration['pdftk_path']) ? $this->configuration['pdftk_path'] : 'pdftk';
}
public function mergeStream($pdf_content, array $field_mappings, array $context) {
$template_file = file_save_data($pdf_content);
return $this
->mergeFile($template_file, $field_mappings, $context);
}
public function mergeFile(FileInterface $template_file, array $field_mappings, array $context) {
$template_uri = $template_file
->getFileUri();
$fields = [];
foreach ($field_mappings as $pdf_key => $mapping) {
if ($mapping instanceof TextFieldMapping) {
$fields[$pdf_key] = (string) $mapping;
}
}
module_load_include('inc', 'fillpdf', 'xfdf');
$xfdf_name = $template_uri . '.xfdf';
$xfdf = create_xfdf(basename($xfdf_name), $fields);
$xfdf_file = file_save_data($xfdf, $xfdf_name, FileSystemInterface::EXISTS_RENAME);
if (!isset($context['fid'])) {
throw new InvalidArgumentException("pdftk requires \$context['fid'] to be set to the ID of the FillPDF Form so that it can check if encryption is configured. The merge was aborted because it was not set.");
}
$fillpdf_form = $this->entityTypeManager
->getStorage('fillpdf_form')
->load($context['fid']);
$arg_permissions = $arg_owner_password = $arg_user_password = '';
$arg_pdftk_encryption = $fillpdf_form->pdftk_encryption->value ? " {$fillpdf_form->pdftk_encryption->value}" : '';
$permissions = $fillpdf_form->permissions
->getString();
if ($permissions) {
$arg_permissions = ' allow ' . str_replace(',', '', $permissions);
}
$owner_password = $fillpdf_form->owner_password->value;
if ($owner_password) {
$arg_owner_password = ' owner_pw ' . $this->shellManager
->escapeShellArg($owner_password);
}
$user_password = $fillpdf_form->user_password->value;
if ($user_password) {
$arg_user_password = ' user_pw ' . $this->shellManager
->escapeShellArg($user_password);
}
$template_path = $this->shellManager
->escapeShellArg($this->fileSystem
->realpath($template_uri));
$xfdf_path = $this->shellManager
->escapeShellArg($this->fileSystem
->realpath($xfdf_file
->getFileUri()));
$pdftk_path = $this
->getPdftkPath();
ob_start();
$command = "{$pdftk_path} {$template_path} fill_form {$xfdf_path} output - " . ($context['flatten'] ? 'flatten drop_xfa' : '') . "{$arg_pdftk_encryption}{$arg_permissions}{$arg_owner_password}{$arg_user_password}";
passthru($command);
$data = ob_get_clean();
if ($data === FALSE) {
$this
->messenger()
->addError($this
->t('pdftk not properly installed. No PDF generated.'));
}
$xfdf_file
->delete();
if ($data) {
return $data;
}
return NULL;
}
public static function getEncryptionOptions() : array {
return [
'' => t('No encryption (Default)'),
'encrypt_128bit' => t('128-bit encryption (Recommended)'),
'encrypt_40bit' => t('40-bit encryption'),
];
}
public static function getUserPermissionList() : array {
return [
'Printing' => t('Printing (Top Quality Printing)'),
'DegradedPrinting' => t('DegradedPrinting (Lower Quality Printing)'),
'ModifyContents' => t('ModifyContents (Also allows <em>Assembly</em>)'),
'Assembly' => t('Assembly'),
'CopyContents' => t('CopyContents (Also allows <em>ScreenReaders</em>)'),
'ScreenReaders' => t('ScreenReaders'),
'ModifyAnnotations' => t('ModifyAnnotations (Also allows <em>FillIn</em>)'),
'FillIn' => t('FillIn'),
'AllFeatures' => t('AllFeatures (Allows the user to perform all of the above, and top quality printing.)'),
];
}
}