You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

406 lines
11KB

  1. <?php
  2. /**
  3. * @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2014 - 2015
  4. * @package yii2-mpdf
  5. * @version 1.0.1
  6. */
  7. namespace kartik\mpdf;
  8. use Yii;
  9. use yii\base\Component;
  10. use yii\base\InvalidConfigException;
  11. use yii\base\InvalidParamException;
  12. use \mPDF;
  13. /**
  14. * PDF library component wrapping the mPDF class with additional enhancements.
  15. *
  16. * @author Kartik Visweswaran <kartikv2@gmail.com>
  17. * @since 1.0
  18. */
  19. class Pdf extends Component
  20. {
  21. // mode
  22. const MODE_BLANK = '';
  23. const MODE_CORE = 'c';
  24. const MODE_UTF8 = 'UTF-8';
  25. const MODE_ASIAN = '+aCJK';
  26. // format
  27. const FORMAT_A3 = 'A3';
  28. const FORMAT_A4 = 'A4';
  29. const FORMAT_LETTER = 'Letter';
  30. const FORMAT_LEGAL = 'Legal';
  31. const FORMAT_FOLIO = 'Folio';
  32. const FORMAT_LEDGER = 'Ledger-L';
  33. const FORMAT_TABLOID = 'Tabloid';
  34. // orientation
  35. const ORIENT_PORTRAIT = 'P';
  36. const ORIENT_LANDSCAPE = 'L';
  37. // output destination
  38. const DEST_BROWSER = 'I';
  39. const DEST_DOWNLOAD = 'D';
  40. const DEST_FILE = 'F';
  41. const DEST_STRING = 'S';
  42. /**
  43. * @var string specifies the mode of the new document. If the mode is set by passing a country/language string,
  44. * this may also set: available fonts, text justification, and directionality RTL.
  45. */
  46. public $mode = self::MODE_BLANK;
  47. /**
  48. * @var string|array, the format can be specified either as a pre-defined page size,
  49. * or as an array of width and height in millimetres.
  50. */
  51. public $format = self::FORMAT_A4;
  52. /**
  53. * @var int sets the default document font size in points (pt)
  54. */
  55. public $defaultFontSize = 0;
  56. /**
  57. * @var string sets the default font-family for the new document. Uses default value set in defaultCSS
  58. * unless codepage has been set to "win-1252". If codepage="win-1252", the appropriate core Adobe font
  59. * will be set i.e. Helvetica, Times, or Courier.
  60. */
  61. public $defaultFont = '';
  62. /**
  63. * @var float sets the page left margin for the new document. All values should be specified as LENGTH in millimetres.
  64. * If you are creating a DOUBLE-SIDED document, the margin values specified will be used for ODD pages; left and right margins
  65. * will be mirrored for EVEN pages.
  66. */
  67. public $marginLeft = 15;
  68. /**
  69. * @var float sets the page right margin for the new document (in millimetres).
  70. */
  71. public $marginRight = 15;
  72. /**
  73. * @var float sets the page top margin for the new document (in millimetres).
  74. */
  75. public $marginTop = 16;
  76. /**
  77. * @var float sets the page bottom margin for the new document (in millimetres).
  78. */
  79. public $marginBottom = 16;
  80. /**
  81. * @var float sets the page header margin for the new document (in millimetres).
  82. */
  83. public $marginHeader = 9;
  84. /**
  85. * @var float sets the page footer margin for the new document (in millimetres).
  86. */
  87. public $marginFooter = 9;
  88. /**
  89. * @var string specifies the default page orientation of the new document.
  90. */
  91. public $orientation = self::ORIENT_PORTRAIT;
  92. /**
  93. * @var string css file to prepend to the PDF
  94. */
  95. public $cssFile = '@vendor/kartik-v/yii2-mpdf/assets/kv-mpdf-bootstrap.min.css';
  96. /**
  97. * @var string additional inline css to append after the cssFile
  98. */
  99. public $cssInline = '';
  100. /**
  101. * @var string the HTML content to be converted to PDF
  102. */
  103. public $content = '';
  104. /**
  105. * @var string the output filename
  106. */
  107. public $filename = '';
  108. /**
  109. * @var string the output destination
  110. */
  111. public $destination = self::DEST_BROWSER;
  112. /**
  113. * @var string the folder path for storing the temporary data generated by mpdf.
  114. * If not set this defaults to `Yii::getAlias('@runtime/mpdf')`.
  115. */
  116. public $tempPath;
  117. /**
  118. * @var array the mPDF methods that will called in the sequence listed before
  119. * rendering the content. Should be an associative array of $method => $params
  120. * format, where:
  121. * - `$method`: string, is the mPDF method / function name
  122. * - `$param`: mixed, are the mPDF method parameters
  123. */
  124. public $methods = '';
  125. /**
  126. * @var string the mPDF configuration options entered as a `$key => value`
  127. * associative array, where:
  128. * - `$key`: string is the mPDF configuration property name
  129. * - `$value`: mixed is the mPDF configured property value
  130. */
  131. public $options = [
  132. 'autoScriptToLang' => true,
  133. 'ignore_invalid_utf8' => true,
  134. 'tabSpaces' => 4
  135. ];
  136. /**
  137. * @var mPDF api instance
  138. */
  139. protected $_mpdf;
  140. /**
  141. * @var string the css file content
  142. */
  143. protected $_css;
  144. /**
  145. *
  146. * @var array Array of file-pathes that should be attached to the generated PDF
  147. */
  148. protected $_pdfAttachements;
  149. /**
  150. * @inherit doc
  151. */
  152. public function init()
  153. {
  154. $this->initTempPaths();
  155. parent::init();
  156. $this->parseFormat();
  157. }
  158. /**
  159. * Initialize folder paths to allow mpdf to write temporary data.
  160. */
  161. public function initTempPaths()
  162. {
  163. if (empty($this->tempPath)) {
  164. $this->tempPath = Yii::getAlias('@runtime/mpdf');
  165. }
  166. $prefix = $this->tempPath . DIRECTORY_SEPARATOR;
  167. static::definePath('_MPDF_TEMP_PATH', "{$prefix}tmp");
  168. static::definePath('_MPDF_TTFONTDATAPATH', "{$prefix}ttfontdata");
  169. }
  170. /**
  171. * Defines a mPDF temporary path if not set
  172. *
  173. * @param string $prop the mPDF constant to define
  174. * @param string $dir the directory to create
  175. *
  176. * @return bool
  177. * @throws InvalidConfigException
  178. */
  179. protected static function definePath($prop, $dir)
  180. {
  181. if (defined($prop)) {
  182. return;
  183. }
  184. $status = true;
  185. if (!is_dir($dir)) {
  186. $status = mkdir($dir, 0777, true);
  187. }
  188. if (!$status) {
  189. throw new InvalidConfigException("Could not create the folder '{$dir}' in '\$tempPath' set.");
  190. }
  191. define($prop, $dir);
  192. }
  193. /**
  194. * Renders and returns the PDF output. Uses the class level property settings.
  195. */
  196. public function render()
  197. {
  198. $this->configure($this->options);
  199. if (!empty($this->methods)) {
  200. foreach ($this->methods as $method => $param) {
  201. $this->execute($method, $param);
  202. }
  203. }
  204. return $this->output($this->content, $this->filename, $this->destination);
  205. }
  206. /**
  207. * Initializes (if needed) and fetches the mPDF API instance
  208. * @return mPDF instance
  209. */
  210. public function getApi()
  211. {
  212. if (empty($this->_mpdf) || !$this->_mpdf instanceof mPDF) {
  213. $this->setApi();
  214. }
  215. return $this->_mpdf;
  216. }
  217. /**
  218. * Sets the mPDF API instance
  219. */
  220. public function setApi()
  221. {
  222. $this->_mpdf = new mPDF(
  223. $this->mode,
  224. $this->format,
  225. $this->defaultFontSize,
  226. $this->defaultFont,
  227. $this->marginLeft,
  228. $this->marginRight,
  229. $this->marginTop,
  230. $this->marginBottom,
  231. $this->marginHeader,
  232. $this->marginFooter,
  233. $this->orientation
  234. );
  235. }
  236. /**
  237. * Fetches the content of the CSS file if supplied
  238. * @return string
  239. */
  240. public function getCss()
  241. {
  242. if (!empty($this->_css)) {
  243. return $this->_css;
  244. }
  245. $cssFile = empty($this->cssFile) ? '' : Yii::getAlias($this->cssFile);
  246. if (empty($cssFile) || !file_exists($cssFile)) {
  247. $css = '';
  248. } else {
  249. $css = file_get_contents($cssFile);
  250. }
  251. $css .= $this->cssInline;
  252. return $css;
  253. }
  254. /**
  255. * @return array Array of attachements
  256. */
  257. public function getPdfAttachements (){
  258. return $this->_pdfAttachements;
  259. }
  260. /**
  261. * add an PDF to attach to the generated PDF
  262. * @param string $filePath
  263. */
  264. public function addPdfAttachement ($filePath){
  265. $this->_pdfAttachements[] = $filePath;
  266. }
  267. /**
  268. * Configures mPDF options
  269. * @param array the mPDF configuration options entered as a `$key => value`
  270. * associative array, where:
  271. * - `$key`: string is the configuration property name
  272. * - `$value`: mixed is the configured property value
  273. */
  274. public function configure($options = [])
  275. {
  276. if (empty($options)) {
  277. return;
  278. }
  279. $api = $this->api;
  280. foreach ($options as $key => $value) {
  281. if (property_exists($api, $key)) {
  282. $api->$key = $value;
  283. }
  284. }
  285. }
  286. /**
  287. * Calls the mPDF method with parameters
  288. * @param string method the mPDF method / function name
  289. * @param array params the mPDF parameters
  290. * @return mixed
  291. */
  292. public function execute($method, $params = [])
  293. {
  294. $api = $this->api;
  295. if (!method_exists($api, $method)) {
  296. throw new InvalidParamException("Invalid or undefined mPDF method '{$method}' passed to 'Pdf::execute'.");
  297. }
  298. if (!is_array($params)) {
  299. $params = [$params];
  300. }
  301. return call_user_func_array([$api, $method], $params);
  302. }
  303. /**
  304. * Generates a PDF output
  305. * @param string content, the input HTML content
  306. * @param string file, the name of the file. If not specified, the document will be
  307. * sent to the browser inline (destination I).
  308. * @param string dest, the destination. Defaults to Pdf::DEST_BROWSER.
  309. * @return mixed
  310. */
  311. public function output($content = '', $file = '', $dest = self::DEST_BROWSER)
  312. {
  313. $api = $this->api;
  314. $css = $this->css;
  315. $pdfAttachements = $this->getPdfAttachements();
  316. if (!empty($css)) {
  317. $api->WriteHTML($css, 1);
  318. $api->WriteHTML($content, 2);
  319. } else {
  320. $api->WriteHTML($content);
  321. }
  322. if($pdfAttachements){
  323. $api->SetImportUse();
  324. $api->SetHeader(null);
  325. $api->SetFooter(null);
  326. foreach ($pdfAttachements as $attachement){
  327. $this->writePdfAttachement($api, $attachement);
  328. }
  329. }
  330. return $api->Output($file, $dest);
  331. }
  332. /**
  333. * appends the given attachement to the generated PDF
  334. * @param mPDF $api
  335. * @param String $attachement
  336. */
  337. private function writePdfAttachement ($api, $attachement){
  338. try {
  339. $pageCount = $api->SetSourceFile($attachement);
  340. } catch (\MpdfException $e){
  341. $pageCount = 0;
  342. }
  343. for($i=1; $i<=$pageCount; $i++){
  344. $api->AddPage();
  345. $templateId = $api->ImportPage($i);
  346. $api->UseTemplate($templateId);
  347. }
  348. }
  349. /**
  350. * Parse the format automatically based on the orientation
  351. */
  352. protected function parseFormat()
  353. {
  354. $tag = '-' . self::ORIENT_LANDSCAPE;
  355. if ($this->orientation == self::ORIENT_LANDSCAPE && is_string($this->format) && substr($this->format,
  356. -2) != $tag
  357. ) {
  358. $this->format .= $tag;
  359. }
  360. }
  361. }