commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / vendor / dompdf / dompdf / src / FrameDecorator / Table.php
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 namespace Dompdf\FrameDecorator;
9
10 use Dompdf\Cellmap;
11 use DOMNode;
12 use Dompdf\Dompdf;
13 use Dompdf\Frame;
14 use Dompdf\Frame\Factory;
15
16 /**
17 * Decorates Frames for table layout
18 *
19 * @package dompdf
20 */
21 class Table extends AbstractFrameDecorator
22 {
23 public static $VALID_CHILDREN = array(
24 "table-row-group",
25 "table-row",
26 "table-header-group",
27 "table-footer-group",
28 "table-column",
29 "table-column-group",
30 "table-caption",
31 "table-cell"
32 );
33
34 public static $ROW_GROUPS = array(
35 'table-row-group',
36 'table-header-group',
37 'table-footer-group'
38 );
39
40 /**
41 * The Cellmap object for this table. The cellmap maps table cells
42 * to rows and columns, and aids in calculating column widths.
43 *
44 * @var \Dompdf\Cellmap
45 */
46 protected $_cellmap;
47
48 /**
49 * The minimum width of the table, in pt
50 *
51 * @var float
52 */
53 protected $_min_width;
54
55 /**
56 * The maximum width of the table, in pt
57 *
58 * @var float
59 */
60 protected $_max_width;
61
62 /**
63 * Table header rows. Each table header is duplicated when a table
64 * spans pages.
65 *
66 * @var array
67 */
68 protected $_headers;
69
70 /**
71 * Table footer rows. Each table footer is duplicated when a table
72 * spans pages.
73 *
74 * @var array
75 */
76 protected $_footers;
77
78 /**
79 * Class constructor
80 *
81 * @param Frame $frame the frame to decorate
82 * @param Dompdf $dompdf
83 */
84 public function __construct(Frame $frame, Dompdf $dompdf)
85 {
86 parent::__construct($frame, $dompdf);
87 $this->_cellmap = new Cellmap($this);
88
89 if ($frame->get_style()->table_layout === "fixed") {
90 $this->_cellmap->set_layout_fixed(true);
91 }
92
93 $this->_min_width = null;
94 $this->_max_width = null;
95 $this->_headers = array();
96 $this->_footers = array();
97 }
98
99
100 public function reset()
101 {
102 parent::reset();
103 $this->_cellmap->reset();
104 $this->_min_width = null;
105 $this->_max_width = null;
106 $this->_headers = array();
107 $this->_footers = array();
108 $this->_reflower->reset();
109 }
110
111 //........................................................................
112
113 /**
114 * split the table at $row. $row and all subsequent rows will be
115 * added to the clone. This method is overidden in order to remove
116 * frames from the cellmap properly.
117 *
118 * @param Frame $child
119 * @param bool $force_pagebreak
120 *
121 * @return void
122 */
123 public function split(Frame $child = null, $force_pagebreak = false)
124 {
125 if (is_null($child)) {
126 parent::split();
127
128 return;
129 }
130
131 // If $child is a header or if it is the first non-header row, do
132 // not duplicate headers, simply move the table to the next page.
133 if (count($this->_headers) && !in_array($child, $this->_headers, true) &&
134 !in_array($child->get_prev_sibling(), $this->_headers, true)
135 ) {
136
137 $first_header = null;
138
139 // Insert copies of the table headers before $child
140 foreach ($this->_headers as $header) {
141
142 $new_header = $header->deep_copy();
143
144 if (is_null($first_header)) {
145 $first_header = $new_header;
146 }
147
148 $this->insert_child_before($new_header, $child);
149 }
150
151 parent::split($first_header);
152
153 } elseif (in_array($child->get_style()->display, self::$ROW_GROUPS)) {
154
155 // Individual rows should have already been handled
156 parent::split($child);
157
158 } else {
159
160 $iter = $child;
161
162 while ($iter) {
163 $this->_cellmap->remove_row($iter);
164 $iter = $iter->get_next_sibling();
165 }
166
167 parent::split($child);
168 }
169 }
170
171 /**
172 * Return a copy of this frame with $node as its node
173 *
174 * @param DOMNode $node
175 *
176 * @return Frame
177 */
178 public function copy(DOMNode $node)
179 {
180 $deco = parent::copy($node);
181
182 // In order to keep columns' widths through pages
183 $deco->_cellmap->set_columns($this->_cellmap->get_columns());
184 $deco->_cellmap->lock_columns();
185
186 return $deco;
187 }
188
189 /**
190 * Static function to locate the parent table of a frame
191 *
192 * @param Frame $frame
193 *
194 * @return Table the table that is an ancestor of $frame
195 */
196 public static function find_parent_table(Frame $frame)
197 {
198
199 while ($frame = $frame->get_parent())
200 if ($frame->is_table())
201 break;
202
203 return $frame;
204 }
205
206 /**
207 * Return this table's Cellmap
208 *
209 * @return \Dompdf\Cellmap
210 */
211 public function get_cellmap()
212 {
213 return $this->_cellmap;
214 }
215
216 /**
217 * Return the minimum width of this table
218 *
219 * @return float
220 */
221 public function get_min_width()
222 {
223 return $this->_min_width;
224 }
225
226 /**
227 * Return the maximum width of this table
228 *
229 * @return float
230 */
231 public function get_max_width()
232 {
233 return $this->_max_width;
234 }
235
236 /**
237 * Set the minimum width of the table
238 *
239 * @param float $width the new minimum width
240 */
241 public function set_min_width($width)
242 {
243 $this->_min_width = $width;
244 }
245
246 /**
247 * Set the maximum width of the table
248 *
249 * @param float $width the new maximum width
250 */
251 public function set_max_width($width)
252 {
253 $this->_max_width = $width;
254 }
255
256 /**
257 * Restructure tree so that the table has the correct structure.
258 * Invalid children (i.e. all non-table-rows) are moved below the
259 * table.
260 */
261 public function normalise()
262 {
263 // Store frames generated by invalid tags and move them outside the table
264 $erroneous_frames = array();
265 $anon_row = false;
266 $iter = $this->get_first_child();
267 while ($iter) {
268 $child = $iter;
269 $iter = $iter->get_next_sibling();
270
271 $display = $child->get_style()->display;
272
273 if ($anon_row) {
274
275 if ($display === "table-row") {
276 // Add the previous anonymous row
277 $this->insert_child_before($table_row, $child);
278
279 $table_row->normalise();
280 $child->normalise();
281 $anon_row = false;
282 continue;
283 }
284
285 // add the child to the anonymous row
286 $table_row->append_child($child);
287 continue;
288
289 } else {
290
291 if ($display === "table-row") {
292 $child->normalise();
293 continue;
294 }
295
296 if ($display === "table-cell") {
297 // Create an anonymous table row
298 $tr = $this->get_node()->ownerDocument->createElement("tr");
299
300 $frame = new Frame($tr);
301
302 $css = $this->get_style()->get_stylesheet();
303 $style = $css->create_style();
304 $style->inherit($this->get_style());
305
306 // Lookup styles for tr tags. If the user wants styles to work
307 // better, they should make the tr explicit... I'm not going to
308 // try to guess what they intended.
309 if ($tr_style = $css->lookup("tr")) {
310 $style->merge($tr_style);
311 }
312
313 // Okay, I have absolutely no idea why I need this clone here, but
314 // if it's omitted, php (as of 2004-07-28) segfaults.
315 $frame->set_style(clone $style);
316 $table_row = Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
317
318 // Add the cell to the row
319 $table_row->append_child($child);
320
321 $anon_row = true;
322 continue;
323 }
324
325 if (!in_array($display, self::$VALID_CHILDREN)) {
326 $erroneous_frames[] = $child;
327 continue;
328 }
329
330 // Normalise other table parts (i.e. row groups)
331 foreach ($child->get_children() as $grandchild) {
332 if ($grandchild->get_style()->display === "table-row") {
333 $grandchild->normalise();
334 }
335 }
336
337 // Add headers and footers
338 if ($display === "table-header-group") {
339 $this->_headers[] = $child;
340 } elseif ($display === "table-footer-group") {
341 $this->_footers[] = $child;
342 }
343 }
344 }
345
346 if ($anon_row && $table_row instanceof DOMNode) {
347 // Add the row to the table
348 $this->_frame->append_child($table_row);
349 $table_row->normalise();
350 $this->_cellmap->add_row();
351 }
352
353 foreach ($erroneous_frames as $frame) {
354 $this->move_after($frame);
355 }
356 }
357
358 //........................................................................
359
360 /**
361 * Moves the specified frame and it's corresponding node outside of
362 * the table.
363 *
364 * @param Frame $frame the frame to move
365 */
366 public function move_after(Frame $frame)
367 {
368 $this->get_parent()->insert_child_after($frame, $this);
369 }
370 }