source: branches/2.2/filemanager/tp/dompdf/include/block_frame_reflower.cls.php @ 3019

Revision 3019, 18.0 KB checked in by amuller, 14 years ago (diff)

Ticket #1135 - Corrigindo CSS e adicionando filemanager

Line 
1<?php
2/**
3 * DOMPDF - PHP5 HTML to PDF renderer
4 *
5 * File: $RCSfile: block_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: block_frame_reflower.cls.php 186 2009-10-19 22:42:06Z eclecticgeek@gmail.com $ */
41
42/**
43 * Reflows block frames
44 *
45 * @access private
46 * @package dompdf
47 */
48class Block_Frame_Reflower extends Frame_Reflower {
49  const MIN_JUSTIFY_WIDTH = 0.80;  // (Minimum line width to justify, as
50                                   // fraction of available width)
51
52  function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); }
53
54  //........................................................................
55
56  // Calculate the ideal used value for the width property as per:
57  // http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
58
59  protected function _calculate_width($width) {
60    $style = $this->_frame->get_style();
61    $w = $this->_frame->get_containing_block("w");
62
63    $rm = $style->length_in_pt($style->margin_right, $w);
64    $lm = $style->length_in_pt($style->margin_left, $w);
65
66    $left = $style->length_in_pt($style->left, $w);
67    $right = $style->length_in_pt($style->right, $w);
68   
69    // Handle 'auto' values
70    $dims = array($style->border_left_width,
71                  $style->border_right_width,
72                  $style->padding_left,
73                  $style->padding_right,
74                  $width !== "auto" ? $width : 0,
75                  $rm !== "auto" ? $rm : 0,
76                  $lm !== "auto" ? $lm : 0);
77
78    // absolutely positioned boxes take the 'left' and 'right' properties into account
79    if ( $style->position == "absolute" || $style->position == "fixed" ) {
80      $absolute = true;
81      $dims[] = $left !== "auto" ? $left : 0;
82      $dims[] = $right !== "auto" ? $right : 0;
83    } else {
84      $absolute = false;
85    }
86
87    $sum = $style->length_in_pt($dims, $w);
88
89    // Compare to the containing block
90    $diff = $w - $sum;
91
92    if ( $diff > 0 ) {
93
94      if ( $absolute ) {
95
96        // resolve auto properties: see
97        // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
98
99        if ( $width === "auto" && $left === "auto" && $right === "auto" ) {
100
101          if ( $lm === "auto" )
102            $lm = 0;
103          if ( $rm === "auto" )
104            $rm = 0;
105
106          // Technically, the width should be "shrink-to-fit" i.e. based on the
107          // preferred width of the content...  a little too costly here as a
108          // special case.  Just get the width to take up the slack:
109          $left = 0;
110          $right = 0;
111          $width = $diff;
112
113        } else if ( $width === "auto" ) {
114
115          if ( $lm === "auto" )
116            $lm = 0;
117          if ( $rm === "auto" )
118            $rm = 0;
119          if ( $left === "auto" )
120            $left = 0;
121          if ( $right === "auto" )
122            $right = 0;
123
124          $width = $diff;
125
126        } else if ( $left === "auto" ) {
127          if ( $lm === "auto" )
128            $lm = 0;
129          if ( $rm === "auto" )
130            $rm = 0;
131          if ( $right === "auto" )
132            $right = 0;
133
134          $left = $diff;
135
136        } else if ( $right === "auto" ) {
137
138          if ( $lm === "auto" )
139            $lm = 0;
140          if ( $rm === "auto" )
141            $rm = 0;
142
143          $right = $diff;
144        }
145
146      } else {
147
148        // Find auto properties and get them to take up the slack
149        if ( $width === "auto" )
150          $width = $diff;
151
152        else if ( $lm === "auto" && $rm === "auto" )
153          $lm = $rm = round($diff / 2);
154
155        else if ( $lm === "auto" )
156          $lm = $diff;
157
158        else if ( $rm === "auto" )
159          $rm = $diff;
160      }
161
162    } else if ($diff < 0) {
163
164      // We are over constrained--set margin-right to the difference
165      $rm = $diff;
166
167    }
168
169    $ret = array("width"=> $width, "margin_left" => $lm, "margin_right" => $rm, "left" => $left, "right" => $right);
170
171    return $ret;
172  }
173
174  // Call the above function, but resolve max/min widths
175  protected function _calculate_restricted_width() {
176    $style = $this->_frame->get_style();
177    $cb = $this->_frame->get_containing_block();
178
179    if ( !isset($cb["w"]) )
180      throw new DOMPDF_Exception("Box property calculation requires containing block width");
181
182    // Treat width 100% as auto
183    if ( $style->width === "100%" )
184      $width = "auto";
185    else
186      $width = $style->length_in_pt($style->width, $cb["w"]);
187
188    extract($this->_calculate_width($width));
189
190    // Handle min/max width
191    $min_width = $style->length_in_pt($style->min_width, $cb["w"]);
192    $max_width = $style->length_in_pt($style->max_width, $cb["w"]);
193
194    if ( $max_width !== "none" && $min_width > $max_width)
195      // Swap 'em
196      list($max_width, $min_width) = array($min_width, $max_width);
197
198    if ( $max_width !== "none" && $width > $max_width )
199      extract($this->_calculate_width($max_width));
200
201    if ( $width < $min_width )
202      extract($this->_calculate_width($min_width));
203
204    return array($width, $margin_left, $margin_right, $left, $right);
205
206  }
207
208  //........................................................................
209
210  // Determine the unrestricted height of content within the block
211  protected function _calculate_content_height() {
212
213    // Calculate the actual height
214    $height = 0;
215   
216    // Add the height of all lines
217    foreach ($this->_frame->get_lines() as $line)
218      $height += $line["h"];
219
220    return $height;
221
222  }
223
224  // Determine the frame's restricted height
225  protected function _calculate_restricted_height() {
226    $style = $this->_frame->get_style();
227    $content_height = $this->_calculate_content_height();
228    $cb = $this->_frame->get_containing_block();
229
230    $height = $style->length_in_pt($style->height, $cb["h"]);
231
232    $top = $style->length_in_pt($style->top, $cb["h"]);
233    $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
234
235    $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
236    $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
237
238    if ( $style->position == "absolute" || $style->position == "fixed" ) {
239
240      // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
241
242      $dims = array($top !== "auto" ? $top : 0,
243                    $style->margin_top !== "auto" ? $style->margin_top : 0,
244                    $style->padding_top,
245                    $style->border_top_width,
246                    $height !== "height" ? $height : 0,
247                    $style->border_bottom_width,
248                    $style->padding_bottom,
249                    $style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
250                    $bottom !== "auto" ? $bottom : 0);
251
252      $sum = $style->length_in_pt($dims, $cb["h"]);
253
254      $diff = $cb["h"] - $sum;
255
256      if ( $diff > 0 ) {
257
258        if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) {
259
260          if ( $margin_top === "auto" )
261            $margin_top = 0;
262          if ( $margin_bottom === "auto" )
263            $margin_bottom = 0;
264
265          $height = $diff;
266
267        } else if ( $height === "auto" && $top === "auto" ) {
268
269          if ( $margin_top === "auto" )
270            $margin_top = 0;
271          if ( $margin_bottom === "auto" )
272            $margin_bottom = 0;
273
274          $height = $content_height;
275          $top = $diff - $content_height;
276
277        } else if ( $height === "auto" && $bottom === "auto" ) {
278
279          if ( $margin_top === "auto" )
280            $margin_top = 0;
281          if ( $margin_bottom === "auto" )
282            $margin_bottom = 0;
283
284          $height = $content_height;
285          $bottom = $diff - $content_height;
286
287        } else if ( $top === "auto" && $bottom === "auto" ) {
288
289          if ( $margin_top === "auto" )
290            $margin_top = 0;
291          if ( $margin_bottom === "auto" )
292            $margin_bottom = 0;
293
294          $bottom = $diff;
295
296        } else if ( $top === "auto" ) {
297
298          if ( $margin_top === "auto" )
299            $margin_top = 0;
300          if ( $margin_bottom === "auto" )
301            $margin_bottom = 0;
302
303          $top = $diff;
304
305        } else if ( $height === "auto" ) {
306
307          if ( $margin_top === "auto" )
308            $margin_top = 0;
309          if ( $margin_bottom === "auto" )
310            $margin_bottom = 0;
311
312          $height = $diff;
313
314        } else if ( $bottom === "auto" ) {
315
316          if ( $margin_top === "auto" )
317            $margin_top = 0;
318          if ( $margin_bottom === "auto" )
319            $margin_bottom = 0;
320
321          $bottom = $diff;
322
323        } else {
324
325          if ( $style->overflow === "visible" ) {
326
327            // set all autos to zero
328            if ( $margin_top === "auto" )
329              $margin_top = 0;
330            if ( $margin_bottom === "auto" )
331              $margin_bottom = 0;
332            if ( $top === "auto" )
333              $top = 0;
334            if ( $bottom === "auto" )
335              $bottom = 0;
336            if ( $height === "auto" )
337              $height = $content_height;
338
339          }
340
341          // FIXME: overflow hidden
342        }
343
344      }
345
346    } else {
347
348      // Expand the height if overflow is visible
349      if ( $height == "auto" && $content_height > $height && $style->overflow === "visible" )
350        $height = $content_height;
351
352      // FIXME: this should probably be moved to a seperate function as per
353      // _calculate_restricted_width
354     
355      // Only handle min/max height if the height is independent of the frame's content
356      if ( !($style->overflow === "visible" ||
357             ($style->overflow === "hidden" && $height === "auto")) ) {
358
359        $min_height = $style->min_height;
360        $max_height = $style->max_height;
361
362        if ( isset($cb["h"]) ) {
363          $min_height = $style->length_in_pt($min_height, $cb["h"]);
364          $max_height = $style->length_in_pt($max_height, $cb["h"]);
365
366        } else if ( isset($cb["w"]) ) {
367
368          if ( mb_strpos($min_height, "%") !== false )
369            $min_height = 0;
370          else
371            $min_height = $style->length_in_pt($min_height, $cb["w"]);
372
373          if ( mb_strpos($max_height, "%") !== false )
374            $max_height = "none";
375          else
376            $max_height = $style->length_in_pt($max_height, $cb["w"]);
377        }
378
379        if ( $max_height !== "none" && $min_height > $max_height )
380          // Swap 'em
381          list($max_height, $min_height) = array($min_height, $max_height);
382
383        if ( $max_height !== "none" && $height > $max_height )
384          $height = $max_height;
385
386        if ( $height < $min_height )
387          $height = $min_height;
388      }
389
390    }
391
392    return array($height, $margin_top, $margin_bottom, $top, $bottom);
393
394  }
395
396  //........................................................................
397
398  protected function _text_align() {
399    $style = $this->_frame->get_style();
400    $w = $this->_frame->get_containing_block("w");
401    $width = $style->length_in_pt($style->width, $w);
402
403    // Adjust the justification of each of our lines.
404    // http://www.w3.org/TR/CSS21/text.html#propdef-text-align
405    switch ($style->text_align) {
406
407    default:
408    case "left":
409      return;
410
411    case "right":
412      foreach ($this->_frame->get_lines() as $line) {
413
414        // Move each child over by $dx
415        $dx = $width - $line["w"];
416        foreach($line["frames"] as $frame)
417          $frame->set_position( $frame->get_position("x") + $dx );
418
419      }
420      break;
421
422
423    case "justify":
424      foreach ($this->_frame->get_lines() as $i => $line) {
425
426        // Only set the spacing if the line is long enough.  This is really
427        // just an aesthetic choice ;)
428        if ( $line["w"] > self::MIN_JUSTIFY_WIDTH * $width ) {
429          // Set the spacing for each child
430          if ( $line["wc"] > 1 )
431            $spacing = ($width - $line["w"]) / ($line["wc"] - 1);
432          else
433            $spacing = 0;
434
435          $dx = 0;
436          foreach($line["frames"] as $frame) {
437            if ( !$frame instanceof Text_Frame_Decorator )
438              continue;
439
440            $frame->set_position( $frame->get_position("x") + $dx );
441            $frame->set_text_spacing($spacing);
442            $dx += mb_substr_count($frame->get_text(), " ") * $spacing;
443          }
444
445          // The line (should) now occupy the entire width
446          $this->_frame->set_line($i, null, $width);
447
448        }
449      }
450      break;
451
452    case "center":
453    case "centre":
454      foreach ($this->_frame->get_lines() as $i => $line) {
455        // Centre each line by moving each frame in the line by:
456        $dx = ($width - $line["w"]) / 2;
457        foreach ($line["frames"] as $frame)
458          $frame->set_position( $frame->get_position("x") + $dx );
459      }
460      break;
461    }
462
463  }
464  /**
465   * Align inline children vertically
466   */
467  function vertical_align() {
468    // Align each child vertically after each line is reflowed
469    foreach ( $this->_frame->get_lines() as $i => $line ) {
470
471      foreach ( $line["frames"] as $frame ) {
472        $style = $frame->get_style();
473
474        if ( $style->display != "inline" && $style->display != "text" )
475          continue;
476
477        $align = $style->vertical_align;
478
479        $frame_h = $frame->get_margin_height();
480
481        switch ($align) {
482
483        case "baseline":
484          $y = $line["y"] + $line["h"] - $frame_h;
485          break;
486
487        case "middle":
488          $y = $line["y"] + ($line["h"] + $frame_h) / 2;
489          break;
490
491        case "sub":
492          $y = $line["y"] + 0.9 * $line["h"];
493          break;
494
495        case "super":
496          $y = $line["y"] + 0.1 * $line["h"];
497          break;
498
499        case  "text-top":
500        case "top": // Not strictly accurate, but good enough for now
501          $y = $line["y"];
502          break;
503
504        case "text-bottom":
505        case "bottom":
506          $y = $line["y"] + $line["h"] - $frame_h;
507          break;
508        }
509
510        $x = $frame->get_position("x");
511        $frame->set_position($x, $y);
512
513      }
514    }
515  }
516
517  //........................................................................
518
519  function reflow() {
520
521    // Check if a page break is forced
522    $page = $this->_frame->get_root();
523    $page->check_forced_page_break($this->_frame);
524
525    // Bail if the page is full
526    if ( $page->is_full() )
527      return;
528
529    // Collapse margins if required
530    $this->_collapse_margins();
531
532    $style = $this->_frame->get_style();
533    $cb = $this->_frame->get_containing_block();
534
535    // Determine the constraints imposed by this frame: calculate the width
536    // of the content area:
537    list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
538
539    // Store the calculated properties
540    $style->width = $w;
541    $style->margin_left = $left_margin."pt";
542    $style->margin_right = $right_margin."pt";
543    $style->left = $left ."pt";
544    $style->right = $right . "pt";
545   
546    // Update the position
547    $this->_frame->position();
548    list($x, $y) = $this->_frame->get_position();
549
550    // Adjust the first line based on the text-indent property
551    $indent = $style->length_in_pt($style->text_indent, $cb["w"]);
552    $this->_frame->increase_line_width($indent);
553
554    // Determine the content edge
555    $top = $style->length_in_pt(array($style->margin_top,
556                                      $style->padding_top,
557                                      $style->border_top_width), $cb["h"]);
558
559    $bottom = $style->length_in_pt(array($style->border_bottom_width,
560                                         $style->margin_bottom,
561                                         $style->padding_bottom), $cb["h"]);
562
563    $cb_x = $x + $left_margin +
564      $style->length_in_pt($style->border_left_width, $cb["w"]) +
565      $style->length_in_pt($style->padding_left, $cb["w"]);
566
567    $cb_y = $line_y = $y + $top;
568
569    $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
570
571    // Set the y position of the first line in this block
572    $this->_frame->set_current_line($line_y);
573
574    // Set the containing blocks and reflow each child
575    foreach ( $this->_frame->get_children() as $child ) {
576
577      // Bail out if the page is full
578      if ( $page->is_full() )
579        break;
580     
581      $child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
582      $child->reflow();
583
584      // Don't add the child to the line if a page break has occurred
585      if ( $page->check_page_break($child) )
586        break;
587
588      // If the frame is not absolutely positioned, It's okay to add the frame
589      // to the line
590      if ( $child->get_style()->position != "absolute" &&
591           $child->get_style()->position != "fixed" ) {
592        $this->_frame->add_frame_to_line( $child );
593      }
594    }
595
596    // Determine our height
597    list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
598    $style->height = $height;
599    $style->margin_top = $margin_top;
600    $style->margin_bottom = $margin_bottom;
601    $style->top = $top;
602    $style->bottom = $bottom;
603
604    $this->_text_align();
605
606    $this->vertical_align();
607  }
608
609  //........................................................................
610
611}
612?>
Note: See TracBrowser for help on using the repository browser.