generate-document-concat.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. <?php
  2. set_time_limit(0);
  3. require_once dirname(__FILE__) . '/../../common.php';
  4. /**
  5. * Concatenating PDF files locally - advanced
  6. *
  7. * In the case that you wish to generate one output document that contains
  8. * several thousand populated templates, it is more efficient to use LiveDocx to
  9. * generate a large number of singular PDF files and then to concatenate them
  10. * locally, than it is to use the backend service directly.
  11. *
  12. * As the size of the output document in such cases can be several hundred
  13. * megabytes in size, it would take a long time to transfer all data from the
  14. * backend service to the local server. Hence, a local concatenation approach is
  15. * more desirable and considerably faster.
  16. *
  17. * In this example, the backend service is used to populate a template and
  18. * create a large number of documents (see variable $iterations). Then, using a
  19. * 3rd party external command line tool - either pdftk (http://is.gd/4KO72) or
  20. * ghostscript (http://is.gd/4LK3N) - the singular PDF files are concatenated
  21. * together locally to create one large output PDF file.
  22. *
  23. * NOTE: This sample application depends upon either pdftk or ghostscript being
  24. * install on your system. Both are available for Linux and Microsoft
  25. * Windows. Please take a look at the constants EXEC_PDFTK and
  26. * EXEC_GHOSTSCRIPT. You may need to redefine these, if you are running
  27. * Windows, or if your Linux distribution installs the tools at a different
  28. * location. The specified paths are correct for Debian 5.0.3.
  29. */
  30. define('EXEC_PDFTK', '/usr/bin/pdftk');
  31. define('EXEC_GHOSTSCRIPT', '/usr/bin/gs');
  32. define('PROCESSOR_PDFTK', 1);
  33. define('PROCESSOR_GHOSTSCRIPT', 2);
  34. // -----------------------------------------------------------------------------
  35. // Processor to use for concatenation.
  36. //
  37. // There are 2 options (only):
  38. //
  39. // o PROCESSOR_PDFTK
  40. // - Faster
  41. // - Requires little memory (RAM)
  42. // - No reduction in file size
  43. //
  44. // o PROCESSOR_GHOSTSCRIPT
  45. // - Slower
  46. // - Requires lots of memory (RAM)
  47. // - Reduction in file size
  48. //
  49. // If you have both installed on your system, PROCESSOR_PDFTK is recommended.
  50. $processor = PROCESSOR_PDFTK;
  51. // Number of documents (populated with random strings) to concatenate.
  52. $iterations = 3;
  53. // -----------------------------------------------------------------------------
  54. // Logger to output status messages
  55. $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
  56. Zend_Registry::set('logger', $logger);
  57. // -----------------------------------------------------------------------------
  58. // Create temporary directory
  59. $tempDirectory = sys_get_temp_dir() . '/' . md5(rand(1, 10000) . __FILE__);
  60. if (is_dir($tempDirectory)) {
  61. recursiveRemoveDirectory($tempDirectory);
  62. }
  63. $logger->log(sprintf('Making temporary directory %s.', $tempDirectory), Zend_Log::INFO);
  64. mkdir($tempDirectory);
  65. // -----------------------------------------------------------------------------
  66. // Generate temporary documents
  67. $tempFilenames = array();
  68. $mailMerge = new Zend_Service_LiveDocx_MailMerge();
  69. $mailMerge->setUsername(DEMOS_ZEND_SERVICE_LIVEDOCX_USERNAME)
  70. ->setPassword(DEMOS_ZEND_SERVICE_LIVEDOCX_PASSWORD);
  71. $mailMerge->setLocalTemplate('template.docx');
  72. for ($iteration = 1; $iteration <= $iterations; $iteration ++) {
  73. $tempFilename = sprintf('%s/%010s.pdf', $tempDirectory, $iteration);
  74. $tempFilenames[] = $tempFilename;
  75. $mailMerge->assign('software', randomString())
  76. ->assign('licensee', randomString())
  77. ->assign('company', randomString())
  78. ->assign('date', Zend_Date::now()->toString(Zend_Date::DATE_LONG))
  79. ->assign('time', Zend_Date::now()->toString(Zend_Date::TIME_LONG))
  80. ->assign('city', randomString())
  81. ->assign('country', randomString());
  82. $mailMerge->createDocument();
  83. file_put_contents($tempFilename, $mailMerge->retrieveDocument('pdf'));
  84. $logger->log(sprintf('Generating temporary document %s.', $tempFilename), Zend_Log::INFO);
  85. }
  86. unset($mailMerge);
  87. // -----------------------------------------------------------------------------
  88. // Concatenate temporary documents and write output document
  89. $outputFilename = './document-concat.pdf';
  90. $logger->log('Concatenating temporary documents...', Zend_Log::INFO);
  91. if (true === concatenatePdfFilenames($tempFilenames, $outputFilename, $processor)) {
  92. $logger->log(sprintf('...DONE. Saved output document as %s.', $outputFilename), Zend_Log::INFO);
  93. } else {
  94. $logger->log(sprintf('...ERROR.'), Zend_Log::ERR);
  95. }
  96. // -----------------------------------------------------------------------------
  97. // Delete temporary directory
  98. $logger->log(sprintf('Deleting temporary directory %s.', $tempDirectory), Zend_Log::INFO);
  99. if (is_dir($tempDirectory)) {
  100. recursiveRemoveDirectory($tempDirectory);
  101. }
  102. // =============================================================================
  103. // Helper functions
  104. /**
  105. * Create a random string
  106. *
  107. * @param $length
  108. * @return string
  109. */
  110. function randomString()
  111. {
  112. $ret = '';
  113. $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  114. $poolLen = strlen($pool);
  115. $stringLen = rand(5, 25);
  116. for ($i = 0; $i < $stringLen; $i ++) {
  117. $pos = (rand() % $poolLen);
  118. $ret .= $pool{$pos};
  119. }
  120. return $ret;
  121. }
  122. /**
  123. * Recursively remove directory
  124. *
  125. * @param $dir
  126. * @return void
  127. */
  128. function recursiveRemoveDirectory($dir)
  129. {
  130. $files = glob($dir . '*', GLOB_MARK);
  131. foreach ($files as $file) {
  132. if ('/' === substr($file, - 1)) {
  133. recursiveRemoveDirectory($file);
  134. } else {
  135. unlink($file);
  136. }
  137. }
  138. if (is_dir($dir)) {
  139. rmdir($dir);
  140. }
  141. }
  142. /**
  143. * Concatenate the files in passed array $inputFilenames into one file
  144. * $outputFilename, using concatenation processor (external 3rd party command
  145. * line tool) specified in $processor
  146. *
  147. * @param $inputFilenames
  148. * @param $outputFilename
  149. * @param $processor
  150. * @return boolean
  151. */
  152. function concatenatePdfFilenames($inputFilenames, $outputFilename, $processor = EXEC_PDFTK)
  153. {
  154. $ret = false;
  155. $logger = Zend_Registry::get('logger');
  156. if (! (is_file(EXEC_PDFTK) || is_file(EXEC_GHOSTSCRIPT))) {
  157. $logger->log('Either pdftk or ghostscript are required for this sample application.', Zend_Log::CRIT);
  158. exit();
  159. }
  160. if (is_file($outputFilename)) {
  161. unlink($outputFilename);
  162. }
  163. switch ($processor) {
  164. case PROCESSOR_PDFTK :
  165. $format = '%s %s cat output %s';
  166. $command = sprintf($format, EXEC_PDFTK, implode($inputFilenames, ' '), $outputFilename);
  167. break;
  168. case PROCESSOR_GHOSTSCRIPT :
  169. $format = '%s -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dAutoFilterColorImages=false ';
  170. $format .= '-dAutoFilterGrayImages=false -dAutoFilterMonoImages=false ';
  171. $format .= '-dColorImageFilter=/FlateEncode -dCompatibilityLevel=1.3 -dEmbedAllFonts=true ';
  172. $format .= '-dGrayImageFilter=/FlateEncode -dMaxSubsetPct=100 -dMonoImageFilter=/CCITTFaxEncode ';
  173. $format .= '-dSubsetFonts=true -sOUTPUTFILE=%s %s';
  174. $command = sprintf($format, EXEC_GHOSTSCRIPT, $outputFilename, implode($inputFilenames, ' '));
  175. break;
  176. default:
  177. $logger->log('Invalid concatenation processor - use PROCESSOR_PDFTK or PROCESSOR_GHOSTSCRIPT only.', Zend_Log::CRIT);
  178. exit();
  179. break;
  180. }
  181. $command = escapeshellcmd($command);
  182. exec($command);
  183. if (is_file($outputFilename) && filesize($outputFilename) > 0) {
  184. $ret = true;
  185. }
  186. return $ret;
  187. }