pdflib_adapter.cls.php 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085
  1. <?php
  2. /**
  3. * @package dompdf
  4. * @link http://dompdf.github.com/
  5. * @author Benj Carson <benjcarson@digitaljunkies.ca>
  6. * @author Helmut Tischer <htischer@weihenstephan.org>
  7. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  8. */
  9. /**
  10. * PDF rendering interface
  11. *
  12. * PDFLib_Adapter provides a simple, stateless interface to the one
  13. * provided by PDFLib.
  14. *
  15. * Unless otherwise mentioned, all dimensions are in points (1/72 in).
  16. * The coordinate origin is in the top left corner and y values
  17. * increase downwards.
  18. *
  19. * See {@link http://www.pdflib.com/} for more complete documentation
  20. * on the underlying PDFlib functions.
  21. *
  22. * @package dompdf
  23. */
  24. class PDFLib_Adapter implements Canvas {
  25. /**
  26. * Dimensions of paper sizes in points
  27. *
  28. * @var array;
  29. */
  30. static public $PAPER_SIZES = array(); // Set to CPDF_Adapter::$PAPER_SIZES below.
  31. /**
  32. * Whether to create PDFs in memory or on disk
  33. *
  34. * @var bool
  35. */
  36. static $IN_MEMORY = true;
  37. /**
  38. * @var DOMPDF
  39. */
  40. private $_dompdf;
  41. /**
  42. * Instance of PDFLib class
  43. *
  44. * @var PDFlib
  45. */
  46. private $_pdf;
  47. /**
  48. * Name of temporary file used for PDFs created on disk
  49. *
  50. * @var string
  51. */
  52. private $_file;
  53. /**
  54. * PDF width, in points
  55. *
  56. * @var float
  57. */
  58. private $_width;
  59. /**
  60. * PDF height, in points
  61. *
  62. * @var float
  63. */
  64. private $_height;
  65. /**
  66. * Last fill color used
  67. *
  68. * @var array
  69. */
  70. private $_last_fill_color;
  71. /**
  72. * Last stroke color used
  73. *
  74. * @var array
  75. */
  76. private $_last_stroke_color;
  77. /**
  78. * Cache of image handles
  79. *
  80. * @var array
  81. */
  82. private $_imgs;
  83. /**
  84. * Cache of font handles
  85. *
  86. * @var array
  87. */
  88. private $_fonts;
  89. /**
  90. * List of objects (templates) to add to multiple pages
  91. *
  92. * @var array
  93. */
  94. private $_objs;
  95. /**
  96. * Current page number
  97. *
  98. * @var int
  99. */
  100. private $_page_number;
  101. /**
  102. * Total number of pages
  103. *
  104. * @var int
  105. */
  106. private $_page_count;
  107. /**
  108. * Text to display on every page
  109. *
  110. * @var array
  111. */
  112. private $_page_text;
  113. /**
  114. * Array of pages for accesing after rendering is initially complete
  115. *
  116. * @var array
  117. */
  118. private $_pages;
  119. /**
  120. * Class constructor
  121. *
  122. * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or
  123. * an array(xmin,ymin,xmax,ymax)
  124. * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
  125. * @param DOMPDF $dompdf
  126. */
  127. function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
  128. if ( is_array($paper) ) {
  129. $size = $paper;
  130. }
  131. else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) {
  132. $size = self::$PAPER_SIZES[mb_strtolower($paper)];
  133. }
  134. else {
  135. $size = self::$PAPER_SIZES["letter"];
  136. }
  137. if ( mb_strtolower($orientation) === "landscape" ) {
  138. list($size[2], $size[3]) = array($size[3], $size[2]);
  139. }
  140. $this->_width = $size[2] - $size[0];
  141. $this->_height= $size[3] - $size[1];
  142. $this->_dompdf = $dompdf;
  143. $this->_pdf = new PDFLib();
  144. if ( defined("DOMPDF_PDFLIB_LICENSE") )
  145. $this->_pdf->set_parameter( "license", DOMPDF_PDFLIB_LICENSE);
  146. $this->_pdf->set_parameter("textformat", "utf8");
  147. $this->_pdf->set_parameter("fontwarning", "false");
  148. $this->_pdf->set_info("Creator", "DOMPDF");
  149. // Silence pedantic warnings about missing TZ settings
  150. $tz = @date_default_timezone_get();
  151. date_default_timezone_set("UTC");
  152. $this->_pdf->set_info("Date", date("Y-m-d"));
  153. date_default_timezone_set($tz);
  154. if ( self::$IN_MEMORY )
  155. $this->_pdf->begin_document("","");
  156. else {
  157. $tmp_dir = $this->_dompdf->get_options("temp_dir");
  158. $tmp_name = tempnam($tmp_dir, "libdompdf_pdf_");
  159. @unlink($tmp_name);
  160. $this->_file = "$tmp_name.pdf";
  161. $this->_pdf->begin_document($this->_file,"");
  162. }
  163. $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
  164. $this->_page_number = $this->_page_count = 1;
  165. $this->_page_text = array();
  166. $this->_imgs = array();
  167. $this->_fonts = array();
  168. $this->_objs = array();
  169. // Set up font paths
  170. $families = Font_Metrics::get_font_families();
  171. foreach ($families as $files) {
  172. foreach ($files as $file) {
  173. $face = basename($file);
  174. $afm = null;
  175. // Prefer ttfs to afms
  176. if ( file_exists("$file.ttf") ) {
  177. $outline = "$file.ttf";
  178. } else if ( file_exists("$file.TTF") ) {
  179. $outline = "$file.TTF";
  180. } else if ( file_exists("$file.pfb") ) {
  181. $outline = "$file.pfb";
  182. if ( file_exists("$file.afm") ) {
  183. $afm = "$file.afm";
  184. }
  185. } else if ( file_exists("$file.PFB") ) {
  186. $outline = "$file.PFB";
  187. if ( file_exists("$file.AFM") ) {
  188. $afm = "$file.AFM";
  189. }
  190. } else {
  191. continue;
  192. }
  193. $this->_pdf->set_parameter("FontOutline", "\{$face\}=\{$outline\}");
  194. if ( !is_null($afm) ) {
  195. $this->_pdf->set_parameter("FontAFM", "\{$face\}=\{$afm\}");
  196. }
  197. }
  198. }
  199. }
  200. function get_dompdf(){
  201. return $this->_dompdf;
  202. }
  203. /**
  204. * Close the pdf
  205. */
  206. protected function _close() {
  207. $this->_place_objects();
  208. // Close all pages
  209. $this->_pdf->suspend_page("");
  210. for ($p = 1; $p <= $this->_page_count; $p++) {
  211. $this->_pdf->resume_page("pagenumber=$p");
  212. $this->_pdf->end_page_ext("");
  213. }
  214. $this->_pdf->end_document("");
  215. }
  216. /**
  217. * Returns the PDFLib instance
  218. *
  219. * @return PDFLib
  220. */
  221. function get_pdflib() {
  222. return $this->_pdf;
  223. }
  224. /**
  225. * Add meta information to the PDF
  226. *
  227. * @param string $label label of the value (Creator, Producter, etc.)
  228. * @param string $value the text to set
  229. */
  230. function add_info($label, $value) {
  231. $this->_pdf->set_info($label, $value);
  232. }
  233. /**
  234. * Opens a new 'object' (template in PDFLib-speak)
  235. *
  236. * While an object is open, all drawing actions are recorded to the
  237. * object instead of being drawn on the current page. Objects can
  238. * be added later to a specific page or to several pages.
  239. *
  240. * The return value is an integer ID for the new object.
  241. *
  242. * @see PDFLib_Adapter::close_object()
  243. * @see PDFLib_Adapter::add_object()
  244. *
  245. * @return int
  246. */
  247. function open_object() {
  248. $this->_pdf->suspend_page("");
  249. $ret = $this->_pdf->begin_template($this->_width, $this->_height);
  250. $this->_pdf->save();
  251. $this->_objs[$ret] = array("start_page" => $this->_page_number);
  252. return $ret;
  253. }
  254. /**
  255. * Reopen an existing object (NOT IMPLEMENTED)
  256. * PDFLib does not seem to support reopening templates.
  257. *
  258. * @param int $object the ID of a previously opened object
  259. *
  260. * @throws DOMPDF_Exception
  261. * @return void
  262. */
  263. function reopen_object($object) {
  264. throw new DOMPDF_Exception("PDFLib does not support reopening objects.");
  265. }
  266. /**
  267. * Close the current template
  268. *
  269. * @see PDFLib_Adapter::open_object()
  270. */
  271. function close_object() {
  272. $this->_pdf->restore();
  273. $this->_pdf->end_template();
  274. $this->_pdf->resume_page("pagenumber=".$this->_page_number);
  275. }
  276. /**
  277. * Adds the specified object to the document
  278. *
  279. * $where can be one of:
  280. * - 'add' add to current page only
  281. * - 'all' add to every page from the current one onwards
  282. * - 'odd' add to all odd numbered pages from now on
  283. * - 'even' add to all even numbered pages from now on
  284. * - 'next' add the object to the next page only
  285. * - 'nextodd' add to all odd numbered pages from the next one
  286. * - 'nexteven' add to all even numbered pages from the next one
  287. *
  288. * @param int $object the object handle returned by open_object()
  289. * @param string $where
  290. */
  291. function add_object($object, $where = 'all') {
  292. if ( mb_strpos($where, "next") !== false ) {
  293. $this->_objs[$object]["start_page"]++;
  294. $where = str_replace("next", "", $where);
  295. if ( $where == "" )
  296. $where = "add";
  297. }
  298. $this->_objs[$object]["where"] = $where;
  299. }
  300. /**
  301. * Stops the specified template from appearing in the document.
  302. *
  303. * The object will stop being displayed on the page following the
  304. * current one.
  305. *
  306. * @param int $object
  307. */
  308. function stop_object($object) {
  309. if ( !isset($this->_objs[$object]) )
  310. return;
  311. $start = $this->_objs[$object]["start_page"];
  312. $where = $this->_objs[$object]["where"];
  313. // Place the object on this page if required
  314. if ( $this->_page_number >= $start &&
  315. (($this->_page_number % 2 == 0 && $where === "even") ||
  316. ($this->_page_number % 2 == 1 && $where === "odd") ||
  317. ($where === "all")) ) {
  318. $this->_pdf->fit_image($object, 0, 0, "");
  319. }
  320. $this->_objs[$object] = null;
  321. unset($this->_objs[$object]);
  322. }
  323. /**
  324. * Add all active objects to the current page
  325. */
  326. protected function _place_objects() {
  327. foreach ( $this->_objs as $obj => $props ) {
  328. $start = $props["start_page"];
  329. $where = $props["where"];
  330. // Place the object on this page if required
  331. if ( $this->_page_number >= $start &&
  332. (($this->_page_number % 2 == 0 && $where === "even") ||
  333. ($this->_page_number % 2 == 1 && $where === "odd") ||
  334. ($where === "all")) ) {
  335. $this->_pdf->fit_image($obj,0,0,"");
  336. }
  337. }
  338. }
  339. function get_width() { return $this->_width; }
  340. function get_height() { return $this->_height; }
  341. function get_page_number() { return $this->_page_number; }
  342. function get_page_count() { return $this->_page_count; }
  343. function set_page_number($num) { $this->_page_number = (int)$num; }
  344. function set_page_count($count) { $this->_page_count = (int)$count; }
  345. /**
  346. * Sets the line style
  347. *
  348. * @param float $width
  349. * @param $cap
  350. * @param string $join
  351. * @param array $dash
  352. *
  353. * @return void
  354. */
  355. protected function _set_line_style($width, $cap, $join, $dash) {
  356. if ( count($dash) == 1 )
  357. $dash[] = $dash[0];
  358. if ( count($dash) > 1 )
  359. $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}");
  360. else
  361. $this->_pdf->setdash(0,0);
  362. switch ( $join ) {
  363. case "miter":
  364. $this->_pdf->setlinejoin(0);
  365. break;
  366. case "round":
  367. $this->_pdf->setlinejoin(1);
  368. break;
  369. case "bevel":
  370. $this->_pdf->setlinejoin(2);
  371. break;
  372. default:
  373. break;
  374. }
  375. switch ( $cap ) {
  376. case "butt":
  377. $this->_pdf->setlinecap(0);
  378. break;
  379. case "round":
  380. $this->_pdf->setlinecap(1);
  381. break;
  382. case "square":
  383. $this->_pdf->setlinecap(2);
  384. break;
  385. default:
  386. break;
  387. }
  388. $this->_pdf->setlinewidth($width);
  389. }
  390. /**
  391. * Sets the line color
  392. *
  393. * @param array $color array(r,g,b)
  394. */
  395. protected function _set_stroke_color($color) {
  396. if($this->_last_stroke_color == $color)
  397. return;
  398. $this->_last_stroke_color = $color;
  399. if (isset($color[3])) {
  400. $type = "cmyk";
  401. list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
  402. }
  403. elseif (isset($color[2])) {
  404. $type = "rgb";
  405. list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
  406. }
  407. else {
  408. $type = "gray";
  409. list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
  410. }
  411. $this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4);
  412. }
  413. /**
  414. * Sets the fill color
  415. *
  416. * @param array $color array(r,g,b)
  417. */
  418. protected function _set_fill_color($color) {
  419. if($this->_last_fill_color == $color)
  420. return;
  421. $this->_last_fill_color = $color;
  422. if (isset($color[3])) {
  423. $type = "cmyk";
  424. list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
  425. }
  426. elseif (isset($color[2])) {
  427. $type = "rgb";
  428. list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
  429. }
  430. else {
  431. $type = "gray";
  432. list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
  433. }
  434. $this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4);
  435. }
  436. /**
  437. * Sets the opacity
  438. *
  439. * @param $opacity
  440. * @param $mode
  441. */
  442. function set_opacity($opacity, $mode = "Normal") {
  443. if ( $mode === "Normal" ) {
  444. $gstate = $this->_pdf->create_gstate("opacityfill=$opacity opacitystroke=$opacity");
  445. $this->_pdf->set_gstate($gstate);
  446. }
  447. }
  448. function set_default_view($view, $options = array()) {
  449. // TODO
  450. // http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf
  451. /**
  452. * fitheight Fit the page height to the window, with the x coordinate left at the left edge of the window.
  453. * fitrect Fit the rectangle specified by left, bottom, right, and top to the window.
  454. * fitvisible Fit the visible contents of the page (the ArtBox) to the window.
  455. * fitvisibleheight Fit the visible contents of the page to the window with the x coordinate left at the left edge of the window.
  456. * fitvisiblewidth Fit the visible contents of the page to the window with the y coordinate top at the top edge of the window.
  457. * fitwidth Fit the page width to the window, with the y coordinate top at the top edge of the window.
  458. * fitwindow Fit the complete page to the window.
  459. * fixed
  460. */
  461. //$this->_pdf->set_parameter("openaction", $view);
  462. }
  463. /**
  464. * Loads a specific font and stores the corresponding descriptor.
  465. *
  466. * @param string $font
  467. * @param string $encoding
  468. * @param string $options
  469. *
  470. * @return int the font descriptor for the font
  471. */
  472. protected function _load_font($font, $encoding = null, $options = "") {
  473. // Check if the font is a native PDF font
  474. // Embed non-native fonts
  475. $test = strtolower(basename($font));
  476. if ( in_array($test, DOMPDF::$native_fonts) ) {
  477. $font = basename($font);
  478. } else {
  479. // Embed non-native fonts
  480. $options .= " embedding=true";
  481. }
  482. if ( is_null($encoding) ) {
  483. // Unicode encoding is only available for the commerical
  484. // version of PDFlib and not PDFlib-Lite
  485. if ( defined("DOMPDF_PDFLIB_LICENSE") )
  486. $encoding = "unicode";
  487. else
  488. $encoding = "auto";
  489. }
  490. $key = "$font:$encoding:$options";
  491. if ( isset($this->_fonts[$key]) )
  492. return $this->_fonts[$key];
  493. else {
  494. $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options);
  495. return $this->_fonts[$key];
  496. }
  497. }
  498. /**
  499. * Remaps y coords from 4th to 1st quadrant
  500. *
  501. * @param float $y
  502. * @return float
  503. */
  504. protected function y($y) { return $this->_height - $y; }
  505. //........................................................................
  506. /**
  507. * @param float $x1
  508. * @param float $y1
  509. * @param float $x2
  510. * @param float $y2
  511. * @param array $color
  512. * @param float $width
  513. * @param array $style
  514. */
  515. function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
  516. $this->_set_line_style($width, "butt", "", $style);
  517. $this->_set_stroke_color($color);
  518. $y1 = $this->y($y1);
  519. $y2 = $this->y($y2);
  520. $this->_pdf->moveto($x1, $y1);
  521. $this->_pdf->lineto($x2, $y2);
  522. $this->_pdf->stroke();
  523. }
  524. function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
  525. $this->_set_line_style($width, "butt", "", $style);
  526. $this->_set_stroke_color($color);
  527. $y1 = $this->y($y1);
  528. $this->_pdf->arc($x1, $y1, $r1, $astart, $aend);
  529. $this->_pdf->stroke();
  530. }
  531. //........................................................................
  532. function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
  533. $this->_set_stroke_color($color);
  534. $this->_set_line_style($width, "butt", "", $style);
  535. $y1 = $this->y($y1) - $h;
  536. $this->_pdf->rect($x1, $y1, $w, $h);
  537. $this->_pdf->stroke();
  538. }
  539. //........................................................................
  540. function filled_rectangle($x1, $y1, $w, $h, $color) {
  541. $this->_set_fill_color($color);
  542. $y1 = $this->y($y1) - $h;
  543. $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
  544. $this->_pdf->fill();
  545. }
  546. function clipping_rectangle($x1, $y1, $w, $h) {
  547. $this->_pdf->save();
  548. $y1 = $this->y($y1) - $h;
  549. $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
  550. $this->_pdf->clip();
  551. }
  552. function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
  553. // @todo
  554. $this->clipping_rectangle($x1, $y1, $w, $h);
  555. }
  556. function clipping_end() {
  557. $this->_pdf->restore();
  558. }
  559. function save() {
  560. $this->_pdf->save();
  561. }
  562. function restore() {
  563. $this->_pdf->restore();
  564. }
  565. function rotate($angle, $x, $y) {
  566. $pdf = $this->_pdf;
  567. $pdf->translate($x, $this->_height-$y);
  568. $pdf->rotate(-$angle);
  569. $pdf->translate(-$x, -$this->_height+$y);
  570. }
  571. function skew($angle_x, $angle_y, $x, $y) {
  572. $pdf = $this->_pdf;
  573. $pdf->translate($x, $this->_height-$y);
  574. $pdf->skew($angle_y, $angle_x); // Needs to be inverted
  575. $pdf->translate(-$x, -$this->_height+$y);
  576. }
  577. function scale($s_x, $s_y, $x, $y) {
  578. $pdf = $this->_pdf;
  579. $pdf->translate($x, $this->_height-$y);
  580. $pdf->scale($s_x, $s_y);
  581. $pdf->translate(-$x, -$this->_height+$y);
  582. }
  583. function translate($t_x, $t_y) {
  584. $this->_pdf->translate($t_x, -$t_y);
  585. }
  586. function transform($a, $b, $c, $d, $e, $f) {
  587. $this->_pdf->concat($a, $b, $c, $d, $e, $f);
  588. }
  589. //........................................................................
  590. function polygon($points, $color, $width = null, $style = null, $fill = false) {
  591. $this->_set_fill_color($color);
  592. $this->_set_stroke_color($color);
  593. if ( !$fill && isset($width) )
  594. $this->_set_line_style($width, "square", "miter", $style);
  595. $y = $this->y(array_pop($points));
  596. $x = array_pop($points);
  597. $this->_pdf->moveto($x,$y);
  598. while (count($points) > 1) {
  599. $y = $this->y(array_pop($points));
  600. $x = array_pop($points);
  601. $this->_pdf->lineto($x,$y);
  602. }
  603. if ( $fill )
  604. $this->_pdf->fill();
  605. else
  606. $this->_pdf->closepath_stroke();
  607. }
  608. //........................................................................
  609. function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
  610. $this->_set_fill_color($color);
  611. $this->_set_stroke_color($color);
  612. if ( !$fill && isset($width) )
  613. $this->_set_line_style($width, "round", "round", $style);
  614. $y = $this->y($y);
  615. $this->_pdf->circle($x, $y, $r);
  616. if ( $fill )
  617. $this->_pdf->fill();
  618. else
  619. $this->_pdf->stroke();
  620. }
  621. //........................................................................
  622. function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
  623. $w = (int)$w;
  624. $h = (int)$h;
  625. $img_type = Image_Cache::detect_type($img_url);
  626. $img_ext = Image_Cache::type_to_ext($img_type);
  627. if ( !isset($this->_imgs[$img_url]) ) {
  628. $this->_imgs[$img_url] = $this->_pdf->load_image($img_ext, $img_url, "");
  629. }
  630. $img = $this->_imgs[$img_url];
  631. $y = $this->y($y) - $h;
  632. $this->_pdf->fit_image($img, $x, $y, 'boxsize={'."$w $h".'} fitmethod=entire');
  633. }
  634. //........................................................................
  635. function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0, $char_spacing = 0, $angle = 0) {
  636. $fh = $this->_load_font($font);
  637. $this->_pdf->setfont($fh, $size);
  638. $this->_set_fill_color($color);
  639. $y = $this->y($y) - Font_Metrics::get_font_height($font, $size);
  640. $word_spacing = (float)$word_spacing;
  641. $char_spacing = (float)$char_spacing;
  642. $angle = -(float)$angle;
  643. $this->_pdf->fit_textline($text, $x, $y, "rotate=$angle wordspacing=$word_spacing charspacing=$char_spacing ");
  644. }
  645. //........................................................................
  646. function javascript($code) {
  647. if ( defined("DOMPDF_PDFLIB_LICENSE") ) {
  648. $this->_pdf->create_action("JavaScript", $code);
  649. }
  650. }
  651. //........................................................................
  652. /**
  653. * Add a named destination (similar to <a name="foo">...</a> in html)
  654. *
  655. * @param string $anchorname The name of the named destination
  656. */
  657. function add_named_dest($anchorname) {
  658. $this->_pdf->add_nameddest($anchorname,"");
  659. }
  660. //........................................................................
  661. /**
  662. * Add a link to the pdf
  663. *
  664. * @param string $url The url to link to
  665. * @param float $x The x position of the link
  666. * @param float $y The y position of the link
  667. * @param float $width The width of the link
  668. * @param float $height The height of the link
  669. */
  670. function add_link($url, $x, $y, $width, $height) {
  671. $y = $this->y($y) - $height;
  672. if ( strpos($url, '#') === 0 ) {
  673. // Local link
  674. $name = substr($url,1);
  675. if ( $name )
  676. $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} destname=". substr($url,1) . " linewidth=0");
  677. } else {
  678. list($proto, $host, $path, $file) = explode_url($url);
  679. if ( $proto == "" || $proto === "file://" )
  680. return; // Local links are not allowed
  681. $url = build_url($proto, $host, $path, $file);
  682. $url = '{' . rawurldecode($url) . '}';
  683. $action = $this->_pdf->create_action("URI", "url=" . $url);
  684. $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0");
  685. }
  686. }
  687. //........................................................................
  688. function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0) {
  689. $fh = $this->_load_font($font);
  690. // Determine the additional width due to extra spacing
  691. $num_spaces = mb_substr_count($text," ");
  692. $delta = $word_spacing * $num_spaces;
  693. if ( $letter_spacing ) {
  694. $num_chars = mb_strlen($text);
  695. $delta += ($num_chars - $num_spaces) * $letter_spacing;
  696. }
  697. return $this->_pdf->stringwidth($text, $fh, $size) + $delta;
  698. }
  699. //........................................................................
  700. function get_font_height($font, $size) {
  701. $fh = $this->_load_font($font);
  702. $this->_pdf->setfont($fh, $size);
  703. $asc = $this->_pdf->get_value("ascender", $fh);
  704. $desc = $this->_pdf->get_value("descender", $fh);
  705. // $desc is usually < 0,
  706. $ratio = $this->_dompdf->get_option("font_height_ratio");
  707. return $size * ($asc - $desc) * $ratio;
  708. }
  709. function get_font_baseline($font, $size) {
  710. $ratio = $this->_dompdf->get_option("font_height_ratio");
  711. return $this->get_font_height($font, $size) / $ratio * 1.1;
  712. }
  713. //........................................................................
  714. /**
  715. * Writes text at the specified x and y coordinates on every page
  716. *
  717. * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
  718. * with their current values.
  719. *
  720. * See {@link Style::munge_color()} for the format of the color array.
  721. *
  722. * @param float $x
  723. * @param float $y
  724. * @param string $text the text to write
  725. * @param string $font the font file to use
  726. * @param float $size the font size, in points
  727. * @param array $color
  728. * @param float $word_space word spacing adjustment
  729. * @param float $char_space char spacing adjustment
  730. * @param float $angle angle to write the text at, measured CW starting from the x-axis
  731. */
  732. function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
  733. $_t = "text";
  734. $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
  735. }
  736. //........................................................................
  737. /**
  738. * Processes a script on every page
  739. *
  740. * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
  741. *
  742. * This function can be used to add page numbers to all pages
  743. * after the first one, for example.
  744. *
  745. * @param string $code the script code
  746. * @param string $type the language type for script
  747. */
  748. function page_script($code, $type = "text/php") {
  749. $_t = "script";
  750. $this->_page_text[] = compact("_t", "code", "type");
  751. }
  752. //........................................................................
  753. function new_page() {
  754. // Add objects to the current page
  755. $this->_place_objects();
  756. $this->_pdf->suspend_page("");
  757. $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
  758. $this->_page_number = ++$this->_page_count;
  759. }
  760. //........................................................................
  761. /**
  762. * Add text to each page after rendering is complete
  763. */
  764. protected function _add_page_text() {
  765. if ( !count($this->_page_text) )
  766. return;
  767. $this->_pdf->suspend_page("");
  768. for ($p = 1; $p <= $this->_page_count; $p++) {
  769. $this->_pdf->resume_page("pagenumber=$p");
  770. foreach ($this->_page_text as $pt) {
  771. extract($pt);
  772. switch ($_t) {
  773. case "text":
  774. $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
  775. array($p, $this->_page_count), $text);
  776. $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
  777. break;
  778. case "script":
  779. if (!$eval) {
  780. $eval = new PHP_Evaluator($this);
  781. }
  782. $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count));
  783. break;
  784. }
  785. }
  786. $this->_pdf->suspend_page("");
  787. }
  788. $this->_pdf->resume_page("pagenumber=".$this->_page_number);
  789. }
  790. //........................................................................
  791. function stream($filename, $options = null) {
  792. // Add page text
  793. $this->_add_page_text();
  794. if ( isset($options["compress"]) && $options["compress"] != 1 )
  795. $this->_pdf->set_value("compress", 0);
  796. else
  797. $this->_pdf->set_value("compress", 6);
  798. $this->_close();
  799. $data = "";
  800. if ( self::$IN_MEMORY ) {
  801. $data = $this->_pdf->get_buffer();
  802. //$size = strlen($data);
  803. } else {
  804. //$size = filesize($this->_file);
  805. }
  806. $filename = str_replace(array("\n","'"),"", $filename);
  807. $attach = (isset($options["Attachment"]) && $options["Attachment"]) ? "attachment" : "inline";
  808. header("Cache-Control: private");
  809. header("Content-type: application/pdf");
  810. header("Content-Disposition: $attach; filename=\"$filename\"");
  811. //header("Content-length: " . $size);
  812. if ( self::$IN_MEMORY )
  813. echo $data;
  814. else {
  815. // Chunked readfile()
  816. $chunk = (1 << 21); // 2 MB
  817. $fh = fopen($this->_file, "rb");
  818. if ( !$fh )
  819. throw new DOMPDF_Exception("Unable to load temporary PDF file: " . $this->_file);
  820. while ( !feof($fh) )
  821. echo fread($fh,$chunk);
  822. fclose($fh);
  823. //debugpng
  824. if (DEBUGPNG) print '[pdflib stream unlink '.$this->_file.']';
  825. if (!DEBUGKEEPTEMP)
  826. unlink($this->_file);
  827. $this->_file = null;
  828. unset($this->_file);
  829. }
  830. flush();
  831. }
  832. //........................................................................
  833. function output($options = null) {
  834. // Add page text
  835. $this->_add_page_text();
  836. if ( isset($options["compress"]) && $options["compress"] != 1 )
  837. $this->_pdf->set_value("compress", 0);
  838. else
  839. $this->_pdf->set_value("compress", 6);
  840. $this->_close();
  841. if ( self::$IN_MEMORY )
  842. $data = $this->_pdf->get_buffer();
  843. else {
  844. $data = file_get_contents($this->_file);
  845. //debugpng
  846. if (DEBUGPNG) print '[pdflib output unlink '.$this->_file.']';
  847. if (!DEBUGKEEPTEMP)
  848. unlink($this->_file);
  849. $this->_file = null;
  850. unset($this->_file);
  851. }
  852. return $data;
  853. }
  854. }
  855. // Workaround for idiotic limitation on statics...
  856. PDFLib_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES;