source: trunk/filemanager/tp/dompdf/include/cellmap.cls.php @ 7655

Revision 7655, 19.7 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

Line 
1<?php
2/**
3 * DOMPDF - PHP5 HTML to PDF renderer
4 *
5 * File: $RCSfile: cellmap.cls.php,v $
6 * Created on: 2004-07-28
7 *
8 * Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this library in the file LICENSE.LGPL; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 * 02111-1307 USA
24 *
25 * Alternatively, you may distribute this software under the terms of the
26 * PHP License, version 3.0 or later.  A copy of this license should have
27 * been distributed with this file in the file LICENSE.PHP .  If this is not
28 * the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
29 *
30 * The latest version of DOMPDF might be available at:
31 * http://www.digitaljunkies.ca/dompdf
32 *
33 * @link http://www.digitaljunkies.ca/dompdf
34 * @copyright 2004 Benj Carson
35 * @author Benj Carson <benjcarson@digitaljunkies.ca>
36 * @package dompdf
37 * @version 0.5.1
38 */
39
40/* $Id: cellmap.cls.php 185 2009-10-19 22:40:21Z eclecticgeek@gmail.com $ */
41
42/**
43 * Maps table cells to the table grid.
44 *
45 * This class resolves borders in tables with collapsed borders and helps
46 * place row & column spanned table cells.
47 *
48 * @access private
49 * @package dompdf
50 */
51class Cellmap {
52
53  /**
54   * Border style weight lookup for collapsed border resolution.
55   *
56   * @var array
57   */
58  static protected $_BORDER_STYLE_SCORE = array("inset"  => 1,
59                                                "groove" => 2,
60                                                "outset" => 3,
61                                                "ridge"  => 4,
62                                                "dotted" => 5,
63                                                "dashed" => 6,
64                                                "solid"  => 7,
65                                                "double" => 8,
66                                                "none"   => 0);
67
68  /**
69   * The table object this cellmap is attached to.
70   *
71   * @var Table_Frame_Decorator
72   */
73  protected $_table;
74
75  /**
76   * The total number of rows in the table
77   *
78   * @var int
79   */
80  protected $_num_rows;
81
82  /**
83   * The total number of columns in the table
84   *
85   * @var int
86   */
87  protected $_num_cols;
88
89  /**
90   * 2D array mapping <row,column> to frames
91   *
92   * @var array
93   */
94  protected $_cells;
95
96  /**
97   * 1D array of column dimensions
98   *
99   * @var array
100   */
101  protected $_columns;
102
103  /**
104   * 1D array of row dimensions
105   *
106   * @var array
107   */
108  protected $_rows;
109
110  /**
111   * 2D array of border specs
112   *
113   * @var array
114   */
115  protected $_borders;
116
117  /**
118   * 1D Array mapping frames to (multiple) <row, col> pairs, keyed on
119   * frame_id.
120   *
121   * @var array
122   */
123  protected $_frames;
124
125  /**
126   * Current column when adding cells, 0-based
127   *
128   * @var int
129   */
130  private $__col;
131
132  /**
133   * Current row when adding cells, 0-based
134   *
135   * @var int
136   */
137  private $__row;
138
139  //........................................................................
140
141  function __construct(Table_Frame_Decorator $table) {
142    $this->_table = $table;
143    $this->reset();
144  }
145
146  //........................................................................
147
148  function reset() {
149    $this->_num_rows = 0;
150    $this->_num_cols = 0;
151
152    $this->_cells  = array();
153    $this->_frames = array();
154
155    $this->_columns = array();
156    $this->_rows = array();
157
158    $this->_borders = array();
159
160    $this->__col = $this->__row = 0;
161  }
162
163  //........................................................................
164
165  function get_num_rows() { return $this->_num_rows; }
166  function get_num_cols() { return $this->_num_cols; }
167
168  function &get_columns() {
169    return $this->_columns;
170  }
171
172  function &get_column($i) {
173    if ( !isset($this->_columns[$i]) )
174      $this->_columns[$i] = array("x" => 0,
175                                  "min-width" => 0,
176                                  "max-width" => 0,
177                                  "used-width" => null,
178                                  "absolute" => 0,
179                                  "percent" => 0,
180                                  "auto" => true);
181
182    return $this->_columns[$i];
183  }
184
185  function &get_rows() {
186    return $this->_rows;
187  }
188
189  function &get_row($j) {
190    if ( !isset($this->_rows[$j]) )
191      $this->_rows[$j] = array("y" => 0,
192                               "first-column" => 0,
193                               "height" => null);
194    return $this->_rows[$j];
195  }
196
197  function get_border($i, $j, $h_v, $prop = null) {
198    if ( !isset($this->_borders[$i][$j][$h_v]) )
199      $this->_borders[$i][$j][$h_v] = array("width" => 0,
200                                           "style" => "solid",
201                                           "color" => "black");
202    if ( isset($prop) )
203      return $this->_borders[$i][$j][$h_v][$prop];
204
205    return $this->_borders[$i][$j][$h_v];
206  }
207
208  function get_border_properties($i, $j) {
209
210    $left = $this->get_border($i, $j, "vertical");
211    $right = $this->get_border($i, $j+1, "vertical");
212    $top = $this->get_border($i, $j, "horizontal");
213    $bottom = $this->get_border($i+1, $j, "horizontal");
214
215    return compact("top", "bottom", "left", "right");
216  }
217
218  //........................................................................
219
220  function get_spanned_cells($frame) {
221    $key = $frame->get_id();
222
223    if ( !isset($this->_frames[$key]) ) {
224      throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
225    }
226
227    return $this->_frames[$key];
228
229  }
230
231  function frame_exists_in_cellmap($frame) {
232    $key = $frame->get_id();
233    return isset($this->_frames[$key]);
234  }
235 
236  function get_frame_position($frame) {
237    global $_dompdf_warnings;
238
239    $key = $frame->get_id();
240
241    if ( !isset($this->_frames[$key]) ) {
242      throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
243    }
244
245    $col = $this->_frames[$key]["columns"][0];
246    $row = $this->_frames[$key]["rows"][0];
247
248    if ( !isset($this->_columns[$col])) {
249      $_dompdf_warnings[] = "Frame not found in columns array.  Check your table layout for missing or extra TDs.";
250      $x = 0;
251    } else
252      $x = $this->_columns[$col]["x"];
253
254    if ( !isset($this->_rows[$row])) {
255      $_dompdf_warnings[] = "Frame not found in row array.  Check your table layout for missing or extra TDs.";
256      $y = 0;
257    } else
258      $y = $this->_rows[$row]["y"];
259
260    return array($x, $y, "x" => $x, "y" => $y);
261  }
262
263  function get_frame_width($frame) {
264    $key = $frame->get_id();
265
266    if ( !isset($this->_frames[$key]) ) {
267      throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
268    }
269
270    $cols = $this->_frames[$key]["columns"];
271    $w = 0;
272    foreach ($cols as $i)
273      $w += $this->_columns[$i]["used-width"];
274
275    return $w;
276
277  }
278
279  function get_frame_height($frame) {
280    $key = $frame->get_id();
281
282    if ( !isset($this->_frames[$key]) )
283      throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
284
285    $rows = $this->_frames[$key]["rows"];
286    $h = 0;
287    foreach ($rows as $i) {
288      if ( !isset($this->_rows[$i]) )  {
289        throw new Exception("foo");
290      }
291      $h += $this->_rows[$i]["height"];
292    }
293    return $h;
294
295  }
296
297
298  //........................................................................
299
300  function set_column_width($j, $width) {
301    $col =& $this->get_column($j);
302    $col["used-width"] = $width;
303    $next_col =& $this->get_column($j+1);
304    $next_col["x"] = $next_col["x"] + $width;
305  }
306
307  function set_row_height($i, $height) {
308    $row =& $this->get_row($i);
309    if ( $height <= $row["height"] )
310      return;
311
312    $row["height"] = $height;
313    $next_row =& $this->get_row($i+1);
314    $next_row["y"] = $row["y"] + $height;
315
316  }
317
318  //........................................................................
319
320
321  protected function _resolve_border($i, $j, $h_v, $border_spec) {
322    $n_width = $border_spec["width"];
323    $n_style = $border_spec["style"];
324    $n_color = $border_spec["color"];
325
326    if ( !isset($this->_borders[$i][$j][$h_v]) ) {
327      $this->_borders[$i][$j][$h_v] = $border_spec;
328      return $this->_borders[$i][$j][$h_v]["width"];
329    }
330
331    $o_width = $this->_borders[$i][$j][$h_v]["width"];
332    $o_style = $this->_borders[$i][$j][$h_v]["style"];
333    $o_color = $this->_borders[$i][$j][$h_v]["color"];
334
335    if ( ($n_style === "hidden" ||
336          $n_width  >  $o_width ||
337          $o_style === "none")
338
339         or
340
341         ($o_width == $n_width &&
342          in_array($n_style, self::$_BORDER_STYLE_SCORE) &&
343          self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) )
344      $this->_borders[$i][$j][$h_v] = $border_spec;
345
346    return $this->_borders[$i][$j][$h_v]["width"];
347  }
348
349  //........................................................................
350
351  function add_frame(Frame $frame) {
352   
353    $style = $frame->get_style();
354    $display = $style->display;
355
356    $collapse = $this->_table->get_style()->border_collapse == "collapse";
357
358    // Recursively add the frames within tables, table-row-groups and table-rows
359    if ( $display == "table-row" ||
360         $display == "table" ||
361         $display == "inline-table" ||
362         in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
363
364      $start_row = $this->__row;
365      foreach ( $frame->get_children() as $child )
366        $this->add_frame( $child );
367
368      if ( $display == "table-row" )
369        $this->add_row();
370
371      $num_rows = $this->__row - $start_row - 1;
372      $key = $frame->get_id();
373
374      // Row groups always span across the entire table
375      $this->_frames[ $key ]["columns"] = range(0,max(0,$this->_num_cols-1));
376      $this->_frames[ $key ]["rows"] = range($start_row, max(0, $this->__row - 1));
377      $this->_frames[ $key ]["frame"] = $frame;
378
379      if ( $display != "table-row" && $collapse ) {
380
381        $bp = $style->get_border_properties();
382
383        // Resolve the borders
384        for ( $i = 0; $i < $num_rows+1; ++$i) {
385          $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
386          $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
387        }
388
389        for ( $j = 0; $j < $this->_num_cols; ++$j) {
390          $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
391          $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
392        }
393      }
394
395
396      return;
397    }
398
399    // Determine where this cell is going
400    $colspan = $frame->get_node()->getAttribute("colspan");
401    $rowspan = $frame->get_node()->getAttribute("rowspan");
402
403    if ( !$colspan ) {
404      $colspan = 1;
405      $frame->get_node()->setAttribute("colspan",1);
406    }
407
408    if ( !$rowspan ) {
409      $rowspan = 1;
410      $frame->get_node()->setAttribute("rowspan",1);
411    }
412    $key = $frame->get_id();
413
414    $bp = $style->get_border_properties();
415
416
417    // Add the frame to the cellmap
418    $max_left = $max_right = 0;
419
420    // Find the next available column (fix by Ciro Mondueri)
421    $ac = $this->__col;
422    while ( isset($this->_cells[$this->__row][$ac]) )
423       ++$ac;
424    $this->__col = $ac;
425
426    // Rows:
427    for ( $i = 0; $i < $rowspan; ++$i ) {
428      $row = $this->__row + $i;
429
430      $this->_frames[ $key ]["rows"][] = $row;
431
432      for ( $j = 0; $j < $colspan; ++$j)
433        $this->_cells[$row][$this->__col + $j] = $frame;
434
435      if ( $collapse ) {
436        // Resolve vertical borders
437        $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
438        $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
439      }
440    }
441
442    $max_top = $max_bottom = 0;
443
444    // Columns:
445    for ( $j = 0; $j < $colspan; ++$j ) {
446      $col = $this->__col + $j;
447      $this->_frames[ $key ]["columns"][] = $col;
448
449      if ( $collapse ) {
450        // Resolve horizontal borders
451        $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
452        $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
453      }
454    }
455
456    $this->_frames[ $key ]["frame"] = $frame;
457
458    // Handle seperated border model
459    if ( !$collapse ) {
460      list($h, $v) = $this->_table->get_style()->border_spacing;
461
462      // Border spacing is effectively a margin between cells
463      $v = $style->length_in_pt($v) / 2;
464      $h = $style->length_in_pt($h) / 2;
465      $style->margin = "$v $h";
466
467      // The additional 1/2 width gets added to the table proper
468
469    } else {
470
471      // Drop the frame's actual border
472      $style->border_left_width = $max_left / 2;
473      $style->border_right_width = $max_right / 2;
474      $style->border_top_width = $max_top / 2;
475      $style->border_bottom_width = $max_bottom / 2;
476      $style->margin = "none";
477    }
478
479    // Resolve the frame's width
480    list($frame_min, $frame_max) = $frame->get_min_max_width();
481
482    $width = $style->width;
483
484    if ( is_percent($width) ) {
485      $var = "percent";
486      $val = (float)rtrim($width, "% ") / $colspan;
487
488    } else if ( $width !== "auto" ) {
489      $var = "absolute";
490      $val = $style->length_in_pt($frame_min) / $colspan;
491    }
492
493    $min = 0;
494    $max = 0;
495    for ( $cs = 0; $cs < $colspan; ++$cs ) {
496
497      // Resolve the frame's width(s) with other cells
498      $col =& $this->get_column( $this->__col + $cs );
499
500      // Note: $var is either 'percent' or 'absolute'.  We compare the
501      // requested percentage or absolute values with the existing widths
502      // and adjust accordingly.
503      if ( isset($var) && $val > $col[$var] ) {
504        $col[$var] = $val;
505        $col["auto"] = false;
506      }
507
508      $min += $col["min-width"];
509      $max += $col["max-width"];
510    }
511
512
513    if ( $frame_min > $min ) {
514      // The frame needs more space.  Expand each sub-column
515      $inc = ($frame_min - $min) / $colspan;
516      for ($c = 0; $c < $colspan; ++$c) {
517        $col =& $this->get_column($this->__col + $c);
518        $col["min-width"] += $inc;
519      }
520    }
521
522    if ( $frame_max > $max ) {
523      $inc = ($frame_max - $max) / $colspan;
524      for ($c = 0; $c < $colspan; ++$c) {
525        $col =& $this->get_column($this->__col + $c);
526        $col["max-width"] += $inc;
527      }
528    }
529
530    $this->__col += $colspan;
531    if ( $this->__col > $this->_num_cols )
532      $this->_num_cols = $this->__col;
533
534  }
535
536  //........................................................................
537
538  function add_row() {
539
540    $this->__row++;
541    $this->_num_rows++;
542
543    // Find the next available column
544    $i = 0;
545    while ( isset($this->_cells[$this->__row][$i]) )
546      ++$i;
547
548    $this->__col = $i;
549
550  }
551
552  //........................................................................
553
554  /**
555   * Remove a row from the cellmap.
556   *
557   * @param Frame
558   */
559  function remove_row(Frame $row) {
560
561    $key = $row->get_id();
562    if ( !isset($this->_frames[$key]) )
563      return;  // Presumably this row has alredy been removed
564
565    $this->_row = $this->_num_rows--;
566
567    $rows = $this->_frames[$key]["rows"];
568    $columns = $this->_frames[$key]["columns"];
569
570    // Remove all frames from this row
571    foreach ( $rows as $r ) {
572      foreach ( $columns as $c ) {
573        if ( isset($this->_cells[$r][$c]) ) {
574          $frame = $this->_cells[$r][$c];
575          unset($this->_frames[ $frame->get_id() ]);
576          unset($this->_cells[$r][$c]);
577        }
578      }
579      unset($this->_rows[$r]);
580    }
581
582    unset($this->_frames[$key]);
583
584  }
585
586  /**
587   * Remove a row group from the cellmap.
588   *
589   * @param Frame $group  The group to remove
590   */
591  function remove_row_group(Frame $group) {
592
593    $key = $group->get_id();
594    if ( !isset($this->_frames[$key]) )
595      return;  // Presumably this row has alredy been removed
596
597    $iter = $group->get_first_child();
598    while ($iter) {
599      $this->remove_row($iter);
600      $iter = $iter->get_next_sibling();
601    }
602
603    unset($this->_frames[$key]);
604  }
605
606  /**
607   * Update a row group after rows have been removed
608   *
609   * @param Frame $group    The group to update
610   * @param Frame $last_row The last row in the row group
611   */
612  function update_row_group(Frame $group, Frame $last_row) {
613
614    $g_key = $group->get_id();
615    $r_key = $last_row->get_id();
616
617    $r_rows = $this->_frames[$r_key]["rows"];
618    $this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) );
619
620  }
621
622  //........................................................................
623
624  function assign_x_positions() {
625    // Pre-condition: widths must be resolved and assigned to columns and
626    // column[0]["x"] must be set.
627
628    $x = $this->_columns[0]["x"];
629    foreach ( array_keys($this->_columns) as $j ) {
630      $this->_columns[$j]["x"] = $x;
631      $x += $this->_columns[$j]["used-width"];
632
633    }
634
635  }
636
637  function assign_frame_heights() {
638    // Pre-condition: widths and heights of each column & row must be
639    // calcluated
640
641    foreach ( $this->_frames as $arr ) {
642      $frame = $arr["frame"];
643
644      $h = 0;
645      foreach( $arr["rows"] as $row ) {
646        if ( !isset($this->_rows[$row]) )
647          // The row has been removed because of a page split, so skip it.
648          continue;
649        $h += $this->_rows[$row]["height"];
650      }
651
652      if ( $frame instanceof Table_Cell_Frame_Decorator )
653        $frame->set_cell_height($h);
654      else
655        $frame->get_style()->height = $h;
656    }
657
658  }
659
660  //........................................................................
661
662  /**
663   * Re-adjust frame height if the table height is larger than its content
664   */
665  function set_frame_heights($table_height, $content_height) {
666
667
668    // Distribute the increased height proportionally amongst each row
669    foreach ( $this->_frames as $arr ) {
670      $frame = $arr["frame"];
671
672      $h = 0;
673      foreach ($arr["rows"] as $row ) {
674        if ( !isset($this->_rows[$row]) )
675          continue;
676
677        $h += $this->_rows[$row]["height"];
678      }
679
680      $new_height = ($h / $content_height) * $table_height;
681
682      if ( $frame instanceof Table_Cell_Frame_Decorator )
683        $frame->set_cell_height($new_height);
684      else
685        $frame->get_style()->height = $new_height;
686    }
687
688  }
689
690  //........................................................................
691
692  // Used for debugging:
693  function __toString() {
694    $str = "";
695    $str .= "Columns:<br/>";
696    $str .= pre_r($this->_columns, true);
697    $str .=  "Rows:<br/>";
698    $str .= pre_r($this->_rows, true);
699
700    $str .=  "Frames:<br/>";
701    $arr = array();
702    foreach ( $this->_frames as $key => $val )
703      $arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]);
704
705    $str .= pre_r($arr, true);
706
707    if ( php_sapi_name() == "cli" )
708      $str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
709                                    array("\n",chr(27)."[01;33m", chr(27)."[0m"),
710                                    $str));
711    return $str;
712  }
713}
714?>
Note: See TracBrowser for help on using the repository browser.