vendor/mikehaertl/php-pdftk/src/Pdf.php line 296

Open in your IDE?
  1. <?php
  2. namespace mikehaertl\pdftk;
  3. use mikehaertl\tmp\File;
  4. /**
  5.  * Pdf
  6.  *
  7.  * This class is a wrapper around pdftk.
  8.  *
  9.  * The class was developed for pdftk 2.x but should also work with older
  10.  * versions, but you may have to use slightly different page rotation options
  11.  * (e.g 'E' instead 'east').
  12.  *
  13.  * @author Michael Härtl <haertl.mike@gmail.com>
  14.  * @license http://www.opensource.org/licenses/MIT
  15.  */
  16. class Pdf
  17. {
  18.     // The prefix for temporary files
  19.     const TMP_PREFIX 'tmp_php_pdftk_';
  20.     /**
  21.      * @var bool whether to ignore any errors if some non-empty output file was
  22.      * still created. Default is false.
  23.      */
  24.     public $ignoreWarnings false;
  25.     /**
  26.      * @var null|string an optional directory where temporary files should be
  27.      * created. If left empty the directory is autodetected.
  28.      */
  29.     public $tempDir;
  30.     /**
  31.      * @var File the temporary output file
  32.      */
  33.     protected $_tmpFile;
  34.     /**
  35.      * @var string the content type of the tmp output
  36.      */
  37.     protected $_tmpOutputContentType 'application/pdf';
  38.     /**
  39.      * @var Command the command instance that executes pdftk
  40.      */
  41.     protected $_command;
  42.     /**
  43.      * @var int a counter for autogenerated handles
  44.      */
  45.     protected $_handle 0;
  46.     /**
  47.      * @var string the error message
  48.      */
  49.     protected $_error '';
  50.     /**
  51.      * @var string|null the output filename. If null (default) a tmp file is
  52.      * used as output. If false, no output option is added at all.
  53.      */
  54.     protected $_output;
  55.     /**
  56.      * @var string the PDF data as returned from getData()
  57.      */
  58.     protected $_data;
  59.     protected $_data_utf8;
  60.     /**
  61.      * @var DataFields the PDF form field data as returned from getDataFields()
  62.      */
  63.     protected $_dataFields;
  64.     protected $_dataFields_utf8;
  65.     /**
  66.      * @var Pdf[]|null if the input was an instance, we keep a reference here,
  67.      * so that it won't get unlinked before this object gets destroyed
  68.      */
  69.     protected $_pdfs;
  70.     /**
  71.      * @param string|Pdf|array $pdf a pdf filename or Pdf instance or an array
  72.      * of filenames/instances indexed by a handle. The array values can also
  73.      * be arrays of the form array($filename, $password) if some files are
  74.      * password protected.
  75.      * @param array $options Options to pass to set on the Command instance,
  76.      * e.g. the pdftk binary path
  77.      */
  78.     public function __construct($pdf null$options = array())
  79.     {
  80.         $command $this->getCommand();
  81.         if ($options !== array()) {
  82.             $command->setOptions($options);
  83.         }
  84.         if (is_string($pdf) || $pdf instanceof Pdf) {
  85.             $this->addFile($pdf);
  86.         } elseif (is_array($pdf)) {
  87.             foreach ($pdf as $handle => $file) {
  88.                 if (is_array($file)) {
  89.                     $this->addFile($file[0], $handle$file[1]);
  90.                 } else {
  91.                     $this->addFile($file$handle);
  92.                 }
  93.             }
  94.         }
  95.     }
  96.     /**
  97.      * @param string|Pdf $name the PDF filename or Pdf instance to add for
  98.      * processing
  99.      * @param string|null $handle one or more uppercase letters A..Z to
  100.      * reference this file later. If no handle is provided, an internal handle
  101.      * is autocreated, consuming the range Z..A
  102.      * @param string|null $password the owner (or user) password if any
  103.      * @return Pdf the pdf instance for method chaining
  104.      */
  105.     public function addFile($name$handle null$password null)
  106.     {
  107.         if ($handle === null || is_numeric($handle)) {
  108.             $handle $this->nextHandle();
  109.         }
  110.         if ($name instanceof Pdf) {
  111.             // Keep a reference to the object to prevent unlinking
  112.             $this->_pdfs[] = $name;
  113.             if (!$name->getCommand()->getExecuted()) {
  114.                 // @todo: Catch errors!
  115.                 $name->execute();
  116.             }
  117.             $name = (string) $name->getTmpFile();
  118.         }
  119.         $this->getCommand()->addFile($name$handle$password);
  120.         return $this;
  121.     }
  122.     /**
  123.      * Assemble (catenate) pages from the input files.
  124.      *
  125.      * Values for rotation are (in degrees): north: 0, east: 90, south: 180,
  126.      * west: 270, left: -90, right: +90, down: +180. left, right and down make
  127.      * relative adjustments to a page's rotation. Note: Older pdftk versions
  128.      * use N, E, S, W, L, R, and D instead.
  129.      *
  130.      * Example:
  131.      *
  132.      *  $pdf = new Pdf;
  133.      *  $pdf->addFile('file1.pdf', 'A')
  134.      *      ->addFile('file2.pdf', 'B')
  135.      *      ->cat(array(1,3),'B'))          // pages 1 and 3 of file B
  136.      *      ->cat(1, 5, 'A', 'odd')         // pages 1, 3, 5 of file A
  137.      *      ->cat('end', 5, 'B')            // pages 5 to end of file B in reverse order
  138.      *      ->cat(null, null, 'B', 'east')  // All pages from file B rotated by 90 degree
  139.      *      ->saveAs('out.pdf');
  140.      * or
  141.      *  $files = ['file1.pdf', 'file2.pdf', 'file3.pdf'];
  142.      *  $pdf = new Pdf($files);
  143.      *  $pdf->cat()                         // all files, all pages
  144.      *      ->saveAs('out.pdf');
  145.      *
  146.      * @param int|string|array|null $start the start page number or an array of page
  147.      * numbers. If an array, the other arguments will be ignored. $start can
  148.      * also be bigger than $end for pages in reverse order. If $start is null all
  149.      * pages of all files will be added.
  150.      * @param int|string|null $end the end page number or null for single page
  151.      * (or list if $start is an array)
  152.      * @param string|null $handle the handle of the file to use. Can be null if
  153.      * only a single file was added.
  154.      * @param string|null $qualifier the page number qualifier, either 'even'
  155.      * or 'odd' or null for none
  156.      * @param string $rotation the rotation to apply to the pages.
  157.      * @return Pdf the pdf instance for method chaining
  158.      */
  159.     public function cat($start null$end null$handle null$qualifier null$rotation null)
  160.     {
  161.         $this->getCommand()
  162.             ->setOperation('cat')
  163.             ->addPageRange($start$end$handle$qualifier$rotation);
  164.         return $this;
  165.     }
  166.     /**
  167.      * Shuffle pages from the input files.
  168.      *
  169.      * This works the same as cat(), but each call to this method creates a
  170.      * "stream" of pages. The outfile will be assembled by adding one page from
  171.      * each stream at a time.
  172.      *
  173.      * Example:
  174.      *
  175.      *  $pdf = new Pdf;
  176.      *  $pdf1 = $pdf->addFile('file1.pdf');
  177.      *  $pdf->shuffle($pdf1, array(1,3,2))
  178.      *      ->shuffle($pdf1, array(4,5,9)
  179.      *      ->saveAs('out.pdf');
  180.      *
  181.      * This will give the page order 1, 4, 3, 5, 2, 9 in the out.pdf
  182.      *
  183.      * @param string $handle the handle of the input file to use
  184.      * @param int|array $start the start page number or an array of page
  185.      * numbers.
  186.      * @param int|null $end the end page number or null for single page (or
  187.      * list if $start is an array)
  188.      * @param string|null $qualifier the page number qualifier, either 'even'
  189.      * or 'odd' or null for none
  190.      * @param string $rotation the rotation to apply to the pages. See cat()
  191.      * for more details.
  192.      * @return Pdf the pdf instance for method chaining
  193.      */
  194.     public function shuffle($start$end null$handle null$qualifier null$rotation null)
  195.     {
  196.         $this->getCommand()
  197.             ->setOperation('shuffle')
  198.             ->addPageRange($start$end$handle$qualifier$rotation);
  199.         return $this;
  200.     }
  201.     /**
  202.      * Split the PDF document into pages
  203.      *
  204.      * @param string|null $filepattern the output name in sprintf format or
  205.      * null for default 'pg_%04d.pdf'
  206.      * @return bool whether the burst operation was successful
  207.      */
  208.     public function burst($filepattern null)
  209.     {
  210.         $this->constrainSingleFile();
  211.         $this->getCommand()->setOperation('burst');
  212.         $this->_output $filepattern === null 'pg_%04d.pdf' $filepattern;
  213.         return $this->execute();
  214.     }
  215.     /**
  216.      * Attach files to the PDF
  217.      *
  218.      * @param array $files the list of full paths to the files to attach
  219.      * @param string $toPage the page to add the attachment to. If omitted the
  220.      * files are attached at the document level.
  221.      * @return bool whether the operation was successful
  222.      */
  223.     public function attachFiles($files$toPage null)
  224.     {
  225.         $this->constrainSingleFile();
  226.         if ($toPage !== null) {
  227.             $files[] = 'to_page';
  228.             $files[] = $toPage;
  229.         }
  230.         $this->getCommand()
  231.             ->setOperation('attach_files')
  232.             ->setOperationArgument($filestrue);
  233.         return $this;
  234.     }
  235.     /**
  236.      * Copy all attachments from the PDF to the given directory
  237.      *
  238.      * @param string|null $dir the output directory
  239.      * @return bool whether the operation was successful
  240.      */
  241.     public function unpackFiles($dir null)
  242.     {
  243.         $this->constrainSingleFile();
  244.         $this->getCommand()->setOperation('unpack_files');
  245.         $this->_output $dir;
  246.         return $this->execute();
  247.     }
  248.     /**
  249.      * Generate the FDF file for a single PDF file.
  250.      *
  251.      * @param string $name name of the FDF file
  252.      * @return bool whether the pdf is generated successful
  253.      */
  254.     public function generateFdfFile($name)
  255.     {
  256.         $this->constrainSingleFile();
  257.         $this->getCommand()->setOperation('generate_fdf');
  258.         $this->_output $name;
  259.         return $this->execute();
  260.     }
  261.     /**
  262.      * Fill a PDF form
  263.      *
  264.      * @param string|array $data either a XFDF/FDF filename or an array with
  265.      * form field data (name => value)
  266.      * @param string $encoding the encoding of the data. Default is 'UTF-8'.
  267.      * @param bool $dropXfa whether to drop XFA forms (see dropXfa()). Default
  268.      * is true.
  269.      * @param string $format the file format to use for form filling when
  270.      * passing an array in `$data`. This can be `xfdf` or `fdf`. `xfdf` should
  271.      * give best results so you should not have to change the default.
  272.      * @return Pdf the pdf instance for method chaining
  273.      */
  274.     public function fillForm($data$encoding 'UTF-8'$dropXfa true$format 'xfdf')
  275.     {
  276.         $this->constrainSingleFile();
  277.         if (is_array($data)) {
  278.             $className '\mikehaertl\pdftk\\' . ($format === 'xfdf' 'XfdfFile' 'FdfFile');
  279.             $data = new $className($datanullnull$this->tempDir$encoding);
  280.         }
  281.         $this->getCommand()
  282.             ->setOperation('fill_form')
  283.             ->setOperationArgument($datatrue);
  284.         if ($dropXfa) {
  285.             $this->dropXfa();
  286.         }
  287.         return $this;
  288.     }
  289.     /**
  290.      * Update meta data of PDF
  291.      *
  292.      * @param string|array $data either a InfoFile filename or an array with
  293.      * form field data (name => value)
  294.      * @param string the encoding of the data. Default is 'UTF-8'.
  295.      * @return Pdf the pdf instance for method chaining
  296.      */
  297.     public function updateInfo($data$encoding 'UTF-8')
  298.     {
  299.         $this->constrainSingleFile();
  300.         if (is_array($data) || $data instanceof InfoFields) {
  301.             $data = new InfoFile($datanullnull$this->tempDir$encoding);
  302.         }
  303.         $this->getCommand()
  304.             ->setOperation($encoding == 'UTF-8' 'update_info_utf8' 'update_info')
  305.             ->setOperationArgument($datatrue);
  306.         return $this;
  307.     }
  308.     /**
  309.      * Apply a PDF as watermark to the background of a single PDF file.
  310.      *
  311.      * The PDF file must have a transparent background for the watermark to be
  312.      * visible.
  313.      *
  314.      * @param string $file name of the background PDF file. Only the first page
  315.      * is used.
  316.      * @return Pdf the pdf instance for method chaining
  317.      */
  318.     public function background($file)
  319.     {
  320.         $this->constrainSingleFile();
  321.         $this->getCommand()
  322.             ->setOperation('background')
  323.             ->setOperationArgument($filetrue);
  324.         return $this;
  325.     }
  326.     /**
  327.      * Apply multiple PDF pages as watermark to the corresponding pages of a
  328.      * single PDF file.
  329.      *
  330.      * If $file has fewer pages than the PDF file then the last page is
  331.      * repeated as background.
  332.      *
  333.      * @param string $file name of the background PDF file.
  334.      * @return Pdf the pdf instance for method chaining
  335.      */
  336.     public function multiBackground($file)
  337.     {
  338.         $this->getCommand()
  339.             ->setOperation('multibackground')
  340.             ->setOperationArgument($filetrue);
  341.         return $this;
  342.     }
  343.     /**
  344.      * Add $file as overlay to a single PDF file.
  345.      *
  346.      * The $file should have a transparent background.
  347.      *
  348.      * @param string $file name of the PDF file to add as overlay. Only the
  349.      * first page is used.
  350.      * @return Pdf the pdf instance for method chaining
  351.      */
  352.     public function stamp($file)
  353.     {
  354.         $this->constrainSingleFile();
  355.         $this->getCommand()
  356.             ->setOperation('stamp')
  357.             ->setOperationArgument($filetrue);
  358.         return $this;
  359.     }
  360.     /**
  361.      * Add multiple pages from $file as overlay to the corresponding pages of a
  362.      * single PDF file.
  363.      *
  364.      * If $file has fewer pages than the PDF file then the last page is
  365.      * repeated as overlay.
  366.      *
  367.      * @param string $file name of the PDF file to add as overlay
  368.      * @return Pdf the pdf instance for method chaining
  369.      */
  370.     public function multiStamp($file)
  371.     {
  372.         $this->getCommand()
  373.             ->setOperation('multistamp')
  374.             ->setOperationArgument($filetrue);
  375.         return $this;
  376.     }
  377.     /**
  378.      * @param bool $utf8 whether to dump the data UTF-8 encoded. Default is
  379.      * true.
  380.      * @return InfoFields|bool meta data about the PDF or false on failure
  381.      */
  382.     public function getData($utf8 true)
  383.     {
  384.         $property $utf8 '_data_utf8' '_data';
  385.         if ($this->$property === null) {
  386.             $command $this->getCommand();
  387.             $command->setOperation($utf8 'dump_data_utf8' 'dump_data');
  388.             if (!$command->execute()) {
  389.                 $this->_error $command->getError();
  390.                 return false;
  391.             } else {
  392.                 $this->$property = new InfoFields(trim($command->getOutput()));
  393.             }
  394.         }
  395.         return $this->$property;
  396.     }
  397.     /**
  398.      * @param bool $utf8 whether to dump the data UTF-8 encoded. Default is
  399.      * true.
  400.      * @return DataFields|bool data about the PDF form fields or false on
  401.      * failure
  402.      */
  403.     public function getDataFields($utf8 true)
  404.     {
  405.         $property $utf8 '_dataFields_utf8' '_dataFields';
  406.         if ($this->$property === null) {
  407.             $command $this->getCommand();
  408.             $command->setOperation($utf8 'dump_data_fields_utf8' 'dump_data_fields');
  409.             if (!$command->execute()) {
  410.                 $this->_error $command->getError();
  411.                 return false;
  412.             } else {
  413.                 $this->$property = new DataFields(trim($command->getOutput()));
  414.             }
  415.         }
  416.         return $this->$property;
  417.     }
  418.     /**
  419.      * Set PDF permissions
  420.      *
  421.      *
  422.      * @param string|null $permissions list of space separated permissions or
  423.      * null for none. The available permissions are Printing, DegradedPrinting,
  424.      * ModifyContents, Assembly, CopyContents, ScreenReaders,
  425.      * ModifyAnnotations, FillIn, AllFeatures.
  426.      * @return Pdf the pdf instance for method chaining
  427.      */
  428.     public function allow($permissions null)
  429.     {
  430.         $this->getCommand()
  431.             ->addOption('allow'$permissionsfalse);
  432.         return $this;
  433.     }
  434.     /**
  435.      * Flatten the PDF form fields values into a single PDF file.
  436.      *
  437.      * @return Pdf the pdf instance for method chaining
  438.      */
  439.     public function flatten()
  440.     {
  441.         $this->getCommand()
  442.             ->addOption('flatten');
  443.         return $this;
  444.     }
  445.     /**
  446.      * Restore/remove compression
  447.      *
  448.      * @param bool $compress whether to restore (default) or remove the
  449.      * compression
  450.      * @return Pdf the pdf instance for method chaining
  451.      */
  452.     public function compress($compress true)
  453.     {
  454.         $this->getCommand()
  455.             ->addOption($compress 'compress' 'uncompress');
  456.         return $this;
  457.     }
  458.     /**
  459.      * When combining multiple PDFs, use either the first or last ID in the
  460.      * output. If not called, a new ID is created.
  461.      *
  462.      * @param string $id, either 'first' (default) or 'last'
  463.      * @return Pdf the pdf instance for method chaining
  464.      */
  465.     public function keepId($id 'first')
  466.     {
  467.         $this->getCommand()
  468.             ->addOption($id === 'first' 'keep_first_id' 'keep_final_id');
  469.         return $this;
  470.     }
  471.     /**
  472.      * Set need_appearances flag in PDF
  473.      *
  474.      * This flag makes sure, that a PDF reader takes care of rendering form
  475.      * field content, even if it contains non ASCII characters. You should
  476.      * always use this option if you fill in forms e.g. with Unicode
  477.      * characters. You can't combine this option with flatten() though!
  478.      *
  479.      * @return Pdf the pdf instance for method chaining
  480.      */
  481.     public function needAppearances()
  482.     {
  483.         $this->getCommand()
  484.             ->addOption('need_appearances');
  485.         return $this;
  486.     }
  487.     /**
  488.      * Drop XFA data from forms created with newer Acrobat.
  489.      *
  490.      * Newer PDF forms contain both, the newer XFA and the older AcroForm form
  491.      * fields. PDF readers can use both, but will prefer XFA if present. Since
  492.      * pdftk can only fill in AcroForm data you should always add this option
  493.      * when filling in forms with pdftk.
  494.      *
  495.      * @return Pdf the pdf instance for method chaining
  496.      */
  497.     public function dropXfa()
  498.     {
  499.         $this->getCommand()
  500.             ->addOption('drop_xfa');
  501.         return $this;
  502.     }
  503.     /**
  504.      * Drop XMP meta data
  505.      *
  506.      * Newer PDFs can contain both, new style XMP data and old style info
  507.      * directory. PDF readers can use both, but will prefer XMP if present.
  508.      * Since pdftk can only update the info directory you should always add
  509.      * this option when updating PDF info.
  510.      *
  511.      * @return Pdf the pdf instance for method chaining
  512.      */
  513.     public function dropXmp()
  514.     {
  515.         $this->getCommand()
  516.             ->addOption('drop_xmp');
  517.         return $this;
  518.     }
  519.     /**
  520.      * @param string $password the owner password to set on the output PDF
  521.      * @return Pdf the pdf instance for method chaining
  522.      */
  523.     public function setPassword($password)
  524.     {
  525.         $this->getCommand()
  526.             ->addOption('owner_pw'$passwordtrue);
  527.         return $this;
  528.     }
  529.     /**
  530.      * @param string $password the user password to set on the output PDF
  531.      * @return Pdf the pdf instance for method chaining
  532.      */
  533.     public function setUserPassword($password)
  534.     {
  535.         $this->getCommand()
  536.             ->addOption('user_pw'$passwordtrue);
  537.         return $this;
  538.     }
  539.     /**
  540.      * @param int $strength the password encryption strength. Default is 128
  541.      * @return Pdf the pdf instance for method chaining
  542.      */
  543.     public function passwordEncryption($strength 128)
  544.     {
  545.         $this->getCommand()
  546.             ->addOption($strength == 128 'encrypt_128bit' 'encrypt_40bit');
  547.         return $this;
  548.     }
  549.     /**
  550.      * Replace embedded font with a local font when filling a form.
  551.      *
  552.      * This option is only available for pdftk-java >= 3.3.0. It is useful when
  553.      * filling a form with non-ASCII text that is not supported by the fonts
  554.      * included in the input PDF.
  555.      *
  556.      * @param string $fontName the path to the font or the name of a font family.
  557.      * @return Pdf the pdf instance for method chaining
  558.      */
  559.     public function replacementFont($path)
  560.     {
  561.         $this->getCommand()
  562.             ->addOption('replacement_font'$path);
  563.         return $this;
  564.     }
  565.     /**
  566.      * Execute the operation and save the output file
  567.      *
  568.      * @param string $name of output file
  569.      * @return bool whether the PDF could be processed and saved
  570.      */
  571.     public function saveAs($name)
  572.     {
  573.         if (!$this->getCommand()->getExecuted() && !$this->execute()) {
  574.             return false;
  575.         }
  576.         $tmpFile = (string) $this->getTmpFile();
  577.         if (!copy($tmpFile$name)) {
  578.             $this->_error "Could not copy PDF from tmp location '$tmpFile' to '$name'";
  579.             return false;
  580.         }
  581.         return true;
  582.     }
  583.     /**
  584.      * Send PDF to client, either inline or as download (triggers PDF creation)
  585.      *
  586.      * @param string|null $filename the filename to send. If empty, the PDF is
  587.      * streamed inline.
  588.      * @param bool $inline whether to force inline display of the PDF, even if
  589.      * filename is present.
  590.      * @param array $headers a list of additional HTTP headers to send in the
  591.      * response as an array. The array keys are the header names like
  592.      * 'Cache-Control' and the array values the header value strings to send.
  593.      * Each array value can also be another array of strings if the same header
  594.      * should be sent multiple times. This can also be used to override
  595.      * automatically created headers like 'Expires' or 'Content-Length'. To suppress
  596.      * automatically created headers, `false` can also be used as header value.
  597.      * @return bool whether PDF was created successfully
  598.      */
  599.     public function send($filename null$inline false$headers = array())
  600.     {
  601.         if (!$this->getCommand()->getExecuted() && !$this->execute()) {
  602.             return false;
  603.         }
  604.         $this->getTmpFile()->send($filename$this->_tmpOutputContentType$inline$headers);
  605.         return true;
  606.     }
  607.     /**
  608.      * Get the raw PDF contents (triggers PDF creation).
  609.      *
  610.      * @return string|bool the PDF content as a string or `false` if the PDF
  611.      * wasn't created successfully.
  612.      */
  613.     public function toString()
  614.     {
  615.         if (!$this->getCommand()->getExecuted() && !$this->execute()) {
  616.             return false;
  617.         }
  618.         return file_get_contents($this->getTmpFile()->getFileName());
  619.     }
  620.     /**
  621.      * @return Command the command instance that executes pdftk
  622.      */
  623.     public function getCommand()
  624.     {
  625.         if ($this->_command === null) {
  626.             $this->_command = new Command;
  627.         }
  628.         return $this->_command;
  629.     }
  630.     /**
  631.      * @return File the temporary output file instance
  632.      */
  633.     public function getTmpFile()
  634.     {
  635.         if ($this->_tmpFile === null) {
  636.             $this->_tmpFile = new File('''.pdf'self::TMP_PREFIX$this->tempDir);
  637.         }
  638.         return $this->_tmpFile;
  639.     }
  640.     /**
  641.      * @return string the error message or an empty string if none
  642.      */
  643.     public function getError()
  644.     {
  645.         return $this->_error;
  646.     }
  647.     /**
  648.      * Execute the pdftk command and store the output file to a temporary
  649.      * location or $this->_output if set.  You should probably never call this
  650.      * method unless you only need a temporary PDF file as result.
  651.      *
  652.      * @return bool whether the command was executed successfully
  653.      */
  654.     public function execute()
  655.     {
  656.         $command $this->getCommand();
  657.         if ($command->getExecuted()) {
  658.             return false;
  659.         }
  660.         if ($this->_output === false) {
  661.             $filename null;
  662.         } else {
  663.             $filename $this->_output $this->_output : (string) $this->getTmpFile();
  664.         }
  665.         if (!$command->execute($filename)) {
  666.             $this->_error $command->getError();
  667.             if ($filename && !(file_exists($filename) && filesize($filename) !== && $this->ignoreWarnings)) {
  668.                 return false;
  669.             }
  670.         }
  671.         return true;
  672.     }
  673.     /**
  674.      * Make sure, that only one file is present
  675.      */
  676.     protected function constrainSingleFile()
  677.     {
  678.         if ($this->getCommand()->getFileCount() > 1) {
  679.             throw new \Exception('This operation can only process single files');
  680.         }
  681.     }
  682.     /**
  683.      * @return string the next handle in the series A, B, C, ... Z, AA, AB...
  684.      */
  685.     protected function nextHandle()
  686.     {
  687.         // N.B. Multi-character handles are only available in pdftk 1.45+
  688.         $i $this->_handle++;
  689.         $char 'A';
  690.         while ($i-- > 0) {
  691.             $char++;
  692.         }
  693.         return $char;
  694.     }
  695. }