説明なし

frame_decorator.cls.php 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. <?php
  2. /**
  3. * @package dompdf
  4. * @link http://dompdf.github.com/
  5. * @author Benj Carson <benjcarson@digitaljunkies.ca>
  6. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  7. */
  8. /**
  9. * Base Frame_Decorator class
  10. *
  11. * @access private
  12. * @package dompdf
  13. */
  14. abstract class Frame_Decorator extends Frame
  15. {
  16. const DEFAULT_COUNTER = "-dompdf-default-counter";
  17. public $_counters = array(); // array([id] => counter_value) (for generated content)
  18. /**
  19. * The root node of the DOM tree
  20. *
  21. * @var Frame
  22. */
  23. protected $_root;
  24. /**
  25. * The decorated frame
  26. *
  27. * @var Frame
  28. */
  29. protected $_frame;
  30. /**
  31. * Positioner object used to position this frame (Strategy pattern)
  32. *
  33. * @var Positioner
  34. */
  35. protected $_positioner;
  36. /**
  37. * Reflower object used to calculate frame dimensions (Strategy pattern)
  38. *
  39. * @var Frame_Reflower
  40. */
  41. protected $_reflower;
  42. /**
  43. * Reference to the current dompdf instance
  44. *
  45. * @var DOMPDF
  46. */
  47. protected $_dompdf;
  48. /**
  49. * First block parent
  50. *
  51. * @var Block_Frame_Decorator
  52. */
  53. private $_block_parent;
  54. /**
  55. * First positionned parent (position: relative | absolute | fixed)
  56. *
  57. * @var Frame_Decorator
  58. */
  59. private $_positionned_parent;
  60. /**
  61. * Class constructor
  62. *
  63. * @param Frame $frame The decoration target
  64. * @param DOMPDF $dompdf The DOMPDF object
  65. */
  66. function __construct(Frame $frame, DOMPDF $dompdf)
  67. {
  68. $this->_frame = $frame;
  69. $this->_root = null;
  70. $this->_dompdf = $dompdf;
  71. $frame->set_decorator($this);
  72. }
  73. /**
  74. * "Destructor": foribly free all references held by this object
  75. *
  76. * @param bool $recursive if true, call dispose on all children
  77. */
  78. function dispose($recursive = false)
  79. {
  80. if ($recursive) {
  81. while ($child = $this->get_first_child()) {
  82. $child->dispose(true);
  83. }
  84. }
  85. $this->_root = null;
  86. unset($this->_root);
  87. $this->_frame->dispose(true);
  88. $this->_frame = null;
  89. unset($this->_frame);
  90. $this->_positioner = null;
  91. unset($this->_positioner);
  92. $this->_reflower = null;
  93. unset($this->_reflower);
  94. }
  95. /**
  96. * Return a copy of this frame with $node as its node
  97. *
  98. * @param DOMNode $node
  99. *
  100. * @return Frame
  101. */
  102. function copy(DOMNode $node)
  103. {
  104. $frame = new Frame($node);
  105. $frame->set_style(clone $this->_frame->get_original_style());
  106. return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
  107. }
  108. /**
  109. * Create a deep copy: copy this node and all children
  110. *
  111. * @return Frame
  112. */
  113. function deep_copy()
  114. {
  115. $frame = new Frame($this->get_node()->cloneNode());
  116. $frame->set_style(clone $this->_frame->get_original_style());
  117. $deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
  118. foreach ($this->get_children() as $child) {
  119. $deco->append_child($child->deep_copy());
  120. }
  121. return $deco;
  122. }
  123. /**
  124. * Delegate calls to decorated frame object
  125. */
  126. function reset()
  127. {
  128. $this->_frame->reset();
  129. $this->_counters = array();
  130. // Reset all children
  131. foreach ($this->get_children() as $child) {
  132. $child->reset();
  133. }
  134. }
  135. // Getters -----------
  136. function get_id()
  137. {
  138. return $this->_frame->get_id();
  139. }
  140. /**
  141. * @return Frame
  142. */
  143. function get_frame()
  144. {
  145. return $this->_frame;
  146. }
  147. /**
  148. * @return DOMElement|DOMText
  149. */
  150. function get_node()
  151. {
  152. return $this->_frame->get_node();
  153. }
  154. /**
  155. * @return Style
  156. */
  157. function get_style()
  158. {
  159. return $this->_frame->get_style();
  160. }
  161. /**
  162. * @return Style
  163. */
  164. function get_original_style()
  165. {
  166. return $this->_frame->get_original_style();
  167. }
  168. /**
  169. * @param integer $i
  170. *
  171. * @return array|float
  172. */
  173. function get_containing_block($i = null)
  174. {
  175. return $this->_frame->get_containing_block($i);
  176. }
  177. /**
  178. * @param integer $i
  179. *
  180. * @return array|float
  181. */
  182. function get_position($i = null)
  183. {
  184. return $this->_frame->get_position($i);
  185. }
  186. /**
  187. * @return DOMPDF
  188. */
  189. function get_dompdf()
  190. {
  191. return $this->_dompdf;
  192. }
  193. /**
  194. * @return float
  195. */
  196. function get_margin_height()
  197. {
  198. return $this->_frame->get_margin_height();
  199. }
  200. /**
  201. * @return float
  202. */
  203. function get_margin_width()
  204. {
  205. return $this->_frame->get_margin_width();
  206. }
  207. /**
  208. * @return array
  209. */
  210. function get_padding_box()
  211. {
  212. return $this->_frame->get_padding_box();
  213. }
  214. /**
  215. * @return array
  216. */
  217. function get_border_box()
  218. {
  219. return $this->_frame->get_border_box();
  220. }
  221. /**
  222. * @param integer $id
  223. */
  224. function set_id($id)
  225. {
  226. $this->_frame->set_id($id);
  227. }
  228. /**
  229. * @param Style $style
  230. */
  231. function set_style(Style $style)
  232. {
  233. $this->_frame->set_style($style);
  234. }
  235. /**
  236. * @param float $x
  237. * @param float $y
  238. * @param float $w
  239. * @param float $h
  240. */
  241. function set_containing_block($x = null, $y = null, $w = null, $h = null)
  242. {
  243. $this->_frame->set_containing_block($x, $y, $w, $h);
  244. }
  245. /**
  246. * @param float $x
  247. * @param float $y
  248. */
  249. function set_position($x = null, $y = null)
  250. {
  251. $this->_frame->set_position($x, $y);
  252. }
  253. /**
  254. * @return string
  255. */
  256. function __toString()
  257. {
  258. return $this->_frame->__toString();
  259. }
  260. /**
  261. * @param Frame $child
  262. * @param bool $update_node
  263. */
  264. function prepend_child(Frame $child, $update_node = true)
  265. {
  266. while ($child instanceof Frame_Decorator) {
  267. $child = $child->_frame;
  268. }
  269. $this->_frame->prepend_child($child, $update_node);
  270. }
  271. /**
  272. * @param Frame $child
  273. * @param bool $update_node
  274. */
  275. function append_child(Frame $child, $update_node = true)
  276. {
  277. while ($child instanceof Frame_Decorator) {
  278. $child = $child->_frame;
  279. }
  280. $this->_frame->append_child($child, $update_node);
  281. }
  282. /**
  283. * @param Frame $new_child
  284. * @param Frame $ref
  285. * @param bool $update_node
  286. */
  287. function insert_child_before(Frame $new_child, Frame $ref, $update_node = true)
  288. {
  289. while ($new_child instanceof Frame_Decorator) {
  290. $new_child = $new_child->_frame;
  291. }
  292. if ($ref instanceof Frame_Decorator) {
  293. $ref = $ref->_frame;
  294. }
  295. $this->_frame->insert_child_before($new_child, $ref, $update_node);
  296. }
  297. /**
  298. * @param Frame $new_child
  299. * @param Frame $ref
  300. * @param bool $update_node
  301. */
  302. function insert_child_after(Frame $new_child, Frame $ref, $update_node = true)
  303. {
  304. while ($new_child instanceof Frame_Decorator) {
  305. $new_child = $new_child->_frame;
  306. }
  307. while ($ref instanceof Frame_Decorator) {
  308. $ref = $ref->_frame;
  309. }
  310. $this->_frame->insert_child_after($new_child, $ref, $update_node);
  311. }
  312. /**
  313. * @param Frame $child
  314. * @param bool $update_node
  315. *
  316. * @return Frame
  317. */
  318. function remove_child(Frame $child, $update_node = true)
  319. {
  320. while ($child instanceof Frame_Decorator) {
  321. $child = $child->_frame;
  322. }
  323. return $this->_frame->remove_child($child, $update_node);
  324. }
  325. /**
  326. * @return Frame_Decorator
  327. */
  328. function get_parent()
  329. {
  330. $p = $this->_frame->get_parent();
  331. if ($p && $deco = $p->get_decorator()) {
  332. while ($tmp = $deco->get_decorator()) {
  333. $deco = $tmp;
  334. }
  335. return $deco;
  336. } else if ($p) {
  337. return $p;
  338. }
  339. return null;
  340. }
  341. /**
  342. * @return Frame_Decorator
  343. */
  344. function get_first_child()
  345. {
  346. $c = $this->_frame->get_first_child();
  347. if ($c && $deco = $c->get_decorator()) {
  348. while ($tmp = $deco->get_decorator()) {
  349. $deco = $tmp;
  350. }
  351. return $deco;
  352. } else if ($c) {
  353. return $c;
  354. }
  355. return null;
  356. }
  357. /**
  358. * @return Frame_Decorator
  359. */
  360. function get_last_child()
  361. {
  362. $c = $this->_frame->get_last_child();
  363. if ($c && $deco = $c->get_decorator()) {
  364. while ($tmp = $deco->get_decorator()) {
  365. $deco = $tmp;
  366. }
  367. return $deco;
  368. } else if ($c) {
  369. return $c;
  370. }
  371. return null;
  372. }
  373. /**
  374. * @return Frame_Decorator
  375. */
  376. function get_prev_sibling()
  377. {
  378. $s = $this->_frame->get_prev_sibling();
  379. if ($s && $deco = $s->get_decorator()) {
  380. while ($tmp = $deco->get_decorator()) {
  381. $deco = $tmp;
  382. }
  383. return $deco;
  384. } else if ($s) {
  385. return $s;
  386. }
  387. return null;
  388. }
  389. /**
  390. * @return Frame_Decorator
  391. */
  392. function get_next_sibling()
  393. {
  394. $s = $this->_frame->get_next_sibling();
  395. if ($s && $deco = $s->get_decorator()) {
  396. while ($tmp = $deco->get_decorator()) {
  397. $deco = $tmp;
  398. }
  399. return $deco;
  400. } else if ($s) {
  401. return $s;
  402. }
  403. return null;
  404. }
  405. /**
  406. * @return FrameTreeList
  407. */
  408. function get_subtree()
  409. {
  410. return new FrameTreeList($this);
  411. }
  412. function set_positioner(Positioner $posn)
  413. {
  414. $this->_positioner = $posn;
  415. if ($this->_frame instanceof Frame_Decorator) {
  416. $this->_frame->set_positioner($posn);
  417. }
  418. }
  419. function set_reflower(Frame_Reflower $reflower)
  420. {
  421. $this->_reflower = $reflower;
  422. if ($this->_frame instanceof Frame_Decorator) {
  423. $this->_frame->set_reflower($reflower);
  424. }
  425. }
  426. /**
  427. * @return Frame_Reflower
  428. */
  429. function get_reflower()
  430. {
  431. return $this->_reflower;
  432. }
  433. /**
  434. * @param Frame $root
  435. */
  436. function set_root(Frame $root)
  437. {
  438. $this->_root = $root;
  439. if ($this->_frame instanceof Frame_Decorator) {
  440. $this->_frame->set_root($root);
  441. }
  442. }
  443. /**
  444. * @return Page_Frame_Decorator
  445. */
  446. function get_root()
  447. {
  448. return $this->_root;
  449. }
  450. /**
  451. * @return Block_Frame_Decorator
  452. */
  453. function find_block_parent()
  454. {
  455. // Find our nearest block level parent
  456. $p = $this->get_parent();
  457. while ($p) {
  458. if ($p->is_block()) {
  459. break;
  460. }
  461. $p = $p->get_parent();
  462. }
  463. return $this->_block_parent = $p;
  464. }
  465. /**
  466. * @return Frame_Decorator
  467. */
  468. function find_positionned_parent()
  469. {
  470. // Find our nearest relative positionned parent
  471. $p = $this->get_parent();
  472. while ($p) {
  473. if ($p->is_positionned()) {
  474. break;
  475. }
  476. $p = $p->get_parent();
  477. }
  478. if (!$p) {
  479. $p = $this->_root->get_first_child(); // <body>
  480. }
  481. return $this->_positionned_parent = $p;
  482. }
  483. /**
  484. * split this frame at $child.
  485. * The current frame is cloned and $child and all children following
  486. * $child are added to the clone. The clone is then passed to the
  487. * current frame's parent->split() method.
  488. *
  489. * @param Frame $child
  490. * @param boolean $force_pagebreak
  491. *
  492. * @throws DOMPDF_Exception
  493. * @return void
  494. */
  495. function split(Frame $child = null, $force_pagebreak = false)
  496. {
  497. // decrement any counters that were incremented on the current node
  498. $style = $this->_frame->get_style();
  499. if ($style->counter_increment && ($decrement = $style->counter_increment) !== "none") {
  500. $this->decrement_counters($decrement);
  501. }
  502. if (is_null($child)) {
  503. // check for counter increment on :before content (always a child of the selected element @link Frame_Reflower::_set_content)
  504. // this can push the current node to the next page before counter rules have bubbled up (but only if
  505. // it's been rendered, thus the position check)
  506. if (!$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id")) {
  507. foreach ($this->_frame->get_children() as $child) {
  508. if ($this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && $child->get_position('x') !== NULL) {
  509. $style = $child->get_style();
  510. if ($style->counter_increment && ($decrement = $style->counter_increment) !== "none") {
  511. $this->decrement_counters($decrement);
  512. }
  513. }
  514. }
  515. }
  516. $this->get_parent()->split($this, $force_pagebreak);
  517. return;
  518. }
  519. if ($child->get_parent() !== $this) {
  520. throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
  521. }
  522. $node = $this->_frame->get_node();
  523. $split = $this->copy($node->cloneNode());
  524. $this->get_parent()->insert_child_after($split, $this);
  525. // Add $frame and all following siblings to the new split node
  526. $iter = $child;
  527. while ($iter) {
  528. $frame = $iter;
  529. $iter = $iter->get_next_sibling();
  530. $frame->reset();
  531. $split->append_child($frame);
  532. }
  533. $this->get_parent()->split($split, $force_pagebreak);
  534. // If this node resets a counter save the current value to use when rendering on the next page
  535. if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") {
  536. $vars = preg_split('/\s+/', trim($reset), 2);
  537. $split->_counters['__' . $vars[0]] = $this->lookup_counter_frame($vars[0])->_counters[$vars[0]];
  538. }
  539. }
  540. function reset_counter($id = self::DEFAULT_COUNTER, $value = 0)
  541. {
  542. $this->get_parent()->_counters[$id] = intval($value);
  543. }
  544. function decrement_counters($counters)
  545. {
  546. foreach ($counters as $id => $increment) {
  547. $this->increment_counter($id, intval($increment) * -1);
  548. }
  549. }
  550. function increment_counters($counters)
  551. {
  552. foreach ($counters as $id => $increment) {
  553. $this->increment_counter($id, intval($increment));
  554. }
  555. }
  556. function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1)
  557. {
  558. $counter_frame = $this->lookup_counter_frame($id);
  559. if ($counter_frame) {
  560. if (!isset($counter_frame->_counters[$id])) {
  561. $counter_frame->_counters[$id] = 0;
  562. }
  563. $counter_frame->_counters[$id] += $increment;
  564. }
  565. }
  566. function lookup_counter_frame($id = self::DEFAULT_COUNTER)
  567. {
  568. $f = $this->get_parent();
  569. while ($f) {
  570. if (isset($f->_counters[$id])) {
  571. return $f;
  572. }
  573. $fp = $f->get_parent();
  574. if (!$fp) {
  575. return $f;
  576. }
  577. $f = $fp;
  578. }
  579. }
  580. // TODO: What version is the best : this one or the one in List_Bullet_Renderer ?
  581. function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal")
  582. {
  583. $type = mb_strtolower($type);
  584. if (!isset($this->_counters[$id])) {
  585. $this->_counters[$id] = 0;
  586. }
  587. $value = $this->_counters[$id];
  588. switch ($type) {
  589. default:
  590. case "decimal":
  591. return $value;
  592. case "decimal-leading-zero":
  593. return str_pad($value, 2, "0");
  594. case "lower-roman":
  595. return dec2roman($value);
  596. case "upper-roman":
  597. return mb_strtoupper(dec2roman($value));
  598. case "lower-latin":
  599. case "lower-alpha":
  600. return chr(($value % 26) + ord('a') - 1);
  601. case "upper-latin":
  602. case "upper-alpha":
  603. return chr(($value % 26) + ord('A') - 1);
  604. case "lower-greek":
  605. return unichr($value + 944);
  606. case "upper-greek":
  607. return unichr($value + 912);
  608. }
  609. }
  610. final function position()
  611. {
  612. $this->_positioner->position();
  613. }
  614. final function move($offset_x, $offset_y, $ignore_self = false)
  615. {
  616. $this->_positioner->move($offset_x, $offset_y, $ignore_self);
  617. }
  618. final function reflow(Block_Frame_Decorator $block = null)
  619. {
  620. // Uncomment this to see the frames before they're laid out, instead of
  621. // during rendering.
  622. //echo $this->_frame; flush();
  623. $this->_reflower->reflow($block);
  624. }
  625. final function get_min_max_width()
  626. {
  627. return $this->_reflower->get_min_max_width();
  628. }
  629. }