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

Revision 1575, 42.8 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: style.cls.php,v $
6 * Created on: 2004-06-01
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: style.cls.php,v 1.18 2006/07/07 21:31:04 benjcarson Exp $ */
41
42/**
43 * Represents CSS properties.
44 *
45 * The Style class is responsible for handling and storing CSS properties.
46 * It includes methods to resolve colours and lengths, as well as getters &
47 * setters for many CSS properites.
48 *
49 * Actual CSS parsing is performed in the {@link Stylesheet} class.
50 *
51 * @package dompdf
52 */
53class Style {
54
55  /**
56   * Default font size, in points.
57   *
58   * @var float
59   */
60  static $default_font_size = 12;
61
62  /**
63   * Default line height, as a fraction of the font size.
64   *
65   * @var float
66   */
67  static $default_line_height = 1.2;
68
69  /**
70   * List of all inline types.  Should really be a constant.
71   *
72   * @var array
73   */
74  static $INLINE_TYPES = array("inline");
75
76  /**
77   * List of all block types.  Should really be a constant.
78   *
79   * @var array
80   */
81  static $BLOCK_TYPES = array("block","inline-block", "table-cell", "list-item");
82
83  /**
84   * List of all table types.  Should really be a constant.
85   *
86   * @var array;
87   */
88  static $TABLE_TYPES = array("table", "inline-table");
89
90  /**
91   * List of valid border styles.  Should also really be a constant.
92   *
93   * @var array
94   */
95  static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid",
96                                "double", "groove", "ridge", "inset", "outset");
97
98  /**
99   * Default style values.
100   *
101   * @link http://www.w3.org/TR/CSS21/propidx.html
102   *
103   * @var array
104   */
105  static protected $_defaults = null;
106
107  /**
108   * List of inherited properties
109   *
110   * @link http://www.w3.org/TR/CSS21/propidx.html
111   *
112   * @var array
113   */
114  static protected $_inherited = null;
115
116  /**
117   * The stylesheet this style belongs to
118   *
119   * @see Stylesheet
120   * @var Stylesheet
121   */
122  protected $_stylesheet; // stylesheet this style is attached to
123
124  /**
125   * Main array of all CSS properties & values
126   *
127   * @var array
128   */
129  protected $_props;
130
131  /**
132   * Cached property values
133   *
134   * @var array
135   */
136  protected $_prop_cache;
137 
138  /**
139   * Font size of parent element in document tree.  Used for relative font
140   * size resolution.
141   *
142   * @var float
143   */
144  protected $_parent_font_size; // Font size of parent element
145 
146  // private members
147  /**
148   * True once the font size is resolved absolutely
149   *
150   * @var bool
151   */
152  private $__font_size_calculated; // Cache flag
153 
154  /**
155   * Class constructor
156   *
157   * @param Stylesheet $stylesheet the stylesheet this Style is associated with.
158   */
159  function __construct(Stylesheet $stylesheet) {
160    $this->_props = array();
161    $this->_stylesheet = $stylesheet;
162    $this->_parent_font_size = null;
163    $this->__font_size_calculated = false;
164   
165    if ( !isset(self::$_defaults) ) {
166   
167      // Shorthand
168      $d =& self::$_defaults;
169   
170      // All CSS 2.1 properties, and their default values
171      $d["azimuth"] = "center";
172      $d["background_attachment"] = "scroll";
173      $d["background_color"] = "transparent";
174      $d["background_image"] = "none";
175      $d["background_position"] = "0% 0%";
176      $d["background_repeat"] = "repeat";
177      $d["background"] = "";
178      $d["border_collapse"] = "separate";
179      $d["border_color"] = "";
180      $d["border_spacing"] = "0";
181      $d["border_style"] = "";
182      $d["border_top"] = "";
183      $d["border_right"] = "";
184      $d["border_bottom"] = "";
185      $d["border_left"] = "";
186      $d["border_top_color"] = "";
187      $d["border_right_color"] = "";
188      $d["border_bottom_color"] = "";
189      $d["border_left_color"] = "";
190      $d["border_top_style"] = "none";
191      $d["border_right_style"] = "none";
192      $d["border_bottom_style"] = "none";
193      $d["border_left_style"] = "none";
194      $d["border_top_width"] = "medium";
195      $d["border_right_width"] = "medium";
196      $d["border_bottom_width"] = "medium";
197      $d["border_left_width"] = "medium";
198      $d["border_width"] = "medium";
199      $d["border"] = "";
200      $d["bottom"] = "auto";
201      $d["caption_side"] = "top";
202      $d["clear"] = "none";
203      $d["clip"] = "auto";
204      $d["color"] = "#000000";
205      $d["content"] = "normal";
206      $d["counter_increment"] = "none";
207      $d["counter_reset"] = "none";
208      $d["cue_after"] = "none";
209      $d["cue_before"] = "none";
210      $d["cue"] = "";
211      $d["cursor"] = "auto";
212      $d["direction"] = "ltr";
213      $d["display"] = "inline";
214      $d["elevation"] = "level";
215      $d["empty_cells"] = "show";
216      $d["float"] = "none";
217      $d["font_family"] = "serif";
218      $d["font_size"] = "medium";
219      $d["font_style"] = "normal";
220      $d["font_variant"] = "normal";
221      $d["font_weight"] = "normal";
222      $d["font"] = "";
223      $d["height"] = "auto";
224      $d["left"] = "auto";
225      $d["letter_spacing"] = "normal";
226      $d["line_height"] = "normal";
227      $d["list_style_image"] = "none";
228      $d["list_style_position"] = "outside";
229      $d["list_style_type"] = "disc";
230      $d["list_style"] = "";
231      $d["margin_right"] = "0";
232      $d["margin_left"] = "0";
233      $d["margin_top"] = "0";
234      $d["margin_bottom"] = "0";
235      $d["margin"] = "";
236      $d["max_height"] = "none";
237      $d["max_width"] = "none";
238      $d["min_height"] = "0";
239      $d["min_width"] = "0";
240      $d["orphans"] = "2";
241      $d["outline_color"] = "invert";
242      $d["outline_style"] = "none";
243      $d["outline_width"] = "medium";
244      $d["outline"] = "";
245      $d["overflow"] = "visible";
246      $d["padding_top"] = "0";
247      $d["padding_right"] = "0";
248      $d["padding_bottom"] = "0";
249      $d["padding_left"] = "0";
250      $d["padding"] = "";
251      $d["page_break_after"] = "auto";
252      $d["page_break_before"] = "auto";
253      $d["page_break_inside"] = "auto";
254      $d["pause_after"] = "0";
255      $d["pause_before"] = "0";
256      $d["pause"] = "";
257      $d["pitch_range"] = "50";
258      $d["pitch"] = "medium";
259      $d["play_during"] = "auto";
260      $d["position"] = "static";
261      $d["quotes"] = "";
262      $d["richness"] = "50";
263      $d["right"] = "auto";
264      $d["speak_header"] = "once";
265      $d["speak_numeral"] = "continuous";
266      $d["speak_punctuation"] = "none";
267      $d["speak"] = "normal";
268      $d["speech_rate"] = "medium";
269      $d["stress"] = "50";
270      $d["table_layout"] = "auto";
271      $d["text_align"] = "left";
272      $d["text_decoration"] = "none";
273      $d["text_indent"] = "0";
274      $d["text_transform"] = "none";
275      $d["top"] = "auto";
276      $d["unicode_bidi"] = "normal";
277      $d["vertical_align"] = "baseline";
278      $d["visibility"] = "visible";
279      $d["voice_family"] = "";
280      $d["volume"] = "medium";
281      $d["white_space"] = "normal";
282      $d["widows"] = "2";
283      $d["width"] = "auto";
284      $d["word_spacing"] = "normal";
285      $d["z_index"] = "auto";
286
287      // Properties that inherit by default
288      self::$_inherited = array("azimuth",
289                                 "border_collapse",
290                                 "border_spacing",
291                                 "caption_side",
292                                 "color",
293                                 "cursor",
294                                 "direction",
295                                 "elevation",
296                                 "empty_cells",
297                                 "font_family",
298                                 "font_size",
299                                 "font_style",
300                                 "font_variant",
301                                 "font_weight",
302                                 "font",
303                                 "letter_spacing",
304                                 "line_height",
305                                 "list_style_image",
306                                 "list_style_position",
307                                 "list_style_type",
308                                 "list_style",
309                                 "orphans",
310                                 "page_break_inside",
311                                 "pitch_range",
312                                 "pitch",
313                                 "quotes",
314                                 "richness",
315                                 "speak_header",
316                                 "speak_numeral",
317                                 "speak_punctuation",
318                                 "speak",
319                                 "speech_rate",
320                                 "stress",
321                                 "text_align",
322                                 "text_indent",
323                                 "text_transform",
324                                 "visibility",
325                                 "voice_family",
326                                 "volume",
327                                 "white_space",
328                                 "widows",
329                                 "word_spacing");
330    }
331
332  }
333
334  /**
335   * "Destructor": forcibly free all references held by this object
336   */
337  function dispose() {
338    unset($this->_stylesheet);
339  }
340 
341  /**
342   * returns the {@link Stylesheet} this Style is associated with.
343   *
344   * @return Stylesheet
345   */
346  function get_stylesheet() { return $this->_stylesheet; }
347 
348  /**
349   * Converts any CSS length value into an absolute length in points.
350   *
351   * length_in_pt() takes a single length (e.g. '1em') or an array of
352   * lengths and returns an absolute length.  If an array is passed, then
353   * the return value is the sum of all elements.
354   *
355   * If a reference size is not provided, the default font size is used
356   * ({@link Style::$default_font_size}).
357   *
358   * @param float|array $length   the length or array of lengths to resolve
359   * @param float       $ref_size  an absolute reference size to resolve percentage lengths
360   * @return float
361   */
362  function length_in_pt($length, $ref_size = null) {
363
364    if ( !is_array($length) )
365      $length = array($length);
366
367    if ( !isset($ref_size) )
368      $ref_size = self::$default_font_size;
369
370    $ret = 0;
371    foreach ($length as $l) {
372
373      if ( $l === "auto" )
374        return "auto";
375     
376      if ( $l === "none" )
377        return "none";
378
379      // Assume numeric values are already in points
380      if ( is_numeric($l) ) {
381        $ret += $l;
382        continue;
383      }
384       
385      if ( $l === "normal" ) {
386        $ret += $ref_size;
387        continue;
388      }
389
390      // Border lengths
391      if ( $l === "thin" ) {
392        $ret += 0.5;
393        continue;
394      }
395     
396      if ( $l === "medium" ) {
397        $ret += 1.5;
398        continue;
399      }
400   
401      if ( $l === "thick" ) {
402        $ret += 2.5;
403        continue;
404      }
405     
406      if ( ($i = mb_strpos($l, "pt"))  !== false ) {
407        $ret += mb_substr($l, 0, $i);
408        continue;
409      }
410
411      if ( ($i = mb_strpos($l, "px"))  !== false ) {
412        $ret += mb_substr($l, 0, $i);
413        continue;
414      }
415
416      if ( ($i = mb_strpos($l, "em"))  !== false ) {
417        $ret += mb_substr($l, 0, $i) * $this->__get("font_size");
418        continue;
419      }
420     
421      // FIXME: em:ex ratio?
422      if ( ($i = mb_strpos($l, "ex"))  !== false ) {
423        $ret += mb_substr($l, 0, $i) * $this->__get("font_size");
424        continue;
425      }
426     
427      if ( ($i = mb_strpos($l, "%"))  !== false ) {
428        $ret += mb_substr($l, 0, $i)/100 * $ref_size;
429        continue;
430      }
431     
432      if ( ($i = mb_strpos($l, "in")) !== false ) {
433        $ret += mb_substr($l, 0, $i) * 72;
434        continue;
435      }
436         
437      if ( ($i = mb_strpos($l, "cm")) !== false ) {
438        $ret += mb_substr($l, 0, $i) * 72 / 2.54;
439        continue;
440      }
441
442      if ( ($i = mb_strpos($l, "mm")) !== false ) {
443        $ret += mb_substr($l, 0, $i) * 72 / 25.4;
444        continue;
445      }
446         
447      if ( ($i = mb_strpos($l, "pc")) !== false ) {
448        $ret += mb_substr($l, 0, $i) / 12;
449        continue;
450      }
451         
452      // Bogus value
453      $ret += $ref_size;
454    }
455
456    return $ret;
457  }
458
459 
460  /**
461   * Set inherited properties in this style using values in $parent
462   *
463   * @param Style $parent
464   */
465  function inherit(Style $parent) {
466
467    // Set parent font size
468    $this->_parent_font_size = $parent->get_font_size();
469   
470    foreach (self::$_inherited as $prop) {
471      if ( !isset($this->_props[$prop]) && isset($parent->_props[$prop]) )
472        $this->_props[$prop] = $parent->_props[$prop];
473    }
474     
475    foreach (array_keys($this->_props) as $prop) {
476      if ( $this->_props[$prop] == "inherit" )
477        $this->$prop = $parent->$prop;
478    }
479         
480    return $this;
481  }
482
483 
484  /**
485   * Override properties in this style with those in $style
486   *
487   * @param Style $style
488   */
489  function merge(Style $style) {
490    $this->_props = array_merge($this->_props, $style->_props);
491
492    if ( isset($style->_props["font_size"]) )
493      $this->__font_size_calculated = false;   
494  }
495
496 
497  /**
498   * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb")
499   * based on the provided CSS colour value.
500   *
501   * @param string $colour
502   * @return array
503   */
504  function munge_colour($colour) {
505    if ( is_array($colour) )
506      // Assume the array has the right format...
507      // FIXME: should/could verify this.
508      return $colour;
509   
510    $r = 0;
511    $g = 0;
512    $b = 0;
513
514    // Handle colour names
515    switch ($colour) {
516
517    case "maroon":
518      $r = 0x80;
519      break;
520
521    case "red":
522      $r = 0xff;
523      break;
524
525    case "orange":
526      $r = 0xff;
527      $g = 0xa5;
528      break;
529
530    case "yellow":
531      $r = 0xff;
532      $g = 0xff;
533      break;
534
535    case "olive":
536      $r = 0x80;
537      $g = 0x80;
538      break;
539
540    case "purple":
541      $r = 0x80;
542      $b = 0x80;
543      break;
544
545    case "fuchsia":
546      $r = 0xff;
547      $b = 0xff;
548      break;
549
550    case "white":
551      $r = $g = $b = 0xff;
552      break;
553
554    case "lime":
555      $g = 0xff;
556      break;
557
558    case "green":
559      $g = 0x80;
560      break;
561
562    case "navy":
563      $b = 0x80;
564      break;
565
566    case "blue":
567      $b = 0xff;
568      break;
569
570    case "aqua":
571      $g = 0xff;
572      $b = 0xff;
573      break;
574
575    case "teal":
576      $g = 0x80;
577      $b = 0x80;
578      break;
579
580    case "black":
581      break;
582
583    case "sliver":
584      $r = $g = $b = 0xc0;
585      break;
586
587    case "gray":
588    case "grey":
589      $r = $g = $b = 0x80;
590      break;
591
592    case "transparent":
593      return "transparent";
594     
595    default:
596
597      if ( mb_strlen($colour) == 4 && $colour{0} == "#" ) {
598        // #rgb format
599        $r = hexdec($colour{1} . $colour{1});
600        $g = hexdec($colour{2} . $colour{2});
601        $b = hexdec($colour{3} . $colour{3});
602
603      } else if ( mb_strlen($colour) == 7 && $colour{0} == "#" ) {
604        // #rrggbb format
605        $r = hexdec(mb_substr($colour, 1, 2));
606        $g = hexdec(mb_substr($colour, 3, 2));
607        $b = hexdec(mb_substr($colour, 5, 2));
608
609      } else if ( mb_strpos($colour, "rgb") !== false ) {
610
611        // rgb( r,g,b ) format
612        $i = mb_strpos($colour, "(");
613        $j = mb_strpos($colour, ")");
614       
615        // Bad colour value
616        if ($i === false || $j === false)
617          return null;
618
619        $triplet = explode(",", mb_substr($colour, $i+1, $j-$i-1));
620
621        if (count($triplet) != 3)
622          return null;
623       
624        foreach (array_keys($triplet) as $c) {
625          $triplet[$c] = trim($triplet[$c]);
626         
627          if ( $triplet[$c]{mb_strlen($triplet[$c]) - 1} == "%" )
628            $triplet[$c] = round($triplet[$c] * 0.255);
629        }
630
631        list($r, $g, $b) = $triplet;
632
633      } else {
634        // Who knows?
635        return null;
636      }
637     
638      // Clip to 0 - 1
639      $r = $r < 0 ? 0 : ($r > 255 ? 255 : $r);
640      $g = $g < 0 ? 0 : ($g > 255 ? 255 : $g);
641      $b = $b < 0 ? 0 : ($b > 255 ? 255 : $b);
642      break;
643     
644    }
645   
646    // Form array
647    $arr = array(0 => $r / 0xff, 1 => $g / 0xff, 2 => $b / 0xff,
648                 "r"=>$r / 0xff, "g"=>$g / 0xff, "b"=>$b / 0xff,
649                 "hex" => sprintf("#%02X%02X%02X", $r, $g, $b));
650    return $arr;
651     
652  }
653
654 
655  /**
656   * Alias for {@link Style::munge_colour()}
657   *
658   * @param string $color
659   * @return array
660   */
661  function munge_color($color) { return $this->munge_colour($color); } 
662 
663
664  /**
665   * PHP5 overloaded setter
666   *
667   * This function along with {@link Style::__get()} permit a user of the
668   * Style class to access any (CSS) property using the following syntax:
669   * <code>
670   *  Style->margin_top = "1em";
671   *  echo (Style->margin_top);
672   * </code>
673   *
674   * __set() automatically calls the provided set function, if one exists,
675   * otherwise it sets the property directly.  Typically, __set() is not
676   * called directly from outside of this class.
677   *
678   * @param string $prop  the property to set
679   * @param mixed  $val   the value of the property
680   *
681   */
682  function __set($prop, $val) {
683    global $_dompdf_warnings;
684
685    $prop = str_replace("-", "_", $prop);
686    $this->_prop_cache[$prop] = null;
687   
688    if ( !isset(self::$_defaults[$prop]) ) {
689      $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property.";
690      return;
691    }
692   
693    if ( $prop !== "content" && is_string($val) && mb_strpos($val, "url") === false ) {
694      $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val)));
695      $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val);
696    }
697   
698    $method = "set_$prop";
699
700    if ( method_exists($this, $method) )
701      $this->$method($val);
702    else
703      $this->_props[$prop] = $val;
704   
705  }
706
707  /**
708   * PHP5 overloaded getter
709   *
710   * Along with {@link Style::__set()} __get() provides access to all CSS
711   * properties directly.  Typically __get() is not called directly outside
712   * of this class.
713   *
714   * @param string $prop
715   * @return mixed
716   */
717  function __get($prop) {
718   
719    if ( !isset(self::$_defaults[$prop]) )
720      throw new DOMPDF_Exception("'$prop' is not a valid CSS2 property.");
721
722    if ( isset($this->_prop_cache[$prop]) )
723      return $this->_prop_cache[$prop];
724   
725    $method = "get_$prop";
726
727    // Fall back on defaults if property is not set
728    if ( !isset($this->_props[$prop]) )
729      $this->_props[$prop] = self::$_defaults[$prop];
730
731    if ( method_exists($this, $method) )
732      return $this->_prop_cache[$prop] = $this->$method();
733
734
735    return $this->_prop_cache[$prop] = $this->_props[$prop];
736  }
737
738
739  /**
740   * Getter for the 'font-family' CSS property.
741   *
742   * Uses the {@link Font_Metrics} class to resolve the font family into an
743   * actual font file.
744   *
745   * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
746   * @return string
747   */
748  function get_font_family() {
749    // Select the appropriate font.  First determine the subtype, then check
750    // the specified font-families for a candidate.
751
752    // Resolve font-weight
753    $weight = $this->__get("font_weight");
754   
755    if ( is_numeric($weight) ) {
756
757      if ( $weight < 700 )
758        $weight = "normal";
759      else
760        $weight = "bold";
761
762    } else if ( $weight == "bold" || $weight == "bolder" ) {
763      $weight = "bold";
764
765    } else {
766      $weight = "normal";
767
768    }
769
770    // Resolve font-style
771    $font_style = $this->__get("font_style");
772
773    if ( $weight == "bold" && $font_style == "italic" )
774      $subtype = "bold_italic";
775    else if ( $weight == "bold" && $font_style != "italic" )
776      $subtype = "bold";
777    else if ( $weight != "bold" && $font_style == "italic" )
778      $subtype = "italic";
779    else
780      $subtype = "normal";
781   
782    // Resolve the font family
783    $families = explode(",", $this->_props["font_family"]);
784    reset($families);
785    $font = null;
786    while ( current($families) ) {
787      list(,$family) = each($families);
788      $font = Font_Metrics::get_font($family, $subtype);
789
790      if ( $font )
791        return $font;
792    }
793    throw new DOMPDF_Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] ."'");
794   
795  }
796
797  /**
798   * Returns the resolved font size, in points
799   *
800   * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size   
801   * @return float
802   */
803  function get_font_size() {
804
805    if ( $this->__font_size_calculated )
806      return $this->_props["font_size"];
807   
808    if ( !isset($this->_props["font_size"]) )
809      $fs = self::$_defaults["font_size"];
810    else
811      $fs = $this->_props["font_size"];
812   
813    if ( !isset($this->_parent_font_size) )
814      $this->_parent_font_size = self::$default_font_size;
815   
816    switch ($fs) {
817     
818    case "xx-small":
819      $fs = 3/5 * $this->_parent_font_size;
820      break;
821
822    case "x-small":
823      $fs = 3/4 * $this->_parent_font_size;
824      break;
825
826    case "smaller":
827    case "small":
828      $fs = 8/9 * $this->_parent_font_size;
829      break;
830
831    case "medium":
832      $fs = $this->_parent_font_size;
833      break;
834
835    case "larger":
836    case "large":
837      $fs = 6/5 * $this->_parent_font_size;
838      break;
839
840    case "x-large":
841      $fs = 3/2 * $this->_parent_font_size;
842      break;
843
844    case "xx-large":
845      $fs = 2/1 * $this->_parent_font_size;
846      break;
847
848    default:
849      break;
850    }
851
852    // Ensure relative sizes resolve to something
853    if ( ($i = mb_strpos($fs, "em")) !== false )
854      $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
855
856    else if ( ($i = mb_strpos($fs, "ex")) !== false )
857      $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
858
859    else
860      $fs = $this->length_in_pt($fs);
861
862    $this->_props["font_size"] = $fs;   
863    $this->__font_size_calculated = true;
864    return $this->_props["font_size"];
865
866  }
867
868  /**
869   * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
870   * @return float
871   */
872  function get_word_spacing() {
873    if ( $this->_props["word_spacing"] === "normal" )
874      return 0;
875
876    return $this->_props["word_spacing"];
877  }
878
879  /**
880   * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
881   * @return float
882   */
883  function get_line_height() {
884    if ( $this->_props["line_height"] === "normal" )
885      return self::$default_line_height * $this->get_font_size();
886
887    if ( is_numeric($this->_props["line_height"]) )
888      return $this->length_in_pt( $this->_props["line_height"] . "%", $this->get_font_size());
889   
890    return $this->length_in_pt( $this->_props["line_height"], $this->get_font_size() );
891  }
892
893  /**
894   * Returns the colour as an array
895   *
896   * The array has the following format:
897   * <code>array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb")</code>
898   *
899   * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
900   * @return array
901   */
902  function get_color() {
903    return $this->munge_color( $this->_props["color"] );
904  }
905
906  /**
907   * Returns the background colour as an array
908   *
909   * The returned array has the same format as {@link Style::get_color()}
910   *
911   * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
912   * @return array
913   */
914  function get_background_color() {
915    return $this->munge_color( $this->_props["background_color"] );
916  }
917 
918  /**
919   * Returns the background position as an array
920   *
921   * The returned array has the following format:
922   * <code>array(x,y, "x" => x, "y" => y)</code>
923   *
924   * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
925   * @return array
926   */
927  function get_background_position() {
928   
929    $tmp = explode(" ", $this->_props["background_position"]);
930
931    switch ($tmp[0]) {
932
933    case "left":
934      $x = "0%";
935      break;
936
937    case "right":
938      $x = "100%";
939      break;
940
941    case "top":
942      $y = "0%";
943      break;
944
945    case "bottom":
946      $y = "100%";
947      break;
948
949    case "center":
950      $x = "50%";
951      $y = "50%";
952      break;
953
954    default:
955      $x = $tmp[0];
956      break;
957    }
958
959    if ( isset($tmp[1]) ) {
960
961      switch ($tmp[1]) {
962      case "left":
963        $x = "0%";
964        break;
965       
966      case "right":
967        $x = "100%";
968        break;
969       
970      case "top":
971        $y = "0%";
972        break;
973       
974      case "bottom":
975        $y = "100%";
976        break;
977       
978      case "center":
979        if ( $tmp[0] == "left" || $tmp[0] == "right" || $tmp[0] == "center" )
980          $y = "50%";
981        else
982          $x = "50%";
983        break;
984       
985      default:
986        $y = $tmp[1];
987        break;
988      }
989
990    } else {
991      $y = "50%";
992    }
993
994    if ( !isset($x) )
995      $x = "0%";
996
997    if ( !isset($y) )
998      $y = "0%";
999
1000    return array( 0 => $x, "x" => $x,
1001                  1 => $y, "y" => $y );
1002  }
1003           
1004       
1005  /**#@+
1006   * Returns the border colour as an array
1007   *
1008   * See {@link Style::get_color()}
1009   *
1010   * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
1011   * @return array
1012   */
1013  function get_border_top_color() {
1014    if ( $this->_props["border_top_color"] === "" )
1015      $this->_props["border_top_color"] = $this->__get("color");   
1016    return $this->munge_color($this->_props["border_top_color"]);
1017  }
1018
1019  function get_border_right_color() {
1020    if ( $this->_props["border_right_color"] === "" )
1021      $this->_props["border_right_color"] = $this->__get("color");
1022    return $this->munge_color($this->_props["border_right_color"]);
1023  }
1024
1025  function get_border_bottom_color() {
1026    if ( $this->_props["border_bottom_color"] === "" )
1027      $this->_props["border_bottom_color"] = $this->__get("color");
1028    return $this->munge_color($this->_props["border_bottom_color"]);;
1029  }
1030
1031  function get_border_left_color() {
1032    if ( $this->_props["border_left_color"] === "" )
1033      $this->_props["border_left_color"] = $this->__get("color");
1034    return $this->munge_color($this->_props["border_left_color"]);
1035  }
1036 
1037  /**#@-*/
1038
1039 /**#@+
1040   * Returns the border width, as it is currently stored
1041   *
1042   * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties
1043   * @return float|string   
1044   */
1045  function get_border_top_width() {
1046    $style = $this->__get("border_top_style");
1047    return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0;
1048  }
1049 
1050  function get_border_right_width() {
1051    $style = $this->__get("border_right_style");   
1052    return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0;
1053  }
1054
1055  function get_border_bottom_width() {
1056    $style = $this->__get("border_bottom_style");
1057    return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0;
1058  }
1059
1060  function get_border_left_width() {
1061    $style = $this->__get("border_left_style");
1062    return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0;
1063  }
1064  /**#@-*/
1065
1066  /**
1067   * Return an array of all border properties.
1068   *
1069   * The returned array has the following structure:
1070   * <code>
1071   * array("top" => array("width" => [border-width],
1072   *                      "style" => [border-style],
1073   *                      "color" => [border-color (array)]),
1074   *       "bottom" ... )
1075   * </code>
1076   *
1077   * @return array
1078   */
1079  function get_border_properties() {
1080    return array("top" => array("width" => $this->__get("border_top_width"),
1081                                "style" => $this->__get("border_top_style"),
1082                                "color" => $this->__get("border_top_color")),
1083                 "bottom" => array("width" => $this->__get("border_bottom_width"),
1084                                   "style" => $this->__get("border_bottom_style"),
1085                                   "color" => $this->__get("border_bottom_color")),
1086                 "right" => array("width" => $this->__get("border_right_width"),
1087                                  "style" => $this->__get("border_right_style"),
1088                                  "color" => $this->__get("border_right_color")),
1089                 "left" => array("width" => $this->__get("border_left_width"),
1090                                 "style" => $this->__get("border_left_style"),
1091                                 "color" => $this->__get("border_left_color")));
1092  }
1093
1094  /**
1095   * Return a single border property
1096   *
1097   * @return mixed
1098   */
1099  protected function _get_border($side) {
1100    $color = $this->__get("border_" . $side . "_color");
1101   
1102    return $this->__get("border_" . $side . "_width") . " " .
1103      $this->__get("border_" . $side . "_style") . " " . $color["hex"];
1104  }
1105
1106  /**#@+
1107   * Return full border properties as a string
1108   *
1109   * Border properties are returned just as specified in CSS:
1110   * <pre>[width] [style] [color]</pre>
1111   * e.g. "1px solid blue"
1112   *
1113   * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
1114   * @return string
1115   */
1116  function get_border_top() { return $this->_get_border("top"); }
1117  function get_border_right() { return $this->_get_border("right"); }
1118  function get_border_bottom() { return $this->_get_border("bottom"); }
1119  function get_border_left() { return $this->_get_border("left"); }
1120  /**#@-*/
1121
1122
1123  /**
1124   * Returns border spacing as an array
1125   *
1126   * The array has the format (h_space,v_space)
1127   *
1128   * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
1129   * @return array
1130   */
1131  function get_border_spacing() {
1132    return explode(" ", $this->_props["border_spacing"]);
1133  }
1134 
1135 
1136  /**
1137   * Sets colour
1138   *
1139   * The colour parameter can be any valid CSS colour value
1140   *   
1141   * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
1142   * @param string $colour
1143   */
1144  function set_color($colour) {
1145    $col = $this->munge_colour($colour);
1146
1147    if ( is_null($col) )
1148      $col = self::$_defaults["color"];
1149   
1150    $this->_props["color"] = $col["hex"];
1151  }
1152
1153  /**
1154   * Sets the background colour
1155   *
1156   * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
1157   * @param string $colour
1158   */
1159  function set_background_color($colour) {
1160    $col = $this->munge_colour($colour);
1161    if ( is_null($col) )
1162      $col = self::$_defaults["background_color"];
1163   
1164    $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col;
1165  }
1166
1167  /**
1168   * Set the background image url
1169   *
1170   * @link http://www.w3.org/TR/CSS21/colors.html#background-properties
1171   * @param string $url
1172   */
1173  function set_background_image($val) {
1174
1175    if ( mb_strpos($val, "url") !== false ) {
1176      $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val));
1177    } else {
1178      $val = "none";
1179    }
1180
1181    // Resolve the url now in the context of the current stylesheet
1182    $parsed_url = explode_url($val);
1183    if ( $parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "" )
1184      $url = realpath($this->_stylesheet->get_base_path() . $parsed_url["file"]);
1185    else
1186      $url = build_url($this->_stylesheet->get_protocol(),
1187                       $this->_stylesheet->get_host(),
1188                       $this->_stylesheet->get_base_path(),
1189                       $val);                     
1190                     
1191    $this->_props["background_image"] = $url;
1192  }
1193
1194  /**
1195   * Sets the font size
1196   *
1197   * $size can be any acceptable CSS size
1198   *
1199   * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
1200   * @param string|float $size
1201   */
1202  function set_font_size($size) {
1203    $this->__font_size_calculated = false;
1204    $this->_props["font_size"] = $size;
1205  }
1206 
1207  /**#@+
1208   * Sets page break properties
1209   *
1210   * @link http://www.w3.org/TR/CSS21/page.html#page-breaks
1211   * @param string $break
1212   */
1213  function set_page_break_before($break) {
1214    if ($break === "left" || $break === "right")
1215      $break = "always";
1216
1217    $this->_props["page_break_before"] = $break;
1218  }
1219
1220  function set_page_break_after($break) {
1221    if ($break === "left" || $break === "right")
1222      $break = "always";
1223
1224    $this->_props["page_break_after"] = $break;
1225  }
1226  /**#@-*/
1227   
1228  //........................................................................
1229
1230  /**#@+
1231   * Sets the margin size
1232   *
1233   * @link http://www.w3.org/TR/CSS21/box.html#margin-properties
1234   * @param $val
1235   */
1236  function set_margin_top($val) {
1237    $this->_props["margin_top"] = str_replace("none", "0px", $val);
1238  }
1239
1240  function set_margin_right($val) {
1241    $this->_props["margin_right"] = str_replace("none", "0px", $val);
1242  }
1243
1244  function set_margin_bottom($val) {
1245    $this->_props["margin_bottom"] = str_replace("none", "0px", $val);
1246  }
1247
1248  function set_margin_left($val) {
1249    $this->_props["margin_left"] = str_replace("none", "0px", $val);
1250  }
1251 
1252  function set_margin($val) {
1253    $val = str_replace("none", "0px", $val);
1254    $margins = explode(" ", $val);
1255
1256    switch (count($margins)) {
1257
1258    case 1:
1259      $this->_props["margin_top"] = $margins[0];
1260      $this->_props["margin_right"] = $margins[0];
1261      $this->_props["margin_bottom"] = $margins[0];
1262      $this->_props["margin_left"] = $margins[0];
1263      break;
1264
1265    case 2:
1266      $this->_props["margin_top"] = $margins[0];
1267      $this->_props["margin_bottom"] = $margins[0];
1268
1269      $this->_props["margin_right"] = $margins[1];
1270      $this->_props["margin_left"] = $margins[1];
1271      break;
1272       
1273    case 3:
1274      $this->_props["margin_top"] = $margins[0];
1275      $this->_props["margin_right"] = $margins[1];
1276      $this->_props["margin_bottom"] = $margins[1];
1277      $this->_props["margin_left"] = $margins[2];
1278      break;
1279
1280    case 4:
1281      $this->_props["margin_top"] = $margins[0];
1282      $this->_props["margin_right"] = $margins[1];
1283      $this->_props["margin_bottom"] = $margins[2];
1284      $this->_props["margin_left"] = $margins[3];
1285      break;
1286
1287    default:
1288      break;
1289    }
1290
1291    $this->_props["margin"] = $val;
1292   
1293  }
1294  /**#@-*/
1295     
1296
1297  /**#@+
1298   * Sets the padding size
1299   *
1300   * @link http://www.w3.org/TR/CSS21/box.html#padding-properties
1301   * @param $val
1302   */
1303  function set_padding_top($val) {
1304    $this->_props["padding_top"] = str_replace("none", "0px", $val);
1305  }
1306
1307  function set_padding_right($val) {
1308    $this->_props["padding_right"] = str_replace("none", "0px", $val);
1309  }
1310
1311  function set_padding_bottom($val) {
1312    $this->_props["padding_bottom"] = str_replace("none", "0px", $val);
1313  }
1314
1315  function set_padding_left($val) {
1316    $this->_props["padding_left"] = str_replace("none", "0px", $val);
1317  }
1318
1319  function set_padding($val) {
1320    $val = str_replace("none", "0px", $val);
1321    $paddings = explode(" ", $val);
1322
1323    switch (count($paddings)) {
1324
1325    case 1:
1326      $this->_props["padding_top"] = $paddings[0];
1327      $this->_props["padding_right"] = $paddings[0];
1328      $this->_props["padding_bottom"] = $paddings[0];
1329      $this->_props["padding_left"] = $paddings[0];
1330      break;
1331
1332    case 2:
1333      $this->_props["padding_top"] = $paddings[0];
1334      $this->_props["padding_bottom"] = $paddings[0];
1335
1336      $this->_props["padding_right"] = $paddings[1];
1337      $this->_props["padding_left"] = $paddings[1];
1338      break;
1339       
1340    case 3:
1341      $this->_props["padding_top"] = $paddings[0];
1342      $this->_props["padding_right"] = $paddings[1];
1343      $this->_props["padding_bottom"] = $paddings[1];
1344      $this->_props["padding_left"] = $paddings[2];
1345      break;
1346
1347    case 4:
1348      $this->_props["padding_top"] = $paddings[0];
1349      $this->_props["padding_right"] = $paddings[1];
1350      $this->_props["padding_bottom"] = $paddings[2];
1351      $this->_props["padding_left"] = $paddings[3];
1352      break;
1353
1354    default:
1355      break;
1356    }
1357
1358    $this->_props["padding"] = $val;
1359  }
1360  /**#@-*/
1361
1362  /**
1363   * Sets a single border
1364   *
1365   * @param string $side
1366   * @param string $border_spec  ([width] [style] [color])
1367   */
1368  protected function _set_border($side, $border_spec) {
1369    $border_spec = str_replace(",", " ", $border_spec);
1370    $arr = explode(" ", $border_spec);
1371
1372    // FIXME: handle partial values
1373   
1374    $p = "border_" . $side;
1375    $p_width = $p . "_width";
1376    $p_style = $p . "_style";
1377    $p_color = $p . "_color";
1378
1379    foreach ($arr as $value) {
1380      $value = trim($value);
1381      if ( in_array($value, self::$BORDER_STYLES) ) {
1382        $this->_props[$p_style] = $value;
1383
1384      } else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:none|normal|thin|medium|thick)/", $value ) ) {
1385        $this->_props[$p_width] = str_replace("none", "0px", $value);
1386
1387      } else {
1388        // must be colour
1389        $this->_props[$p_color] = $value;
1390      }
1391    }
1392
1393    $this->_props[$p] = $border_spec;
1394  }
1395
1396  /**#@+
1397   * Sets the border styles
1398   *
1399   * @link http://www.w3.org/TR/CSS21/box.html#border-properties
1400   * @param string $val
1401   */
1402  function set_border_top($val) { $this->_set_border("top", $val); }
1403  function set_border_right($val) { $this->_set_border("right", $val); }
1404  function set_border_bottom($val) { $this->_set_border("bottom", $val); }
1405  function set_border_left($val) { $this->_set_border("left", $val); }
1406 
1407  function set_border($val) {
1408    $this->_set_border("top", $val);
1409    $this->_set_border("right", $val);
1410    $this->_set_border("bottom", $val);
1411    $this->_set_border("left", $val);
1412    $this->_props["border"] = $val;
1413  }
1414
1415  function set_border_width($val) {
1416    $arr = explode(" ", $val);
1417
1418    switch (count($arr)) {
1419
1420    case 1:
1421      $this->_props["border_top_width"] = $arr[0];
1422      $this->_props["border_right_width"] = $arr[0];
1423      $this->_props["border_bottom_width"] = $arr[0];
1424      $this->_props["border_left_width"] = $arr[0];
1425      break;
1426
1427    case 2:
1428      $this->_props["border_top_width"] = $arr[0];
1429      $this->_props["border_bottom_width"] = $arr[0];
1430
1431      $this->_props["border_right_width"] = $arr[1];
1432      $this->_props["border_left_width"] = $arr[1];
1433      break;
1434       
1435    case 3:
1436      $this->_props["border_top_width"] = $arr[0];
1437      $this->_props["border_right_width"] = $arr[1];
1438      $this->_props["border_bottom_width"] = $arr[1];
1439      $this->_props["border_left_width"] = $arr[2];
1440      break;
1441
1442    case 4:
1443      $this->_props["border_top_width"] = $arr[0];
1444      $this->_props["border_right_width"] = $arr[1];
1445      $this->_props["border_bottom_width"] = $arr[2];
1446      $this->_props["border_left_width"] = $arr[3];
1447      break;
1448
1449    default:
1450      break;
1451    }
1452
1453    $this->_props["border_width"] = $val;
1454  }
1455 
1456  function set_border_color($val) {
1457   
1458    $arr = explode(" ", $val);
1459   
1460    switch (count($arr)) {
1461
1462    case 1:
1463      $this->_props["border_top_color"] = $arr[0];
1464      $this->_props["border_right_color"] = $arr[0];
1465      $this->_props["border_bottom_color"] = $arr[0];
1466      $this->_props["border_left_color"] = $arr[0];
1467      break;
1468
1469    case 2:
1470      $this->_props["border_top_color"] = $arr[0];
1471      $this->_props["border_bottom_color"] = $arr[0];
1472
1473      $this->_props["border_right_color"] = $arr[1];
1474      $this->_props["border_left_color"] = $arr[1];
1475      break;
1476       
1477    case 3:
1478      $this->_props["border_top_color"] = $arr[0];
1479      $this->_props["border_right_color"] = $arr[1];
1480      $this->_props["border_bottom_color"] = $arr[1];
1481      $this->_props["border_left_color"] = $arr[2];
1482      break;
1483
1484    case 4:
1485      $this->_props["border_top_color"] = $arr[0];
1486      $this->_props["border_right_color"] = $arr[1];
1487      $this->_props["border_bottom_color"] = $arr[2];
1488      $this->_props["border_left_color"] = $arr[3];
1489      break;
1490
1491    default:
1492      break;
1493    }
1494
1495    $this->_props["border_color"] = $val;
1496
1497  }
1498
1499  function set_border_style($val) {
1500    $arr = explode(" ", $val);
1501
1502    switch (count($arr)) {
1503
1504    case 1:
1505      $this->_props["border_top_style"] = $arr[0];
1506      $this->_props["border_right_style"] = $arr[0];
1507      $this->_props["border_bottom_style"] = $arr[0];
1508      $this->_props["border_left_style"] = $arr[0];
1509      break;
1510
1511    case 2:
1512      $this->_props["border_top_style"] = $arr[0];
1513      $this->_props["border_bottom_style"] = $arr[0];
1514
1515      $this->_props["border_right_style"] = $arr[1];
1516      $this->_props["border_left_style"] = $arr[1];
1517      break;
1518       
1519    case 3:
1520      $this->_props["border_top_style"] = $arr[0];
1521      $this->_props["border_right_style"] = $arr[1];
1522      $this->_props["border_bottom_style"] = $arr[1];
1523      $this->_props["border_left_style"] = $arr[2];
1524      break;
1525
1526    case 4:
1527      $this->_props["border_top_style"] = $arr[0];
1528      $this->_props["border_right_style"] = $arr[1];
1529      $this->_props["border_bottom_style"] = $arr[2];
1530      $this->_props["border_left_style"] = $arr[3];
1531      break;
1532
1533    default:
1534      break;
1535    }
1536
1537    $this->_props["border_style"] = $val;
1538  }
1539  /**#@-*/
1540     
1541
1542  /**
1543   * Sets the border spacing
1544   *
1545   * @link http://www.w3.org/TR/CSS21/box.html#border-properties
1546   * @param float $val   
1547   */
1548  function set_border_spacing($val) {
1549
1550    $arr = explode(" ", $val);
1551
1552    if ( count($arr) == 1 )
1553      $arr[1] = $arr[0];
1554
1555    $this->_props["border_spacing"] = $arr[0] . " " . $arr[1];
1556  }
1557
1558  /**
1559   * Sets the list style image
1560   *
1561   * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
1562   * @param $val
1563   */
1564  function set_list_style_image($val) {
1565   
1566    // Strip url(' ... ') from url values
1567    if ( mb_strpos($val, "url") !== false ) {
1568      $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val));
1569    } else {
1570      $val = "none";
1571    }
1572
1573    $this->_props["list_style_image"] = $val;
1574  }
1575 
1576  /**
1577   * Sets the list style
1578   *
1579   * This is not currently implemented
1580   *
1581   * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
1582   * @param $val
1583   */
1584  function set_list_style($val) {
1585    $arr = explode(" ", str_replace(",", " ", $val));
1586
1587    $types = array("disc", "circle", "square", "decimal",
1588                   "decimal-leading-zero", "lower-roman",
1589                   "upper-roman", "lower-greek", "lower-latin",
1590                   "upper-latin", "armenian", "georgian",
1591                   "lower-alpha", "upper-alpha", "none");
1592
1593    $positions = array("inside", "outside");
1594   
1595    foreach ($arr as $value) {
1596      if ( mb_strpos($value, "url") !== false ) {
1597        $this->set_list_style_image($value);
1598        continue;
1599      }
1600
1601      if ( in_array($value, $types) ) {
1602        $this->_props["list_style_type"] = $value;
1603      } else if ( in_array($value, $positions) ) {
1604        $this->_props["list_style_position"] = $value;
1605      }
1606    }
1607  }
1608
1609  /**
1610   * Generate a string representation of the Style
1611   *
1612   * This dumps the entire property array into a string via print_r.  Useful
1613   * for debugging.
1614   *
1615   * @return string
1616   */
1617  function __toString() {
1618    return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size),
1619                               $this->_props), true);
1620  }
1621}
1622?>
Note: See TracBrowser for help on using the repository browser.