1<?php
2// $Header: /cvsroot/html2ps/box.img.php,v 1.50 2007/05/06 18:49:29 Konstantin Exp $
3
4define('SCALE_NONE',0);
5define('SCALE_WIDTH',1);
6define('SCALE_HEIGHT',2);
7
8class GenericImgBox extends GenericInlineBox {
9  function GenericImgBox() {
10    $this->GenericInlineBox();
11  }
12
13  function get_max_width_natural(&$context) {
14    return $this->get_full_width($context);
15  }
16
17  function get_min_width(&$context) {
18    return $this->get_full_width();
19  }
20
21  function get_max_width(&$context) {
22    return $this->get_full_width();
23  }
24
25  function is_null() {
26    return false;
27  }
28
29  function pre_reflow_images() {
30    switch ($this->scale) {
31    case SCALE_WIDTH:
32      // Only 'width' attribute given
33      $size =
34        $this->src_width/$this->src_height*
35        $this->get_width();
36
37      $this->put_height($size);
38
39      // Update baseline according to constrained image height
40      $this->default_baseline = $this->get_full_height();
41      break;
42    case SCALE_HEIGHT:
43      // Only 'height' attribute given
44      $size =
45        $this->src_height/$this->src_width*
46        $this->get_height();
47
48      $this->put_width($size);
49      $this->setCSSProperty(CSS_WIDTH, new WCConstant($size));
50
51      $this->default_baseline = $this->get_full_height();
52      break;
53    };
54  }
55
56  function readCSS(&$state) {
57    parent::readCSS($state);
58
59    // '-html2ps-link-target'
60    global $g_config;
61    if ($g_config["renderlinks"]) {
62      $this->_readCSS($state,
63                      array(CSS_HTML2PS_LINK_TARGET));
64    };
65  }
66
67  function reflow_static(&$parent, &$context) {
68    $this->pre_reflow_images();
69
70    GenericFormattedBox::reflow($parent, $context);
71
72    // Check if we need a line break here
73    $this->maybe_line_break($parent, $context);
74
75    // set default baseline
76    $this->baseline = $this->default_baseline;
77
78    // append to parent line box
79    $parent->append_line($this);
80
81    // Move box to the parent current point
82    $this->guess_corner($parent);
83
84    // Move parent's X coordinate
85    $parent->_current_x += $this->get_full_width();
86
87    // Extend parent height
88    $parent->extend_height($this->get_bottom_margin());
89  }
90
91  function _get_font_name(&$driver, $subword_index) {
92    if (isset($this->_cache[CACHE_TYPEFACE][$subword_index])) {
93      return $this->_cache[CACHE_TYPEFACE][$subword_index];
94    };
95
96    $font_resolver =& $driver->get_font_resolver();
97
98    $font = $this->get_css_property(CSS_FONT);
99    $typeface = $font_resolver->get_typeface_name($font->family,
100                                                $font->weight,
101                                                $font->style,
102                                                'iso-8859-1');
103
104    $this->_cache[CACHE_TYPEFACE][$subword_index] = $typeface;
105
106    return $typeface;
107  }
108
109  function reflow_text(&$driver) {
110    // In XHTML images are treated as a common inline elements; they are affected by line-height and font-size
111    global $g_config;
112    if ($g_config['mode'] == 'xhtml') {
113      /**
114       * A simple assumption is made: fonts used for different encodings
115       * have equal ascender/descender values  (while they have the same
116       * typeface, style and weight).
117       */
118      $font_name = $this->_get_font_name($driver, 0);
119
120      /**
121       * Get font vertical metrics
122       */
123      $ascender  = $driver->font_ascender($font_name, 'iso-8859-1');
124      if (is_null($ascender)) {
125        error_log("ImgBox::reflow_text: cannot get font ascender");
126        return null;
127      };
128
129      $descender = $driver->font_descender($font_name, 'iso-8859-1');
130      if (is_null($descender)) {
131        error_log("ImgBox::reflow_text: cannot get font descender");
132        return null;
133      };
134
135      /**
136       * Setup box size
137       */
138      $font = $this->get_css_property(CSS_FONT_SIZE);
139      $font_size       = $font->getPoints();
140
141      $this->ascender         = $ascender  * $font_size;
142      $this->descender        = $descender * $font_size;
143    } else {
144      $this->ascender = $this->get_height();
145      $this->descender = 0;
146    };
147
148    return true;
149  }
150
151  // Image boxes are regular inline boxes; whitespaces after images should be rendered
152  //
153  function reflow_whitespace(&$linebox_started, &$previous_whitespace) {
154    $linebox_started = true;
155    $previous_whitespace = false;
156    return;
157  }
158
159  function show_fixed(&$driver) {
160    return $this->show($driver);
161  }
162}
163
164class BrokenImgBox extends GenericImgBox {
165  var $alt;
166
167  function BrokenImgBox($width, $height, $alt) {
168    $this->scale = SCALE_NONE;
169    $this->encoding = DEFAULT_ENCODING;
170
171    // Call parent constructor
172    $this->GenericImgBox();
173
174    $this->alt = $alt;
175  }
176
177  function show(&$driver) {
178    $driver->save();
179
180    // draw generic box
181    GenericFormattedBox::show($driver);
182
183    $driver->setlinewidth(0.1);
184    $driver->moveto($this->get_left(),  $this->get_top());
185    $driver->lineto($this->get_right(), $this->get_top());
186    $driver->lineto($this->get_right(), $this->get_bottom());
187    $driver->lineto($this->get_left(),  $this->get_bottom());
188    $driver->closepath();
189    $driver->stroke();
190
191    if (!$GLOBALS['g_config']['debugnoclip']) {
192      $driver->moveto($this->get_left(),  $this->get_top());
193      $driver->lineto($this->get_right(), $this->get_top());
194      $driver->lineto($this->get_right(), $this->get_bottom());
195      $driver->lineto($this->get_left(),  $this->get_bottom());
196      $driver->closepath();
197      $driver->clip();
198    };
199
200    // Output text with the selected font
201    $size = pt2pt(BROKEN_IMAGE_ALT_SIZE_PT);
202
203    $status = $driver->setfont("Times-Roman", "iso-8859-1", $size);
204    if (is_null($status)) {
205      return null;
206    };
207
208    $driver->show_xy($this->alt,
209                     $this->get_left() + $this->width/2 - $driver->stringwidth($this->alt,
210                                                                               "Times-Roman",
211                                                                               "iso-8859-1",
212                                                                               $size)/2,
213                     $this->get_top()  - $this->height/2 - $size/2);
214
215    $driver->restore();
216
217    $strategy =& new StrategyLinkRenderingNormal();
218    $strategy->apply($this, $driver);
219
220    return true;
221  }
222}
223
224class ImgBox extends GenericImgBox {
225  var $image;
226  var $type; // unused; should store the preferred image format (JPG / PNG)
227
228  function ImgBox($img) {
229    $this->encoding = DEFAULT_ENCODING;
230    $this->scale = SCALE_NONE;
231
232    // Call parent constructor
233    $this->GenericImgBox();
234
235    // Store image for further processing
236    $this->image = $img;
237  }
238
239  function &create(&$root, &$pipeline) {
240    // Open image referenced by HTML tag
241    // Some crazy HTML writers add leading and trailing spaces to SRC attribute value - we need to remove them
242    //
243    $url_autofix = new AutofixUrl();
244    $src = $url_autofix->apply(trim($root->get_attribute("src")));
245
246    $image_url = $pipeline->guess_url($src);
247    $src_img = ImageFactory::get($image_url, $pipeline);
248
249    if (is_null($src_img)) {
250      // image could not be opened, use ALT attribute
251
252      if ($root->has_attribute('width')) {
253        $width = px2pt($root->get_attribute('width'));
254      } else {
255        $width = px2pt(BROKEN_IMAGE_DEFAULT_SIZE_PX);
256      };
257
258      if ($root->has_attribute('height')) {
259        $height = px2pt($root->get_attribute('height'));
260      } else {
261        $height = px2pt(BROKEN_IMAGE_DEFAULT_SIZE_PX);
262      };
263
264      $alt = $root->get_attribute('alt');
265
266      $box =& new BrokenImgBox($width, $height, $alt);
267
268      $box->readCSS($pipeline->get_current_css_state());
269
270      $box->put_width($width);
271      $box->put_height($height);
272
273      $box->default_baseline = $box->get_full_height();
274
275      $box->src_height = $box->get_height();
276      $box->src_width  = $box->get_width();
277
278      return $box;
279    } else {
280      $box =& new ImgBox($src_img);
281      $box->readCSS($pipeline->get_current_css_state());
282      $box->_setupSize();
283
284      return $box;
285    }
286  }
287
288  function _setupSize() {
289    $this->put_width(px2pt($this->image->sx()));
290    $this->put_height(px2pt($this->image->sy()));
291    $this->default_baseline = $this->get_full_height();
292
293    $this->src_height = $this->image->sx();
294    $this->src_width  = $this->image->sy();
295
296    $wc = $this->get_css_property(CSS_WIDTH);
297    $hc = $this->get_height_constraint();
298
299    // Proportional scaling
300    if ($hc->is_null() && !$wc->isNull()) {
301      $this->scale = SCALE_WIDTH;
302
303      // Only 'width' attribute given
304      $size =
305        $this->src_width/$this->src_height*
306        $this->get_width();
307
308      $this->put_height($size);
309
310      // Update baseline according to constrained image height
311      $this->default_baseline = $this->get_full_height();
312
313    } elseif (!$hc->is_null() && $wc->isNull()) {
314      $this->scale = SCALE_HEIGHT;
315
316      // Only 'height' attribute given
317      $size =
318        $this->src_height/$this->src_width*
319        $this->get_height();
320
321      $this->put_width($size);
322      $this->setCSSProperty(CSS_WIDTH, new WCConstant($size));
323
324      $this->default_baseline = $this->get_full_height();
325    };
326  }
327
328  function show(&$driver) {
329    // draw generic box
330    GenericFormattedBox::show($driver);
331
332    // Check if "designer" set the height or width of this image to zero; in this there will be no reason
333    // in drawing the image at all
334    //
335    if ($this->get_width() < EPSILON ||
336        $this->get_height() < EPSILON) {
337      return true;
338    };
339
340    $driver->image_scaled($this->image,
341                          $this->get_left(), $this->get_bottom(),
342                          $this->get_width() / $this->image->sx(),
343                          $this->get_height() / $this->image->sy());
344
345    $strategy =& new StrategyLinkRenderingNormal();
346    $strategy->apply($this, $driver);
347
348    return true;
349  }
350}
351?>