print_pdf_tcpdf.pages.inc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <?php
  2. /**
  3. * @file
  4. * Generates the PDF version using TCPDF.
  5. *
  6. * This file is included by the print_pdf_tcpdf module and includes the
  7. * functions that interface with the TCPDF library.
  8. *
  9. * @ingroup print
  10. */
  11. /**
  12. * Implements hook_print_pdf_generate().
  13. */
  14. function print_pdf_tcpdf_print_pdf_generate($html, $meta, $paper_size = NULL, $page_orientation = NULL) {
  15. global $base_url, $language;
  16. module_load_include('inc', 'print', 'includes/print');
  17. $pdf_tool = explode('|', variable_get('print_pdf_pdf_tool', PRINT_PDF_PDF_TOOL_DEFAULT));
  18. if (empty($paper_size)) {
  19. $paper_size = variable_get('print_pdf_paper_size', PRINT_PDF_PAPER_SIZE_DEFAULT);
  20. }
  21. if (empty($page_orientation)) {
  22. $page_orientation = variable_get('print_pdf_page_orientation', PRINT_PDF_PAGE_ORIENTATION_DEFAULT);
  23. }
  24. $images_via_file = variable_get('print_pdf_images_via_file', PRINT_PDF_IMAGES_VIA_FILE_DEFAULT);
  25. if (variable_get('print_pdf_autoconfig', PRINT_PDF_AUTOCONFIG_DEFAULT)) {
  26. $pdf_tool_path = realpath(dirname($pdf_tool[1]));
  27. if (!defined('K_TCPDF_EXTERNAL_CONFIG')) {
  28. define('K_TCPDF_EXTERNAL_CONFIG', TRUE);
  29. }
  30. if (!defined('K_PATH_MAIN')) {
  31. define('K_PATH_MAIN', DRUPAL_ROOT);
  32. }
  33. if (!defined('K_PATH_URL')) {
  34. define('K_PATH_URL', $base_url);
  35. }
  36. if (!defined('K_PATH_FONTS')) {
  37. define('K_PATH_FONTS', $pdf_tool_path . '/fonts/');
  38. }
  39. if (!defined('K_PATH_CACHE')) {
  40. define('K_PATH_CACHE', drupal_realpath('public://print_pdf/print_pdf_tcpdf/cache') . '/');
  41. }
  42. if (!defined('K_PATH_IMAGES')) {
  43. define('K_PATH_IMAGES', '');
  44. }
  45. if (!defined('K_BLANK_IMAGE')) {
  46. define('K_BLANK_IMAGE', $pdf_tool_path . '/images/_blank.png');
  47. }
  48. if (!defined('K_CELL_HEIGHT_RATIO')) {
  49. define('K_CELL_HEIGHT_RATIO', 1.25);
  50. }
  51. if (!defined('K_SMALL_RATIO')) {
  52. define('K_SMALL_RATIO', 2 / 3);
  53. }
  54. }
  55. // Try to use local file access for image files.
  56. $html = _print_access_images_via_file($html, $images_via_file);
  57. // Decode HTML entities in image filenames.
  58. $pattern = "!<img\s[^>]*?src\s*?=\s*?['\"]?{$base_url}[^>]*?>!is";
  59. $html = preg_replace_callback($pattern, create_function('$matches', 'return html_entity_decode($matches[0], ENT_QUOTES);'), $html);
  60. // Remove queries from the image URL.
  61. $pattern = "!(<img\s[^>]*?src\s*?=\s*?['\"]?{$base_url}[^>]*?)(?:%3F|\?)[^\s'\"]+([^>]*?>)!is";
  62. $html = preg_replace($pattern, '$1$2', $html);
  63. $tool_path = DRUPAL_ROOT . '/' . $pdf_tool[1];
  64. if (file_exists($tool_path)) {
  65. require_once $tool_path;
  66. }
  67. else {
  68. watchdog('print_pdf', 'Configured PDF tool does not exist at path: %path', array('%path' => $tool_path), WATCHDOG_ERROR);
  69. throw new Exception("Configured PDF tool does not exist, unable to generate PDF.");
  70. }
  71. module_load_include('inc', 'print_pdf_tcpdf', 'print_pdf_tcpdf.class');
  72. $font = array(
  73. check_plain(variable_get('print_pdf_font_family', PRINT_PDF_TCPDF_FONT_FAMILY_DEFAULT)),
  74. '',
  75. check_plain(variable_get('print_pdf_font_size', PRINT_PDF_TCPDF_FONT_SIZE_DEFAULT)),
  76. );
  77. $orientation = drupal_strtoupper($page_orientation[0]);
  78. // Create new PDF document.
  79. $pdf = new PrintTCPDF($orientation, 'mm', $paper_size, TRUE);
  80. // Set document information.
  81. if (isset($meta['name'])) {
  82. $pdf->SetAuthor(strip_tags($meta['name']));
  83. }
  84. $pdf->SetCreator(variable_get('site_name', 'Drupal'));
  85. $pdf->SetTitle(html_entity_decode($meta['title'], ENT_QUOTES, 'UTF-8'));
  86. $pdf->setPDFVersion('1.6');
  87. $pdf->setFontSubsetting(variable_get('print_pdf_font_subsetting', PRINT_PDF_TCPDF_FONT_SUBSETTING_DEFAULT));
  88. $pdf->setTcpdfLink(FALSE);
  89. if ($language->direction == LANGUAGE_RTL) {
  90. $pdf->setRTL(TRUE);
  91. }
  92. $pdf = theme('print_pdf_tcpdf_header', array(
  93. 'pdf' => $pdf,
  94. 'html' => $html,
  95. 'font' => $font,
  96. ));
  97. $pdf = theme('print_pdf_tcpdf_footer', array(
  98. 'pdf' => $pdf,
  99. 'html' => $html,
  100. 'font' => $font,
  101. ));
  102. $pdf = theme('print_pdf_tcpdf_page', array('pdf' => $pdf));
  103. // Enable third-party module to alter the pdf object, via
  104. // hook_print_pdf_tcpdf_alter().
  105. drupal_alter('print_pdf_tcpdf', $pdf, $html, $meta);
  106. // Add a page.
  107. $pdf->AddPage();
  108. $pdf = theme('print_pdf_tcpdf_content', array(
  109. 'pdf' => $pdf,
  110. 'html' => $html,
  111. 'font' => $font,
  112. ));
  113. // Reset pointer to the last page.
  114. $pdf->lastPage();
  115. // Try to recover from any warning/error.
  116. ob_clean();
  117. return $pdf = $pdf->Output('', 'S');
  118. }
  119. /**
  120. * Format the TCPDF header.
  121. *
  122. * @param array $vars
  123. * An associative array containing:
  124. * - $pdf: current TCPDF object
  125. * - $html: contents of the body of the HTML from the original node
  126. * - $font: array with the font definition (font name, styles and size)
  127. *
  128. * @return object
  129. * modified PDF object
  130. *
  131. * @ingroup themeable
  132. * @ingroup print_themeable
  133. */
  134. function theme_print_pdf_tcpdf_header($vars) {
  135. global $base_url;
  136. $pdf = $vars['pdf'];
  137. preg_match('!<div class="print-logo">(.*?)</div>!si', $vars['html'], $tpl_logo);
  138. preg_match('!<title>(.*?)</title>!si', $vars['html'], $tpl_title);
  139. preg_match('!<div class="print-site_name">(.*?)</div>!si', $vars['html'], $tpl_site_name);
  140. $ratio = 0;
  141. $logo = '';
  142. if (isset($tpl_logo[1]) && preg_match('!src\s*=\s*(?:"(.*?)"|\'(.*?)\'|([^\s]*))!i', $tpl_logo[1], $logo_url)) {
  143. $logo = $logo_url[1];
  144. // Make logo relative again.
  145. $logo = preg_replace("!^$base_url(.*)!sm", DRUPAL_ROOT . '$1', $logo);
  146. if (!empty($logo)) {
  147. $size = getimagesize($logo);
  148. $ratio = $size ? ($size[0] / $size[1]) : 0;
  149. }
  150. }
  151. // Set header font.
  152. $pdf->setHeaderFont($vars['font']);
  153. // Set header margin.
  154. $pdf->setHeaderMargin(5);
  155. // Set header data.
  156. $pdf->setHeaderData($logo, 10 * $ratio, html_entity_decode($tpl_title[1], ENT_QUOTES, 'UTF-8'), html_entity_decode(strip_tags($tpl_site_name[1]), ENT_QUOTES, 'UTF-8'));
  157. return $pdf;
  158. }
  159. /**
  160. * Format the TCPDF page settings (margins, etc).
  161. *
  162. * @param array $vars
  163. * An associative array containing:
  164. * - $pdf: current TCPDF object.
  165. *
  166. * @return object
  167. * modified PDF object
  168. *
  169. * @ingroup themeable
  170. * @ingroup print_themeable
  171. */
  172. function theme_print_pdf_tcpdf_page($vars) {
  173. $pdf = $vars['pdf'];
  174. // Set margins.
  175. $pdf->SetMargins(15, 20, 15);
  176. // Set auto page breaks.
  177. $pdf->SetAutoPageBreak(TRUE, 15);
  178. // Set image scale factor.
  179. $pdf->setImageScale(1);
  180. // Set image compression quality.
  181. $pdf->setJPEGQuality(100);
  182. return $pdf;
  183. }
  184. /**
  185. * Format the TCPDF page content.
  186. *
  187. * @param array $vars
  188. * An associative array containing:
  189. * - $pdf: current TCPDF object
  190. * - $html: contents of the body of the HTML from the original node
  191. * - $font: array with the font definition (font name, styles and size)
  192. *
  193. * @return object
  194. * modified PDF object
  195. *
  196. * @ingroup themeable
  197. * @ingroup print_themeable
  198. */
  199. function theme_print_pdf_tcpdf_content($vars) {
  200. $pdf = $vars['pdf'];
  201. // Set content font.
  202. $pdf->setFont($vars['font'][0], $vars['font'][1], $vars['font'][2]);
  203. // Remove the logo, published, breadcrumb and footer from the main content.
  204. preg_match('!<body.*?>(.*)</body>!sim', $vars['html'], $matches);
  205. $pattern = '!(?:<div class="print-(?:logo|site_name|breadcrumb|footer)">.*?</div>|<hr class="print-hr" />)!si';
  206. $matches[1] = preg_replace($pattern, '', $matches[1]);
  207. // Make CCK fields look better.
  208. $matches[1] = preg_replace('!(<div class="field.*?>)\s*!sm', '$1', $matches[1]);
  209. $matches[1] = preg_replace('!(<div class="field.*?>.*?</div>)\s*!sm', '$1', $matches[1]);
  210. $matches[1] = preg_replace('!<div( class="field-label.*?>.*?)</div>!sm', '<strong$1</strong>', $matches[1]);
  211. // Since TCPDF's writeHTML is so bad with <p>, try to make it look nice.
  212. $matches[1] = preg_replace('!<(?:p(|\s+.*?)/?|/p)>!i', '<br$1 />', $matches[1]);
  213. $matches[1] = str_replace(array('<div', 'div>'), array('<span', 'span><br />'), $matches[1]);
  214. do {
  215. $prev = $matches[1];
  216. $matches[1] = preg_replace('!(</span>)<br />(\s*?</span><br />)!s', '$1$2', $matches[1]);
  217. } while ($prev != $matches[1]);
  218. @$pdf->writeHTML($matches[1]);
  219. return $pdf;
  220. }
  221. /**
  222. * Format the TCPDF footer contents.
  223. *
  224. * @param array $vars
  225. * An associative array containing:
  226. * - $pdf: current TCPDF object
  227. * - $html: contents of the body of the HTML from the original node
  228. * - $font: array with the font definition (font name, styles and size)
  229. *
  230. * @return object
  231. * modified PDF object
  232. *
  233. * @ingroup themeable
  234. * @ingroup print_themeable
  235. */
  236. function theme_print_pdf_tcpdf_footer($vars) {
  237. $pdf = $vars['pdf'];
  238. preg_match('!<div class="print-footer">(.*?)</div>!si', $vars['html'], $tpl_footer);
  239. if (isset($tpl_footer[1])) {
  240. $footer = trim(preg_replace('!</?div[^>]*?>!i', '', $tpl_footer[1]));
  241. // Set footer font.
  242. $vars['font'][2] *= 0.8;
  243. $pdf->setFooterFont($vars['font']);
  244. // Set footer margin.
  245. $pdf->SetFooterMargin(10);
  246. // Set footer data.
  247. $pdf->setFooterContent($footer);
  248. }
  249. return $pdf;
  250. }
  251. /**
  252. * Format the TCPDF footer layout.
  253. *
  254. * @param array $vars
  255. * An associative array containing:
  256. * - $pdf: current TCPDF object.
  257. *
  258. * @return object
  259. * modified PDF object
  260. *
  261. * @ingroup themeable
  262. * @ingroup print_themeable
  263. */
  264. function theme_print_pdf_tcpdf_footer2($vars) {
  265. $pdf = $vars['pdf'];
  266. // Position at 1.5 cm from bottom.
  267. $pdf->writeHTMLCell(0, 15, 15, $pdf->getPageHeight() - 15, $pdf->footer);
  268. $ormargins = $pdf->getOriginalMargins();
  269. $pagenumtxt = t('Page !n of !total', array('!n' => $pdf->PageNo(), '!total' => $pdf->getAliasNbPages()));
  270. // Print page number.
  271. if ($pdf->getRTL()) {
  272. $pdf->SetX($ormargins['right']);
  273. $pdf->Cell(0, 10, $pagenumtxt, 'T', 0, 'L');
  274. }
  275. else {
  276. $pdf->SetX($ormargins['left']);
  277. $pdf->Cell(0, 10, $pagenumtxt, 'T', 0, 'R');
  278. }
  279. return $pdf;
  280. }