source: sandbox/filemanager/tp/dompdf/include/table_frame_reflower.cls.php @ 1575

Revision 1575, 16.0 KB checked in by amuller, 14 years ago (diff)

Ticket #597 - Implentação, melhorias do modulo gerenciador de arquivos

Line 
1<?php
2/**
3 * DOMPDF - PHP5 HTML to PDF renderer
4 *
5 * File: $RCSfile: table_frame_reflower.cls.php,v $
6 * Created on: 2004-06-17
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: table_frame_reflower.cls.php,v 1.10 2006/07/07 21:31:04 benjcarson Exp $ */
41
42/**
43 * Reflows tables
44 *
45 * @access private
46 * @package dompdf
47 */
48class Table_Frame_Reflower extends Frame_Reflower {
49
50  /**
51   * Cache of results between call to get_min_max_width and assign_widths
52   *
53   * @var array
54   */
55  protected $_state;
56
57  function __construct(Table_Frame_Decorator $frame) {
58    $this->_state = null;
59    parent::__construct($frame);
60  }
61
62  /**
63   * State is held here so it needs to be reset along with the decorator
64   */
65  function reset() {
66    $this->_state = null;
67  }
68
69  //........................................................................
70
71  protected function _assign_widths() {
72    $style = $this->_frame->get_style();
73
74    // Find the min/max width of the table and sort the columns into
75    // absolute/percent/auto arrays
76    $min_width = $this->_state["min_width"];
77    $max_width = $this->_state["max_width"];
78    $percent_used = $this->_state["percent_used"];
79    $absolute_used = $this->_state["absolute_used"];
80    $auto_min = $this->_state["auto_min"];
81
82    $absolute =& $this->_state["absolute"];
83    $percent =& $this->_state["percent"];
84    $auto =& $this->_state["auto"];
85
86    // Determine the actual width of the table
87    $cb = $this->_frame->get_containing_block();
88    $columns =& $this->_frame->get_cellmap()->get_columns();
89
90    $width = $style->width;
91
92    // Calcluate padding & border fudge factor
93    $left = $style->margin_left;
94    $right = $style->margin_right;
95
96    $left = $left == "auto" ? 0 : $style->length_in_pt($left, $cb["w"]);
97    $right = $right == "auto" ? 0 : $style->length_in_pt($right, $cb["w"]);
98
99    $delta = $left + $right + $style->length_in_pt(array($style->padding_left,
100                                                         $style->border_left_width,
101                                                         $style->border_right_width,
102                                                         $style->padding_right), $cb["w"]);
103
104    $min_table_width = $style->length_in_pt( $style->min_width, $cb["w"] - $delta );
105
106    if ( $width !== "auto" ) {
107
108      $preferred_width = $style->length_in_pt($width, $cb["w"]) - $delta;
109
110      if ( $preferred_width < $min_table_width )
111        $preferred_width = $min_table_width;
112
113      if ( $preferred_width > $min_width )
114        $width = $preferred_width;
115      else
116        $width = $min_width;
117
118    } else {
119
120      if ( $max_width + $delta < $cb["w"] )
121        $width = $max_width;
122      else if ( $cb["w"] - $delta > $min_width )
123        $width = $cb["w"] - $delta;
124      else
125        $width = $min_width;
126
127      if ( $width < $min_table_width )
128        $width = $min_table_width;
129
130    }
131
132    // Store our resolved width
133    $style->width = $width;
134
135    $cellmap = $this->_frame->get_cellmap();
136
137    // If the whole table fits on the page, then assign each column it's max width
138    if ( $width == $max_width ) {
139
140      foreach (array_keys($columns) as $i)
141        $cellmap->set_column_width($i, $columns[$i]["max-width"]);
142
143      return;
144    }
145
146    // Determine leftover and assign it evenly to all columns
147    if ( $width > $min_width ) {
148
149      // We have four cases to deal with:
150      //
151      // 1. All columns are auto--no widths have been specified.  In this
152      // case we distribute extra space across all columns weighted by max-width.
153      //
154      // 2. Only absolute widths have been specified.  In this case we
155      // distribute any extra space equally among 'width: auto' columns.
156      //
157      // 3. Only percentage widths have been specified.  In this case we
158      // normalize the percentage values and distribute any remaining % to
159      // width: auto columns.  We then proceed to assign widths as fractions
160      // of the table width.
161      //
162      // 4. Both absolute and percentage widths have been specified.  This
163      // is annoying.
164
165      // Case 1:
166      if ( $absolute_used == 0 && $percent_used == 0 ) {
167        $increment = $width - $min_width;
168
169        foreach (array_keys($columns) as $i)
170          $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width));
171        return;
172      }
173
174
175      // Case 2
176      if ( $absolute_used > 0 && $percent_used == 0 ) {
177
178        if ( count($auto) > 0 )
179          $increment = ($width - $auto_min - $absolute_used) / count($auto);
180        else
181          $increment = 0;
182
183        // Use the absolutely specified width or the increment
184        foreach (array_keys($columns) as $i) {
185
186          if ( $columns[$i]["absolute"] > 0 )
187            $cellmap->set_column_width($i, $columns[$i]["min-width"]);
188          else
189            $cellmap->set_column_width($i,$columns[$i]["min-width"] + $increment);
190
191        }
192        return;
193      }
194
195
196      // Case 3:
197      if ( $absolute_used == 0 && $percent_used > 0 ) {
198
199        $scale = null;
200        $remaining = null;
201
202        // Scale percent values if the total percentage is > 100, or if all
203        // values are specified as percentages.
204        if ( $percent_used > 100 || count($auto) == 0)
205          $scale = 100 / $percent_used;
206        else
207          $scale = 1;
208
209        // Account for the minimum space used by the unassigned auto columns
210        $used_width = $auto_min;
211
212        foreach ($percent as $i) {
213          $columns[$i]["percent"] *= $scale;
214
215          $slack = $width - $used_width;
216
217          $w = min($columns[$i]["percent"] * $width/100, $slack);
218
219          if ( $w < $columns[$i]["min-width"] )
220            $w = $columns[$i]["min-width"];
221
222          $cellmap->set_column_width($i, $w);
223          $used_width += $w;
224
225        }
226
227        // This works because $used_width includes the min-width of each
228        // unassigned column
229        if ( count($auto) > 0 ) {
230          $increment = ($width - $used_width) / count($auto);
231
232          foreach ($auto as $i)
233            $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
234
235        }
236        return;
237      }
238
239      // Case 4:
240
241      // First-come, first served
242      if ( $absolute_used > 0 && $percent_used > 0 ) {
243
244        $used_width = $auto_min;
245
246        foreach ($absolute as $i) {
247          $cellmap->set_column_width($i, $columns[$i]["min-width"]);
248          $used_width +=  $columns[$i]["min-width"];
249        }
250
251        // Scale percent values if the total percentage is > 100 or there
252        // are no auto values to take up slack
253        if ( $percent_used > 100 || count($auto) == 0 )
254          $scale = 100 / $percent_used;
255        else
256          $scale = 1;
257
258        $remaining_width = $width - $used_width;
259
260        foreach ($percent as $i) {
261          $slack = $remaining_width - $used_width;
262
263          $columns[$i]["percent"] *= $scale;
264          $w = min($columns[$i]["percent"] * $remaining_width / 100, $slack);
265
266          if ( $w < $columns[$i]["min-width"] )
267            $w = $columns[$i]["min-width"];
268
269          $columns[$i]["used-width"] = $w;
270          $used_width += $w;
271        }
272
273        if ( count($auto) > 0 ) {
274          $increment = ($width - $used_width) / count($auto);
275
276          foreach ($auto as $i)
277            $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
278
279        }
280
281        return;
282      }
283
284
285    } else { // we are over constrained
286
287      // Each column gets its minimum width
288      foreach (array_keys($columns) as $i)
289        $cellmap->set_column_width($i, $columns[$i]["min-width"]);
290
291    }
292  }
293
294  //........................................................................
295
296  // Determine the frame's height based on min/max height
297  protected function _calculate_height() {
298
299    $style = $this->_frame->get_style();
300    $height = $style->height;
301
302    $cellmap = $this->_frame->get_cellmap();
303    $cellmap->assign_frame_heights();
304    $rows = $cellmap->get_rows();
305
306    // Determine our content height
307    $content_height = 0;
308    foreach ( $rows as $r )
309      $content_height += $r["height"];
310
311    $cb = $this->_frame->get_containing_block();
312
313    if ( !($style->overflow === "visible" ||
314           ($style->overflow === "hidden" && $height === "auto")) ) {
315
316      // Only handle min/max height if the height is independent of the frame's content
317
318      $min_height = $style->min_height;
319      $max_height = $style->max_height;
320
321      if ( isset($cb["h"]) ) {
322        $min_height = $style->length_in_pt($min_height, $cb["h"]);
323        $max_height = $style->length_in_pt($max_height, $cb["h"]);
324
325      } else if ( isset($cb["w"]) ) {
326
327        if ( mb_strpos($min_height, "%") !== false )
328          $min_height = 0;
329        else
330          $min_height = $style->length_in_pt($min_height, $cb["w"]);
331
332        if ( mb_strpos($max_height, "%") !== false )
333          $max_height = "none";
334        else
335          $max_height = $style->length_in_pt($max_height, $cb["w"]);
336      }
337
338      if ( $max_height !== "none" && $min_height > $max_height )
339        // Swap 'em
340        list($max_height, $min_height) = array($min_height, $max_height);
341
342      if ( $max_height !== "none" && $height > $max_height )
343        $height = $max_height;
344
345      if ( $height < $min_height )
346        $height = $min_height;
347
348    } else {
349
350      // Use the content height or the height value, whichever is greater
351      if ( $height !== "auto" ) {
352        $height = $style->length_in_pt($height, $cb["h"]);
353
354        if ( $height <= $content_height )
355          $height = $content_height;
356        else
357          $cellmap->set_frame_heights($height,$content_height);
358
359      } else
360        $height = $content_height;
361
362    }
363
364
365    return $height;
366
367  }
368  //........................................................................
369
370  function reflow() {
371
372    // Check if a page break is forced
373    $page = $this->_frame->get_root();
374    $page->check_forced_page_break($this->_frame);
375
376    // Bail if the page is full
377    if ( $page->is_full() )
378      return;
379
380    // Let the page know that we're reflowing a table so that splits
381    // are suppressed (simply setting page-break-inside: avoid won't
382    // work because we may have an arbitrary number of block elements
383    // inside tds.)
384    $page->table_reflow_start();
385
386    // Collapse vertical margins, if required
387    $this->_collapse_margins();
388
389    $this->_frame->position();
390
391    // Table layout algorithm:
392    // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
393
394    if ( is_null($this->_state) )
395      $this->get_min_max_width();
396
397    $cb = $this->_frame->get_containing_block();
398    $style = $this->_frame->get_style();
399
400    // This is slightly inexact, but should be okay.  Add half the
401    // border-spacing to the table as padding.  The other half is added to
402    // the cells themselves.
403    if ( $style->border_collapse === "separate" ) {
404      list($h, $v) = $style->border_spacing;
405
406      $v = $style->length_in_pt($v) / 2;
407      $h = $style->length_in_pt($h) / 2;
408
409      $style->padding_left = $style->length_in_pt($style->padding_left, $cb["w"]) + $h;
410      $style->padding_right = $style->length_in_pt($style->padding_right, $cb["w"]) + $h;
411      $style->padding_top = $style->length_in_pt($style->padding_top, $cb["w"]) + $v;
412      $style->padding_bottom = $style->length_in_pt($style->padding_bottom, $cb["w"]) + $v;
413
414    }
415
416    $this->_assign_widths();
417
418    // Adjust left & right margins, if they are auto
419    $width = $style->width;
420    $left = $style->margin_left;
421    $right = $style->margin_right;
422
423    $diff = $cb["w"] - $width;
424
425    if ( $left === "auto" && $right === "auto" && $diff > 0 ) {
426      $left = $right = $diff / 2;
427      $style->margin_left = "$left pt";
428      $style->margin_right = "$right pt";
429
430    } else {
431      $left = $style->length_in_pt($left, $cb["w"]);
432      $right = $style->length_in_pt($right, $cb["w"]);
433    }
434
435
436    list($x, $y) = $this->_frame->get_position();
437
438    // Determine the content edge
439    $content_x = $x + $left + $style->length_in_pt(array($style->padding_left,
440                                                         $style->border_left_width), $cb["w"]);
441    $content_y = $y + $style->length_in_pt(array($style->margin_top,
442                                                 $style->border_top_width,
443                                                 $style->padding_top), $cb["w"]);
444
445    if ( isset($cb["h"]) )
446      $h = $cb["h"];
447    else
448      $h = null;
449
450
451    $cellmap = $this->_frame->get_cellmap();
452    $col =& $cellmap->get_column(0);
453    $col["x"] = $content_x;
454
455    $row =& $cellmap->get_row(0);
456    $row["y"] = $content_y;
457
458    $cellmap->assign_x_positions();
459
460    // Set the containing block of each child & reflow
461    foreach ( $this->_frame->get_children() as $child ) {
462
463      // Bail if the page is full
464      if ( $page->is_full() )
465        break;
466
467      $child->set_containing_block($content_x, $content_y, $width, $h);
468      $child->reflow();
469
470      // Check if a split has occured
471      $page->check_page_break($child);
472
473    }
474
475    // Assign heights to our cells:
476    $style->height = $this->_calculate_height();
477
478    if ( $style->border_collapse === "collapse" ) {
479      // Unset our borders because our cells are now using them
480      $style->border_style = "none";
481    }
482
483    $page->table_reflow_end();
484
485    // Debugging:
486    //echo ($this->_frame->get_cellmap());
487  }
488
489  //........................................................................
490
491  function get_min_max_width() {
492    $style = $this->_frame->get_style();
493
494    $this->_frame->normalise();
495
496    // Add the cells to the cellmap (this will calcluate column widths as
497    // frames are added)
498    $this->_frame->get_cellmap()->add_frame($this->_frame);
499
500    // Find the min/max width of the table and sort the columns into
501    // absolute/percent/auto arrays
502    $this->_state = array();
503    $this->_state["min_width"] = 0;
504    $this->_state["max_width"] = 0;
505
506    $this->_state["percent_used"] = 0;
507    $this->_state["absolute_used"] = 0;
508    $this->_state["auto_min"] = 0;
509
510    $this->_state["absolute"] = array();
511    $this->_state["percent"] = array();
512    $this->_state["auto"] = array();
513
514    $columns =& $this->_frame->get_cellmap()->get_columns();
515    foreach (array_keys($columns) as $i) {
516      $this->_state["min_width"] += $columns[$i]["min-width"];
517      $this->_state["max_width"] += $columns[$i]["max-width"];
518
519      if ( $columns[$i]["absolute"] > 0 ) {
520        $this->_state["absolute"][] = $i;
521        $this->_state["absolute_used"] += $columns[$i]["absolute"];
522
523      } else if ( $columns[$i]["percent"] > 0 ) {
524        $this->_state["percent"][] = $i;
525        $this->_state["percent_used"] += $columns[$i]["percent"];
526
527      } else {
528        $this->_state["auto"][] = $i;
529        $this->_state["auto_min"] += $columns[$i]["min-width"];
530      }
531    }
532
533    return array($this->_state["min_width"], $this->_state["max_width"],
534                 "min" => $this->_state["min_width"], "max" => $this->_state["max_width"]);
535  }
536}
537
538?>
Note: See TracBrowser for help on using the repository browser.