| 1 |
lars |
1 |
<?php
|
|
|
2 |
// $Header: /cvsroot/html2ps/box.container.php,v 1.68 2007/05/06 18:49:29 Konstantin Exp $
|
|
|
3 |
|
|
|
4 |
require_once(HTML2PS_DIR.'strategy.width.min.php');
|
|
|
5 |
require_once(HTML2PS_DIR.'strategy.width.min.nowrap.php');
|
|
|
6 |
require_once(HTML2PS_DIR.'strategy.width.max.php');
|
|
|
7 |
require_once(HTML2PS_DIR.'strategy.width.max.natural.php');
|
|
|
8 |
|
|
|
9 |
/**
|
|
|
10 |
* @package HTML2PS
|
|
|
11 |
* @subpackage Document
|
|
|
12 |
*
|
|
|
13 |
* This file contains the abstract class describing the behavior of document element
|
|
|
14 |
* containing some other document elements.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
/**
|
|
|
18 |
* @package HTML2PS
|
|
|
19 |
* @subpackage Document
|
|
|
20 |
*
|
|
|
21 |
* The GenericContainerBox class is a common superclass for all document elements able
|
|
|
22 |
* to contain other elements. This class does provide the line-box handling utilies and
|
|
|
23 |
* some minor float related-functions.
|
|
|
24 |
*
|
|
|
25 |
*/
|
|
|
26 |
class GenericContainerBox extends GenericFormattedBox {
|
|
|
27 |
/**
|
|
|
28 |
* @var Array A list of contained elements (of type GenericFormattedBox)
|
|
|
29 |
* @access public
|
|
|
30 |
*/
|
|
|
31 |
var $content;
|
|
|
32 |
|
|
|
33 |
var $_first_line;
|
|
|
34 |
|
|
|
35 |
/**
|
|
|
36 |
* @var Array A list of child nodes in the current line box; changes dynamically
|
|
|
37 |
* during the reflow process.
|
|
|
38 |
* @access private
|
|
|
39 |
*/
|
|
|
40 |
var $_line;
|
|
|
41 |
|
|
|
42 |
/**
|
|
|
43 |
* Sometimes floats may appear inside the line box, consider the following code,
|
|
|
44 |
* for example: "<div>text<div style='float:left'>float</div>word</div>". In
|
|
|
45 |
* this case, the floating DIV should be rendered below the "text word" line;
|
|
|
46 |
* thus, we need to keep a list of deferred floating elements and render them
|
|
|
47 |
* when current line box closes.
|
|
|
48 |
*
|
|
|
49 |
* @var Array A list of floats which should be flown after current line box ends;
|
|
|
50 |
* @access private
|
|
|
51 |
*/
|
|
|
52 |
var $_deferred_floats;
|
|
|
53 |
|
|
|
54 |
/**
|
|
|
55 |
* @var float Current output X value inside the current element
|
|
|
56 |
* @access public
|
|
|
57 |
*/
|
|
|
58 |
var $_current_x;
|
|
|
59 |
|
|
|
60 |
/**
|
|
|
61 |
* @var float Current output Y value inside the current element
|
|
|
62 |
* @access public
|
|
|
63 |
*/
|
|
|
64 |
var $_current_y;
|
|
|
65 |
|
|
|
66 |
function destroy() {
|
|
|
67 |
for ($i=0, $size = count($this->content); $i < $size; $i++) {
|
|
|
68 |
$this->content[$i]->destroy();
|
|
|
69 |
};
|
|
|
70 |
unset($this->content);
|
|
|
71 |
|
|
|
72 |
parent::destroy();
|
|
|
73 |
}
|
|
|
74 |
|
|
|
75 |
/**
|
|
|
76 |
* Render current container box using the specified output method.
|
|
|
77 |
*
|
|
|
78 |
* @param OutputDriver $driver The output driver object
|
|
|
79 |
*
|
|
|
80 |
* @return Boolean flag indicating the success or 'null' value in case of critical rendering
|
|
|
81 |
* error
|
|
|
82 |
*/
|
|
|
83 |
function show(&$driver) {
|
|
|
84 |
GenericFormattedBox::show($driver);
|
|
|
85 |
|
|
|
86 |
$overflow = $this->get_css_property(CSS_OVERFLOW);
|
|
|
87 |
|
|
|
88 |
/**
|
|
|
89 |
* Sometimes the content may overflow container boxes. This situation arise, for example,
|
|
|
90 |
* for relative-positioned child boxes, boxes having constrained height and in some
|
|
|
91 |
* other cases. If the container box does not have CSS 'overflow' property
|
|
|
92 |
* set to 'visible' value, the content should be visually clipped using container box
|
|
|
93 |
* padding area.
|
|
|
94 |
*/
|
|
|
95 |
if ($overflow !== OVERFLOW_VISIBLE) {
|
|
|
96 |
$driver->save();
|
|
|
97 |
$this->_setupClip($driver);
|
|
|
98 |
};
|
|
|
99 |
|
|
|
100 |
/**
|
|
|
101 |
* Render child elements
|
|
|
102 |
*/
|
|
|
103 |
for ($i=0, $size = count($this->content); $i < $size; $i++) {
|
|
|
104 |
$child =& $this->content[$i];
|
|
|
105 |
|
|
|
106 |
/**
|
|
|
107 |
* We'll check the visibility property here
|
|
|
108 |
* Reason: all boxes (except the top-level one) are contained in some other box,
|
|
|
109 |
* so every box will pass this check. The alternative is to add this check into every
|
|
|
110 |
* box class show member.
|
|
|
111 |
*
|
|
|
112 |
* The only exception of absolute positioned block boxes which are drawn separately;
|
|
|
113 |
* their show method is called explicitly; the similar check should be performed there
|
|
|
114 |
*/
|
|
|
115 |
if ($child->isVisibleInFlow()) {
|
|
|
116 |
/**
|
|
|
117 |
* To reduce the drawing overhead, we'll check if some part if current child element
|
|
|
118 |
* belongs to current output page. If not, there will be no reason to draw this
|
|
|
119 |
* child this time.
|
|
|
120 |
*
|
|
|
121 |
* @see OutputDriver::contains()
|
|
|
122 |
*
|
|
|
123 |
* @todo In rare cases the element content may be placed outside the element itself;
|
|
|
124 |
* in such situantion content may be visible on the page, while element is not.
|
|
|
125 |
* This situation should be resolved somehow.
|
|
|
126 |
*/
|
|
|
127 |
if ($driver->contains($child)) {
|
|
|
128 |
if (is_null($child->show($driver))) {
|
|
|
129 |
return null;
|
|
|
130 |
};
|
|
|
131 |
};
|
|
|
132 |
};
|
|
|
133 |
}
|
|
|
134 |
|
|
|
135 |
/**
|
|
|
136 |
* Restore previous clipping mode, if it have been modified for non-'overflow: visible'
|
|
|
137 |
* box.
|
|
|
138 |
*/
|
|
|
139 |
if ($overflow !== OVERFLOW_VISIBLE) {
|
|
|
140 |
$driver->restore();
|
|
|
141 |
};
|
|
|
142 |
|
|
|
143 |
return true;
|
|
|
144 |
}
|
|
|
145 |
|
|
|
146 |
/**
|
|
|
147 |
* Render current fixed-positioned container box using the specified output method. Unlike
|
|
|
148 |
* the 'show' method, there's no check if current page viewport contains current element, as
|
|
|
149 |
* fixed-positioned may be drawn on the page margins, outside the viewport.
|
|
|
150 |
*
|
|
|
151 |
* @param OutputDriver $driver The output driver object
|
|
|
152 |
*
|
|
|
153 |
* @return Boolean flag indicating the success or 'null' value in case of critical rendering
|
|
|
154 |
* error
|
|
|
155 |
*
|
|
|
156 |
* @see GenericContainerBox::show()
|
|
|
157 |
*
|
|
|
158 |
* @todo the 'show' and 'show_fixed' method code are almost the same except the child element
|
|
|
159 |
* method called in the inner loop; also, no check is done if current viewport contains this element,
|
|
|
160 |
* thus sllowinf printing data on page margins, where no data should be printed normally
|
|
|
161 |
* I suppose some more generic method containing the common code should be made.
|
|
|
162 |
*/
|
|
|
163 |
function show_fixed(&$driver) {
|
|
|
164 |
GenericFormattedBox::show($driver);
|
|
|
165 |
|
|
|
166 |
$overflow = $this->get_css_property(CSS_OVERFLOW);
|
|
|
167 |
|
|
|
168 |
/**
|
|
|
169 |
* Sometimes the content may overflow container boxes. This situation arise, for example,
|
|
|
170 |
* for relative-positioned child boxes, boxes having constrained height and in some
|
|
|
171 |
* other cases. If the container box does not have CSS 'overflow' property
|
|
|
172 |
* set to 'visible' value, the content should be visually clipped using container box
|
|
|
173 |
* padding area.
|
|
|
174 |
*/
|
|
|
175 |
if ($overflow !== OVERFLOW_VISIBLE) {
|
|
|
176 |
// Save graphics state (of course, BEFORE the clipping area will be set)
|
|
|
177 |
$driver->save();
|
|
|
178 |
$this->_setupClip($driver);
|
|
|
179 |
};
|
|
|
180 |
|
|
|
181 |
/**
|
|
|
182 |
* Render child elements
|
|
|
183 |
*/
|
|
|
184 |
$size = count($this->content);
|
|
|
185 |
for ($i=0; $i < $size; $i++) {
|
|
|
186 |
/**
|
|
|
187 |
* We'll check the visibility property here
|
|
|
188 |
* Reason: all boxes (except the top-level one) are contained in some other box,
|
|
|
189 |
* so every box will pass this check. The alternative is to add this check into every
|
|
|
190 |
* box class show member.
|
|
|
191 |
*
|
|
|
192 |
* The only exception of absolute positioned block boxes which are drawn separately;
|
|
|
193 |
* their show method is called explicitly; the similar check should be performed there
|
|
|
194 |
*/
|
|
|
195 |
$child =& $this->content[$i];
|
|
|
196 |
if ($child->get_css_property(CSS_VISIBILITY) === VISIBILITY_VISIBLE) {
|
|
|
197 |
// Fixed-positioned blocks are displayed separately;
|
|
|
198 |
// If we call them now, they will be drawn twice
|
|
|
199 |
if ($child->get_css_property(CSS_POSITION) != POSITION_FIXED) {
|
|
|
200 |
if (is_null($child->show_fixed($driver))) {
|
|
|
201 |
return null;
|
|
|
202 |
};
|
|
|
203 |
};
|
|
|
204 |
};
|
|
|
205 |
}
|
|
|
206 |
|
|
|
207 |
/**
|
|
|
208 |
* Restore previous clipping mode, if it have been modified for non-'overflow: visible'
|
|
|
209 |
* box.
|
|
|
210 |
*/
|
|
|
211 |
if ($overflow !== OVERFLOW_VISIBLE) {
|
|
|
212 |
$driver->restore();
|
|
|
213 |
};
|
|
|
214 |
|
|
|
215 |
return true;
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
function _find(&$box) {
|
|
|
219 |
$size = count($this->content);
|
|
|
220 |
for ($i=0; $i<$size; $i++) {
|
|
|
221 |
if ($this->content[$i]->uid == $box->uid) {
|
|
|
222 |
return $i;
|
|
|
223 |
};
|
|
|
224 |
}
|
|
|
225 |
return null;
|
|
|
226 |
}
|
|
|
227 |
|
|
|
228 |
// Inserts new child box at the specified (zero-based) offset; 0 stands for first child
|
|
|
229 |
//
|
|
|
230 |
// @param $index index to insert child at
|
|
|
231 |
// @param $box child to be inserted
|
|
|
232 |
//
|
|
|
233 |
function insert_child($index, &$box) {
|
|
|
234 |
$box->parent =& $this;
|
|
|
235 |
|
|
|
236 |
// Offset the content array
|
|
|
237 |
for ($i = count($this->content)-1; $i>= $index; $i--) {
|
|
|
238 |
$this->content[$i+1] =& $this->content[$i];
|
|
|
239 |
};
|
|
|
240 |
|
|
|
241 |
$this->content[$index] =& $box;
|
|
|
242 |
}
|
|
|
243 |
|
|
|
244 |
function insert_before(&$what, &$where) {
|
|
|
245 |
if ($where) {
|
|
|
246 |
$index = $this->_find($where);
|
|
|
247 |
|
|
|
248 |
if (is_null($index)) {
|
|
|
249 |
return null;
|
|
|
250 |
};
|
|
|
251 |
|
|
|
252 |
$this->insert_child($index, $what);
|
|
|
253 |
} else {
|
|
|
254 |
// If 'where' is not specified, 'what' should become the last child
|
|
|
255 |
$this->add_child($what);
|
|
|
256 |
};
|
|
|
257 |
|
|
|
258 |
return $what;
|
|
|
259 |
}
|
|
|
260 |
|
|
|
261 |
function add_child(&$box) {
|
|
|
262 |
$this->append_child($box);
|
|
|
263 |
}
|
|
|
264 |
|
|
|
265 |
function append_child(&$box) {
|
|
|
266 |
// In general, this function is called like following:
|
|
|
267 |
// $box->add_child(create_pdf_box(...))
|
|
|
268 |
// As create_pdf_box _may_ return null value (for example, for an empty text node),
|
|
|
269 |
// we should process the case of $box == null here
|
|
|
270 |
if ($box) {
|
|
|
271 |
$box->parent =& $this;
|
|
|
272 |
$this->content[] =& $box;
|
|
|
273 |
};
|
|
|
274 |
}
|
|
|
275 |
|
|
|
276 |
// Get first child of current box which actually will be drawn
|
|
|
277 |
// on the page. So, whitespace and null boxes will be ignored
|
|
|
278 |
//
|
|
|
279 |
// See description of is_null for null box definition.
|
|
|
280 |
// (not only NullBox is treated as null box)
|
|
|
281 |
//
|
|
|
282 |
// @return reference to the first visible child of current box
|
|
|
283 |
function &get_first() {
|
|
|
284 |
$size = count($this->content);
|
|
|
285 |
for ($i=0; $i<$size; $i++) {
|
|
|
286 |
if (!is_whitespace($this->content[$i]) &&
|
|
|
287 |
!$this->content[$i]->is_null()) {
|
|
|
288 |
return $this->content[$i];
|
|
|
289 |
};
|
|
|
290 |
};
|
|
|
291 |
|
|
|
292 |
// We use this construct to avoid notice messages in PHP 4.4 and PHP 5
|
|
|
293 |
$dummy = null;
|
|
|
294 |
return $dummy;
|
|
|
295 |
}
|
|
|
296 |
|
|
|
297 |
// Get first text or image child of current box which actually will be drawn
|
|
|
298 |
// on the page.
|
|
|
299 |
//
|
|
|
300 |
// See description of is_null for null box definition.
|
|
|
301 |
// (not only NullBox is treated as null box)
|
|
|
302 |
//
|
|
|
303 |
// @return reference to the first visible child of current box
|
|
|
304 |
function &get_first_data() {
|
|
|
305 |
$size = count($this->content);
|
|
|
306 |
for ($i=0; $i<$size; $i++) {
|
|
|
307 |
if (!is_whitespace($this->content[$i]) && !$this->content[$i]->is_null()) {
|
|
|
308 |
if (is_container($this->content[$i])) {
|
|
|
309 |
$data =& $this->content[$i]->get_first_data();
|
|
|
310 |
if (!is_null($data)) { return $data; };
|
|
|
311 |
} else {
|
|
|
312 |
return $this->content[$i];
|
|
|
313 |
};
|
|
|
314 |
};
|
|
|
315 |
};
|
|
|
316 |
|
|
|
317 |
// We use this construct to avoid notice messages in PHP 4.4 and PHP 5
|
|
|
318 |
$dummy = null;
|
|
|
319 |
return $dummy;
|
|
|
320 |
}
|
|
|
321 |
|
|
|
322 |
// Get last child of current box which actually will be drawn
|
|
|
323 |
// on the page. So, whitespace and null boxes will be ignored
|
|
|
324 |
//
|
|
|
325 |
// See description of is_null for null box definition.
|
|
|
326 |
// (not only NullBox is treated as null box)
|
|
|
327 |
//
|
|
|
328 |
// @return reference to the last visible child of current box
|
|
|
329 |
function &get_last() {
|
|
|
330 |
for ($i=count($this->content)-1; $i>=0; $i--) {
|
|
|
331 |
if (!is_whitespace($this->content[$i]) && !$this->content[$i]->is_null()) {
|
|
|
332 |
return $this->content[$i];
|
|
|
333 |
};
|
|
|
334 |
};
|
|
|
335 |
|
|
|
336 |
// We use this construct to avoid notice messages in PHP 4.4 and PHP 5
|
|
|
337 |
$dummy = null;
|
|
|
338 |
return $dummy;
|
|
|
339 |
}
|
|
|
340 |
|
|
|
341 |
function offset_if_first(&$box, $dx, $dy) {
|
|
|
342 |
if ($this->is_first($box)) {
|
|
|
343 |
// The top-level box (page box) should never be offset
|
|
|
344 |
if ($this->parent) {
|
|
|
345 |
if (!$this->parent->offset_if_first($box, $dx, $dy)) {
|
|
|
346 |
$this->offset($dx, $dy);
|
|
|
347 |
return true;
|
|
|
348 |
};
|
|
|
349 |
};
|
|
|
350 |
};
|
|
|
351 |
return false;
|
|
|
352 |
}
|
|
|
353 |
|
|
|
354 |
function offset($dx, $dy) {
|
|
|
355 |
parent::offset($dx, $dy);
|
|
|
356 |
|
|
|
357 |
$this->_current_x += $dx;
|
|
|
358 |
$this->_current_y += $dy;
|
|
|
359 |
|
|
|
360 |
// Offset contents
|
|
|
361 |
$size = count($this->content);
|
|
|
362 |
for ($i=0; $i < $size; $i++) {
|
|
|
363 |
$this->content[$i]->offset($dx, $dy);
|
|
|
364 |
}
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
function GenericContainerBox() {
|
|
|
368 |
$this->GenericFormattedBox();
|
|
|
369 |
|
|
|
370 |
// By default, box does not have any content
|
|
|
371 |
$this->content = array();
|
|
|
372 |
|
|
|
373 |
// Initialize line box
|
|
|
374 |
$this->_line = array();
|
|
|
375 |
|
|
|
376 |
// Initialize floats-related stuff
|
|
|
377 |
$this->_deferred_floats = array();
|
|
|
378 |
|
|
|
379 |
$this->_additional_text_indent = 0;
|
|
|
380 |
|
|
|
381 |
// Current-point
|
|
|
382 |
$this->_current_x = 0;
|
|
|
383 |
$this->_current_y = 0;
|
|
|
384 |
|
|
|
385 |
// Initialize floating children array
|
|
|
386 |
$this->_floats = array();
|
|
|
387 |
}
|
|
|
388 |
|
|
|
389 |
function add_deferred_float(&$float) {
|
|
|
390 |
$this->_deferred_floats[] =& $float;
|
|
|
391 |
}
|
|
|
392 |
|
|
|
393 |
/**
|
|
|
394 |
* Create the child nodes of current container object using the parsed HTML data
|
|
|
395 |
*
|
|
|
396 |
* @param mixed $root node corresponding to the current container object
|
|
|
397 |
*/
|
|
|
398 |
function create_content(&$root, &$pipeline) {
|
|
|
399 |
// Initialize content
|
|
|
400 |
$child = $root->first_child();
|
|
|
401 |
while ($child) {
|
|
|
402 |
$box_child =& create_pdf_box($child, $pipeline);
|
|
|
403 |
$this->add_child($box_child);
|
|
|
404 |
$child = $child->next_sibling();
|
|
|
405 |
};
|
|
|
406 |
}
|
|
|
407 |
|
|
|
408 |
// Content-handling functions
|
|
|
409 |
|
|
|
410 |
function is_container() {
|
|
|
411 |
return true;
|
|
|
412 |
}
|
|
|
413 |
|
|
|
414 |
function get_content() {
|
|
|
415 |
return join('', array_map(array($this, 'get_content_callback'), $this->content));
|
|
|
416 |
}
|
|
|
417 |
|
|
|
418 |
function get_content_callback(&$node) {
|
|
|
419 |
return $node->get_content();
|
|
|
420 |
}
|
|
|
421 |
|
|
|
422 |
// Get total height of this box content (including floats, if any)
|
|
|
423 |
// Note that floats can be contained inside children, so we'll need to use
|
|
|
424 |
// this function recusively
|
|
|
425 |
function get_real_full_height() {
|
|
|
426 |
$content_size = count($this->content);
|
|
|
427 |
|
|
|
428 |
$overflow = $this->get_css_property(CSS_OVERFLOW);
|
|
|
429 |
|
|
|
430 |
// Treat items with overflow: hidden specifically,
|
|
|
431 |
// as floats flown out of this boxes will not be visible
|
|
|
432 |
if ($overflow == OVERFLOW_HIDDEN) {
|
|
|
433 |
return $this->get_full_height();
|
|
|
434 |
};
|
|
|
435 |
|
|
|
436 |
// Check if this object is totally empty
|
|
|
437 |
$first = $this->get_first();
|
|
|
438 |
if (is_null($first)) {
|
|
|
439 |
return 0;
|
|
|
440 |
};
|
|
|
441 |
|
|
|
442 |
// Initialize the vertical extent taken by content using the
|
|
|
443 |
// very first child
|
|
|
444 |
$max_top = $first->get_top_margin();
|
|
|
445 |
$min_bottom = $first->get_bottom_margin();
|
|
|
446 |
|
|
|
447 |
for ($i=0; $i<$content_size; $i++) {
|
|
|
448 |
if (!$this->content[$i]->is_null()) {
|
|
|
449 |
// Check if top margin of current child is to the up
|
|
|
450 |
// of vertical extent top margin
|
|
|
451 |
$max_top = max($max_top, $this->content[$i]->get_top_margin());
|
|
|
452 |
|
|
|
453 |
/**
|
|
|
454 |
* Check if current child bottom margin will extend
|
|
|
455 |
* the vertical space OR if it contains floats extending
|
|
|
456 |
* this, unless this child have overflow: hidden, because this
|
|
|
457 |
* will prevent additional content to be visible
|
|
|
458 |
*/
|
|
|
459 |
if (!$this->content[$i]->is_container()) {
|
|
|
460 |
$min_bottom = min($min_bottom,
|
|
|
461 |
$this->content[$i]->get_bottom_margin());
|
|
|
462 |
} else {
|
|
|
463 |
$content_overflow = $this->content[$i]->get_css_property(CSS_OVERFLOW);
|
|
|
464 |
|
|
|
465 |
if ($content_overflow == OVERFLOW_HIDDEN) {
|
|
|
466 |
$min_bottom = min($min_bottom,
|
|
|
467 |
$this->content[$i]->get_bottom_margin());
|
|
|
468 |
} else {
|
|
|
469 |
$min_bottom = min($min_bottom,
|
|
|
470 |
$this->content[$i]->get_bottom_margin(),
|
|
|
471 |
$this->content[$i]->get_top_margin() -
|
|
|
472 |
$this->content[$i]->get_real_full_height());
|
|
|
473 |
};
|
|
|
474 |
};
|
|
|
475 |
};
|
|
|
476 |
}
|
|
|
477 |
|
|
|
478 |
return max(0, $max_top - $min_bottom) + $this->_get_vert_extra();
|
|
|
479 |
}
|
|
|
480 |
|
|
|
481 |
// LINE-LENGTH RELATED FUNCTIONS
|
|
|
482 |
|
|
|
483 |
function _line_length() {
|
|
|
484 |
$sum = 0;
|
|
|
485 |
$size = count($this->_line);
|
|
|
486 |
|
|
|
487 |
for ($i=0; $i < $size; $i++) {
|
|
|
488 |
// Note that the line length should include the inline boxes margin/padding
|
|
|
489 |
// as inline boxes are not directly included to the parent line box,
|
|
|
490 |
// we'll need to check the parent of current line box element,
|
|
|
491 |
// and, if it is an inline box, AND this element is last or first contained element
|
|
|
492 |
// add correcponsing padding value
|
|
|
493 |
$element =& $this->_line[$i];
|
|
|
494 |
|
|
|
495 |
if (isset($element->wrapped) && !is_null($element->wrapped)) {
|
|
|
496 |
if ($i==0) {
|
|
|
497 |
$sum += $element->get_full_width() - $element->getWrappedWidth();
|
|
|
498 |
} else {
|
|
|
499 |
$sum += $element->getWrappedWidthAndHyphen();
|
|
|
500 |
};
|
|
|
501 |
} else {
|
|
|
502 |
$sum += $element->get_full_width();
|
|
|
503 |
};
|
|
|
504 |
|
|
|
505 |
if ($element->parent) {
|
|
|
506 |
$first = $element->parent->get_first();
|
|
|
507 |
$last = $element->parent->get_last();
|
|
|
508 |
|
|
|
509 |
if (!is_null($first) && $first->uid === $element->uid) {
|
|
|
510 |
$sum += $element->parent->get_extra_line_left();
|
|
|
511 |
}
|
|
|
512 |
|
|
|
513 |
if (!is_null($last) && $last->uid === $element->uid) {
|
|
|
514 |
$sum += $element->parent->get_extra_line_right();
|
|
|
515 |
}
|
|
|
516 |
};
|
|
|
517 |
}
|
|
|
518 |
|
|
|
519 |
if ($this->_first_line) {
|
|
|
520 |
$ti = $this->get_css_property(CSS_TEXT_INDENT);
|
|
|
521 |
$sum += $ti->calculate($this);
|
|
|
522 |
$sum += $this->_additional_text_indent;
|
|
|
523 |
};
|
|
|
524 |
|
|
|
525 |
return $sum;
|
|
|
526 |
}
|
|
|
527 |
|
|
|
528 |
function _line_length_delta(&$context) {
|
|
|
529 |
return max($this->get_available_width($context) - $this->_line_length(),0);
|
|
|
530 |
}
|
|
|
531 |
|
|
|
532 |
/**
|
|
|
533 |
* Get the last box in current line box
|
|
|
534 |
*/
|
|
|
535 |
function &last_in_line() {
|
|
|
536 |
$size = count($this->_line);
|
|
|
537 |
if ($size < 1) {
|
|
|
538 |
$dummy = null;
|
|
|
539 |
return $dummy;
|
|
|
540 |
};
|
|
|
541 |
|
|
|
542 |
return $this->_line[$size-1];
|
|
|
543 |
}
|
|
|
544 |
|
|
|
545 |
// WIDTH
|
|
|
546 |
|
|
|
547 |
function get_min_width_natural(&$context) {
|
|
|
548 |
$content_size = count($this->content);
|
|
|
549 |
|
|
|
550 |
/**
|
|
|
551 |
* If box does not have any context, its minimal width is determined by extra horizontal space:
|
|
|
552 |
* padding, border width and margins
|
|
|
553 |
*/
|
|
|
554 |
if ($content_size == 0) {
|
|
|
555 |
$min_width = $this->_get_hor_extra();
|
|
|
556 |
return $min_width;
|
|
|
557 |
};
|
|
|
558 |
|
|
|
559 |
/**
|
|
|
560 |
* If we're in 'nowrap' mode, minimal and maximal width will be equal
|
|
|
561 |
*/
|
|
|
562 |
$white_space = $this->get_css_property(CSS_WHITE_SPACE);
|
|
|
563 |
$pseudo_nowrap = $this->get_css_property(CSS_HTML2PS_NOWRAP);
|
|
|
564 |
if ($white_space == WHITESPACE_NOWRAP ||
|
|
|
565 |
$pseudo_nowrap == NOWRAP_NOWRAP) {
|
|
|
566 |
$min_width = $this->get_min_nowrap_width($context);
|
|
|
567 |
return $min_width;
|
|
|
568 |
}
|
|
|
569 |
|
|
|
570 |
/**
|
|
|
571 |
* We need to add text indent size to the width of the first item
|
|
|
572 |
*/
|
|
|
573 |
$start_index = 0;
|
|
|
574 |
while ($start_index < $content_size &&
|
|
|
575 |
$this->content[$start_index]->out_of_flow()) {
|
|
|
576 |
$start_index++;
|
|
|
577 |
};
|
|
|
578 |
|
|
|
579 |
if ($start_index < $content_size) {
|
|
|
580 |
$ti = $this->get_css_property(CSS_TEXT_INDENT);
|
|
|
581 |
$minw =
|
|
|
582 |
$ti->calculate($this) +
|
|
|
583 |
$this->content[$start_index]->get_min_width_natural($context);
|
|
|
584 |
} else {
|
|
|
585 |
$minw = 0;
|
|
|
586 |
};
|
|
|
587 |
|
|
|
588 |
for ($i=$start_index; $i<$content_size; $i++) {
|
|
|
589 |
$item =& $this->content[$i];
|
|
|
590 |
if (!$item->out_of_flow()) {
|
|
|
591 |
$minw = max($minw, $item->get_min_width($context));
|
|
|
592 |
};
|
|
|
593 |
}
|
|
|
594 |
|
|
|
595 |
/**
|
|
|
596 |
* Apply width constraint to min width. Return maximal value
|
|
|
597 |
*/
|
|
|
598 |
$wc = $this->get_css_property(CSS_WIDTH);
|
|
|
599 |
$containing_block =& $this->_get_containing_block();
|
|
|
600 |
|
|
|
601 |
$min_width = $minw;
|
|
|
602 |
return $min_width;
|
|
|
603 |
}
|
|
|
604 |
|
|
|
605 |
function get_min_width(&$context) {
|
|
|
606 |
$strategy = new StrategyWidthMin();
|
|
|
607 |
return $strategy->apply($this, $context);
|
|
|
608 |
}
|
|
|
609 |
|
|
|
610 |
function get_min_nowrap_width(&$context) {
|
|
|
611 |
$strategy = new StrategyWidthMinNowrap();
|
|
|
612 |
return $strategy->apply($this, $context);
|
|
|
613 |
}
|
|
|
614 |
|
|
|
615 |
// Note: <table width="100%" inside some block box cause this box to expand
|
|
|
616 |
// $limit - maximal width which should not be exceeded; by default, there's no limit at all
|
|
|
617 |
//
|
|
|
618 |
function get_max_width_natural(&$context, $limit=10E6) {
|
|
|
619 |
$strategy = new StrategyWidthMaxNatural($limit);
|
|
|
620 |
return $strategy->apply($this, $context);
|
|
|
621 |
}
|
|
|
622 |
|
|
|
623 |
function get_max_width(&$context, $limit=10E6) {
|
|
|
624 |
$strategy = new StrategyWidthMax($limit);
|
|
|
625 |
return $strategy->apply($this, $context);
|
|
|
626 |
}
|
|
|
627 |
|
|
|
628 |
function close_line(&$context, $lastline = false) {
|
|
|
629 |
// Align line-box using 'text-align' property
|
|
|
630 |
$size = count($this->_line);
|
|
|
631 |
|
|
|
632 |
if ($size > 0) {
|
|
|
633 |
$last_item =& $this->_line[$size-1];
|
|
|
634 |
if (is_whitespace($last_item)) {
|
|
|
635 |
$last_item->width = 0;
|
|
|
636 |
$last_item->height = 0;
|
|
|
637 |
};
|
|
|
638 |
};
|
|
|
639 |
|
|
|
640 |
// Note that text-align should not be applied to the block boxes!
|
|
|
641 |
// As block boxes will be alone in the line-box, we can check
|
|
|
642 |
// if the very first box in the line is inline; if not - no justification should be made
|
|
|
643 |
//
|
|
|
644 |
if ($size > 0) {
|
|
|
645 |
if (is_inline($this->_line[0])) {
|
|
|
646 |
$cb = CSSTextAlign::value2pdf($this->get_css_property(CSS_TEXT_ALIGN));
|
|
|
647 |
$cb($this, $context, $lastline);
|
|
|
648 |
} else {
|
|
|
649 |
// Nevertheless, CENTER tag and P/DIV with ALIGN attribute set should affect the
|
|
|
650 |
// position of non-inline children.
|
|
|
651 |
$cb = CSSPseudoAlign::value2pdf($this->get_css_property(CSS_HTML2PS_ALIGN));
|
|
|
652 |
$cb($this, $context, $lastline);
|
|
|
653 |
};
|
|
|
654 |
};
|
|
|
655 |
|
|
|
656 |
// Apply vertical align to all of the line content
|
|
|
657 |
// first, we need to aling all baseline-aligned boxes to determine the basic line-box height, top and bottom edges
|
|
|
658 |
// then, SUP and SUP positioned boxes (as they can extend the top and bottom edges, but not affected themselves)
|
|
|
659 |
// then, MIDDLE, BOTTOM and TOP positioned boxes in the given order
|
|
|
660 |
//
|
|
|
661 |
$baselined = array();
|
|
|
662 |
$baseline = 0;
|
|
|
663 |
$height = 0;
|
|
|
664 |
for ($i=0; $i < $size; $i++) {
|
|
|
665 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
666 |
|
|
|
667 |
if ($vertical_align == VA_BASELINE) {
|
|
|
668 |
// Add current baseline-aligned item to the baseline
|
|
|
669 |
//
|
|
|
670 |
$baselined[] =& $this->_line[$i];
|
|
|
671 |
|
|
|
672 |
$baseline = max($baseline,
|
|
|
673 |
$this->_line[$i]->default_baseline);
|
|
|
674 |
};
|
|
|
675 |
};
|
|
|
676 |
|
|
|
677 |
$size_baselined = count($baselined);
|
|
|
678 |
for ($i=0; $i < $size_baselined; $i++) {
|
|
|
679 |
$baselined[$i]->baseline = $baseline;
|
|
|
680 |
|
|
|
681 |
$height = max($height,
|
|
|
682 |
$baselined[$i]->get_full_height() + $baselined[$i]->getBaselineOffset(),
|
|
|
683 |
$baselined[$i]->get_ascender() + $baselined[$i]->get_descender());
|
|
|
684 |
|
|
|
685 |
};
|
|
|
686 |
|
|
|
687 |
// SUB vertical align
|
|
|
688 |
//
|
|
|
689 |
for ($i=0; $i < $size; $i++) {
|
|
|
690 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
691 |
if ($vertical_align == VA_SUB) {
|
|
|
692 |
$this->_line[$i]->baseline =
|
|
|
693 |
$baseline + $this->_line[$i]->get_full_height()/2;
|
|
|
694 |
};
|
|
|
695 |
}
|
|
|
696 |
|
|
|
697 |
// SUPER vertical align
|
|
|
698 |
//
|
|
|
699 |
for ($i=0; $i < $size; $i++) {
|
|
|
700 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
701 |
if ($vertical_align == VA_SUPER) {
|
|
|
702 |
$this->_line[$i]->baseline = $this->_line[$i]->get_full_height()/2;
|
|
|
703 |
};
|
|
|
704 |
}
|
|
|
705 |
|
|
|
706 |
// MIDDLE vertical align
|
|
|
707 |
//
|
|
|
708 |
$middle = 0;
|
|
|
709 |
for ($i=0; $i < $size; $i++) {
|
|
|
710 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
711 |
if ($vertical_align == VA_MIDDLE) {
|
|
|
712 |
$middle = max($middle, $this->_line[$i]->get_full_height() / 2);
|
|
|
713 |
};
|
|
|
714 |
};
|
|
|
715 |
|
|
|
716 |
if ($middle * 2 > $height) {
|
|
|
717 |
// Offset already aligned items
|
|
|
718 |
//
|
|
|
719 |
for ($i=0; $i < $size; $i++) {
|
|
|
720 |
$this->_line[$i]->baseline += ($middle - $height/2);
|
|
|
721 |
};
|
|
|
722 |
$height = $middle * 2;
|
|
|
723 |
};
|
|
|
724 |
|
|
|
725 |
for ($i=0; $i < $size; $i++) {
|
|
|
726 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
727 |
if ($vertical_align == VA_MIDDLE) {
|
|
|
728 |
$this->_line[$i]->baseline = $this->_line[$i]->default_baseline + ($height/2 - $this->_line[$i]->get_full_height()/2);
|
|
|
729 |
};
|
|
|
730 |
}
|
|
|
731 |
|
|
|
732 |
// BOTTOM vertical align
|
|
|
733 |
//
|
|
|
734 |
$bottom = 0;
|
|
|
735 |
for ($i=0; $i < $size; $i++) {
|
|
|
736 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
737 |
if ($vertical_align == VA_BOTTOM) {
|
|
|
738 |
$bottom = max($bottom, $this->_line[$i]->get_full_height());
|
|
|
739 |
};
|
|
|
740 |
};
|
|
|
741 |
|
|
|
742 |
if ($bottom > $height) {
|
|
|
743 |
// Offset already aligned items
|
|
|
744 |
//
|
|
|
745 |
for ($i=0; $i < $size; $i++) {
|
|
|
746 |
$this->_line[$i]->baseline += ($bottom - $height);
|
|
|
747 |
};
|
|
|
748 |
$height = $bottom;
|
|
|
749 |
};
|
|
|
750 |
|
|
|
751 |
for ($i=0; $i < $size; $i++) {
|
|
|
752 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
753 |
if ($vertical_align == VA_BOTTOM) {
|
|
|
754 |
$this->_line[$i]->baseline = $this->_line[$i]->default_baseline + $height - $this->_line[$i]->get_full_height();
|
|
|
755 |
};
|
|
|
756 |
}
|
|
|
757 |
|
|
|
758 |
// TOP vertical align
|
|
|
759 |
//
|
|
|
760 |
$bottom = 0;
|
|
|
761 |
for ($i=0; $i < $size; $i++) {
|
|
|
762 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
763 |
if ($vertical_align == VA_TOP) {
|
|
|
764 |
$bottom = max($bottom, $this->_line[$i]->get_full_height());
|
|
|
765 |
};
|
|
|
766 |
};
|
|
|
767 |
|
|
|
768 |
if ($bottom > $height) {
|
|
|
769 |
$height = $bottom;
|
|
|
770 |
};
|
|
|
771 |
|
|
|
772 |
for ($i=0; $i < $size; $i++) {
|
|
|
773 |
$vertical_align = $this->_line[$i]->get_css_property(CSS_VERTICAL_ALIGN);
|
|
|
774 |
if ($vertical_align == VA_TOP) {
|
|
|
775 |
$this->_line[$i]->baseline = $this->_line[$i]->default_baseline;
|
|
|
776 |
};
|
|
|
777 |
}
|
|
|
778 |
|
|
|
779 |
// Calculate the bottom Y coordinate of last line box
|
|
|
780 |
//
|
|
|
781 |
$line_bottom = $this->_current_y;
|
|
|
782 |
foreach ($this->_line AS $line_element) {
|
|
|
783 |
// This line is required; say, we have sequence of text and image inside the container,
|
|
|
784 |
// AND image have greater baseline than text; in out case, text will be offset to the bottom
|
|
|
785 |
// of the page and we lose the gap between text and container bottom edge, unless we'll re-extend
|
|
|
786 |
// containier height
|
|
|
787 |
|
|
|
788 |
// Note that we're using the colapsed margin value to get the Y coordinate to extend height to,
|
|
|
789 |
// as bottom margin may be collapsed with parent
|
|
|
790 |
|
|
|
791 |
$effective_bottom =
|
|
|
792 |
$line_element->get_top() -
|
|
|
793 |
$line_element->get_height() -
|
|
|
794 |
$line_element->get_extra_bottom();
|
|
|
795 |
|
|
|
796 |
$this->extend_height($effective_bottom);
|
|
|
797 |
$line_bottom = min($effective_bottom, $line_bottom);
|
|
|
798 |
}
|
|
|
799 |
|
|
|
800 |
$this->extend_height($line_bottom);
|
|
|
801 |
|
|
|
802 |
// Clear the line box
|
|
|
803 |
$this->_line = array();
|
|
|
804 |
|
|
|
805 |
// Reset current X coordinate to the far left
|
|
|
806 |
$this->_current_x = $this->get_left();
|
|
|
807 |
|
|
|
808 |
// Extend Y coordinate
|
|
|
809 |
$this->_current_y = $line_bottom;
|
|
|
810 |
|
|
|
811 |
// Render the deferred floats
|
|
|
812 |
for ($i = 0, $size = count($this->_deferred_floats); $i < $size; $i++) {
|
|
|
813 |
$this->_deferred_floats[$i]->reflow_static_float($this, $context);
|
|
|
814 |
};
|
|
|
815 |
// Clear deferred float list
|
|
|
816 |
$this->_deferred_floats = array();
|
|
|
817 |
|
|
|
818 |
// modify the current-x value, so that next inline box will not intersect any floating boxes
|
|
|
819 |
$this->_current_x = $context->float_left_x($this->_current_x, $this->_current_y);
|
|
|
820 |
|
|
|
821 |
$this->_first_line = false;
|
|
|
822 |
}
|
|
|
823 |
|
|
|
824 |
function append_line(&$item) {
|
|
|
825 |
$this->_line[] =& $item;
|
|
|
826 |
}
|
|
|
827 |
|
|
|
828 |
// Line box should be treated as empty in following cases:
|
|
|
829 |
// 1. It is really empty (so, it contains 0 boxes)
|
|
|
830 |
// 2. It contains only whitespace boxes
|
|
|
831 |
function line_box_empty() {
|
|
|
832 |
$size = count($this->_line);
|
|
|
833 |
if ($size == 0) { return true; }
|
|
|
834 |
|
|
|
835 |
// Scan line box
|
|
|
836 |
for ($i=0; $i<$size; $i++) {
|
|
|
837 |
if (!is_whitespace($this->_line[$i]) &&
|
|
|
838 |
!$this->_line[$i]->is_null()) { return false; };
|
|
|
839 |
}
|
|
|
840 |
|
|
|
841 |
// No non-whitespace boxes were found
|
|
|
842 |
return true;
|
|
|
843 |
}
|
|
|
844 |
|
|
|
845 |
function reflow_anchors(&$viewport, &$anchors, $page_heights) {
|
|
|
846 |
GenericFormattedBox::reflow_anchors($viewport, $anchors, $page_heights);
|
|
|
847 |
|
|
|
848 |
$size = count($this->content);
|
|
|
849 |
for ($i=0; $i<$size; $i++) {
|
|
|
850 |
$this->content[$i]->reflow_anchors($viewport, $anchors, $page_heights);
|
|
|
851 |
}
|
|
|
852 |
}
|
|
|
853 |
|
|
|
854 |
function fitFloats(&$context) {
|
|
|
855 |
$float_bottom = $context->float_bottom();
|
|
|
856 |
if (!is_null($float_bottom)) {
|
|
|
857 |
$this->extend_height($float_bottom);
|
|
|
858 |
};
|
|
|
859 |
|
|
|
860 |
$float_right = $context->float_right();
|
|
|
861 |
if (!is_null($float_right)) {
|
|
|
862 |
$this->extend_width($float_right);
|
|
|
863 |
};
|
|
|
864 |
}
|
|
|
865 |
|
|
|
866 |
function reflow_content(&$context) {
|
|
|
867 |
$text_indent = $this->get_css_property(CSS_TEXT_INDENT);
|
|
|
868 |
|
|
|
869 |
$this->close_line($context);
|
|
|
870 |
|
|
|
871 |
$this->_first_line = true;
|
|
|
872 |
|
|
|
873 |
// If first child is inline - apply text-indent
|
|
|
874 |
$first = $this->get_first();
|
|
|
875 |
if (!is_null($first)) {
|
|
|
876 |
if (is_inline($first)) {
|
|
|
877 |
$this->_current_x += $text_indent->calculate($this);
|
|
|
878 |
$this->_current_x += $this->_additional_text_indent;
|
|
|
879 |
};
|
|
|
880 |
};
|
|
|
881 |
|
|
|
882 |
$this->height = 0;
|
|
|
883 |
// Reset current Y value
|
|
|
884 |
$this->_current_y = $this->get_top();
|
|
|
885 |
|
|
|
886 |
$size = count($this->content);
|
|
|
887 |
for ($i=0; $i < $size; $i++) {
|
|
|
888 |
$child =& $this->content[$i];
|
|
|
889 |
$child->reflow($this, $context);
|
|
|
890 |
};
|
|
|
891 |
|
|
|
892 |
$this->close_line($context, true);
|
|
|
893 |
}
|
|
|
894 |
|
|
|
895 |
function reflow_inline() {
|
|
|
896 |
$size = count($this->content);
|
|
|
897 |
for ($i=0; $i<$size; $i++) {
|
|
|
898 |
$this->content[$i]->reflow_inline();
|
|
|
899 |
};
|
|
|
900 |
}
|
|
|
901 |
|
|
|
902 |
function reflow_text(&$viewport) {
|
|
|
903 |
$size = count($this->content);
|
|
|
904 |
for ($i=0; $i<$size; $i++) {
|
|
|
905 |
if (is_null($this->content[$i]->reflow_text($viewport))) {
|
|
|
906 |
return null;
|
|
|
907 |
};
|
|
|
908 |
}
|
|
|
909 |
return true;
|
|
|
910 |
}
|
|
|
911 |
|
|
|
912 |
/**
|
|
|
913 |
* Position/size current box as floating one
|
|
|
914 |
*/
|
|
|
915 |
function reflow_static_float(&$parent, &$context) {
|
|
|
916 |
// Defer the float rendering till the next line box
|
|
|
917 |
if (!$parent->line_box_empty()) {
|
|
|
918 |
$parent->add_deferred_float($this);
|
|
|
919 |
return;
|
|
|
920 |
};
|
|
|
921 |
|
|
|
922 |
// Calculate margin values if they have been set as a percentage
|
|
|
923 |
$this->_calc_percentage_margins($parent);
|
|
|
924 |
$this->_calc_percentage_padding($parent);
|
|
|
925 |
|
|
|
926 |
// Calculate width value if it have been set as a percentage
|
|
|
927 |
$this->_calc_percentage_width($parent, $context);
|
|
|
928 |
|
|
|
929 |
// Calculate margins and/or width is 'auto' values have been specified
|
|
|
930 |
$this->_calc_auto_width_margins($parent);
|
|
|
931 |
|
|
|
932 |
// Determine the actual width of the floating box
|
|
|
933 |
// Note that get_max_width returns both content and extra width
|
|
|
934 |
$this->put_full_width($this->get_max_width_natural($context, $this->parent->get_width()));
|
|
|
935 |
|
|
|
936 |
// We need to call this function before determining the horizontal coordinate
|
|
|
937 |
// as after vertical offset the additional space to the left may apperar
|
|
|
938 |
$y = $this->apply_clear($parent->_current_y, $context);
|
|
|
939 |
|
|
|
940 |
// determine the position of top-left floating box corner
|
|
|
941 |
if ($this->get_css_property(CSS_FLOAT) === FLOAT_RIGHT) {
|
|
|
942 |
$context->float_right_xy($parent, $this->get_full_width(), $x, $y);
|
|
|
943 |
$x -= $this->get_full_width();
|
|
|
944 |
} else {
|
|
|
945 |
$context->float_left_xy($parent, $this->get_full_width(), $x, $y);
|
|
|
946 |
};
|
|
|
947 |
|
|
|
948 |
// Note that $x and $y contain just a free space corner coordinate;
|
|
|
949 |
// If our float has a margin/padding space, we'll need to offset ot a little;
|
|
|
950 |
// Remember that float margins are never collapsed!
|
|
|
951 |
$this->moveto($x + $this->get_extra_left(), $y - $this->get_extra_top());
|
|
|
952 |
|
|
|
953 |
// Reflow contents.
|
|
|
954 |
// Note that floating box creates a new float flow context for it children.
|
|
|
955 |
|
|
|
956 |
$context->push_floats();
|
|
|
957 |
|
|
|
958 |
// Floating box create a separate margin collapsing context
|
|
|
959 |
$context->push_collapsed_margin(0);
|
|
|
960 |
|
|
|
961 |
$this->reflow_content($context);
|
|
|
962 |
|
|
|
963 |
$context->pop_collapsed_margin();
|
|
|
964 |
|
|
|
965 |
// Floats and boxes with overflow: hidden
|
|
|
966 |
// should completely enclose its child floats
|
|
|
967 |
$this->fitFloats($context);
|
|
|
968 |
|
|
|
969 |
// restore old float flow context
|
|
|
970 |
$context->pop_floats();
|
|
|
971 |
|
|
|
972 |
// Add this box to the list of floats in current context
|
|
|
973 |
$context->add_float($this);
|
|
|
974 |
|
|
|
975 |
// Now fix the value of _current_x for the parent box; it is required
|
|
|
976 |
// in the following case:
|
|
|
977 |
// <body><img align="left">some text
|
|
|
978 |
// in such situation floating image is flown immediately, but it the close_line call have been made before,
|
|
|
979 |
// so _current_x value of container box will be still equal to ots left content edge; by calling float_left_x again,
|
|
|
980 |
// we'll force "some text" to be offset to the right
|
|
|
981 |
$parent->_current_x = $context->float_left_x($parent->_current_x, $parent->_current_y);
|
|
|
982 |
}
|
|
|
983 |
|
|
|
984 |
function reflow_whitespace(&$linebox_started, &$previous_whitespace) {
|
|
|
985 |
$previous_whitespace = false;
|
|
|
986 |
$linebox_started = false;
|
|
|
987 |
|
|
|
988 |
$size = count($this->content);
|
|
|
989 |
for ($i=0; $i<$size; $i++) {
|
|
|
990 |
$child =& $this->content[$i];
|
|
|
991 |
|
|
|
992 |
$child->reflow_whitespace($linebox_started, $previous_whitespace);
|
|
|
993 |
};
|
|
|
994 |
|
|
|
995 |
// remove the last whitespace in block box
|
|
|
996 |
$this->remove_last_whitespace();
|
|
|
997 |
|
|
|
998 |
// Non-inline box have terminated; we may be sure that line box will be closed
|
|
|
999 |
// at this moment and new line box after this will be generated
|
|
|
1000 |
if (!is_inline($this)) {
|
|
|
1001 |
$linebox_started = false;
|
|
|
1002 |
};
|
|
|
1003 |
|
|
|
1004 |
return;
|
|
|
1005 |
}
|
|
|
1006 |
|
|
|
1007 |
function remove_last_whitespace() {
|
|
|
1008 |
if (count($this->content) == 0) {
|
|
|
1009 |
return;
|
|
|
1010 |
};
|
|
|
1011 |
|
|
|
1012 |
$i = count($this->content)-1;
|
|
|
1013 |
$last = $this->content[$i];
|
|
|
1014 |
while ($i >= 0 && is_whitespace($this->content[$i])) {
|
|
|
1015 |
$this->remove($this->content[$i]);
|
|
|
1016 |
|
|
|
1017 |
$i --;
|
|
|
1018 |
if ($i >= 0) {
|
|
|
1019 |
$last = $this->content[$i];
|
|
|
1020 |
};
|
|
|
1021 |
};
|
|
|
1022 |
|
|
|
1023 |
if ($i >= 0) {
|
|
|
1024 |
if (is_container($this->content[$i])) {
|
|
|
1025 |
$this->content[$i]->remove_last_whitespace();
|
|
|
1026 |
};
|
|
|
1027 |
};
|
|
|
1028 |
}
|
|
|
1029 |
|
|
|
1030 |
function remove(&$box) {
|
|
|
1031 |
$size = count($this->content);
|
|
|
1032 |
for ($i=0; $i<$size; $i++) {
|
|
|
1033 |
if ($this->content[$i]->uid === $box->uid) {
|
|
|
1034 |
$this->content[$i] = NullBox::create();
|
|
|
1035 |
};
|
|
|
1036 |
};
|
|
|
1037 |
|
|
|
1038 |
return;
|
|
|
1039 |
}
|
|
|
1040 |
|
|
|
1041 |
function is_first(&$box) {
|
|
|
1042 |
$first =& $this->get_first();
|
|
|
1043 |
|
|
|
1044 |
// Check if there's no first box at all
|
|
|
1045 |
//
|
|
|
1046 |
if (is_null($first)) { return false; };
|
|
|
1047 |
|
|
|
1048 |
return $first->uid == $box->uid;
|
|
|
1049 |
}
|
|
|
1050 |
|
|
|
1051 |
function is_null() {
|
|
|
1052 |
$size = count($this->content);
|
|
|
1053 |
for ($i=0; $i<$size; $i++) {
|
|
|
1054 |
if (!$this->content[$i]->is_null()) { return false; };
|
|
|
1055 |
};
|
|
|
1056 |
return true;
|
|
|
1057 |
}
|
|
|
1058 |
|
|
|
1059 |
// Calculate the available widths - e.g. content width minus space occupied by floats;
|
|
|
1060 |
// as floats may not fill the whole height of this box, this value depends on Y-coordinate.
|
|
|
1061 |
// We use current_Y in calculations
|
|
|
1062 |
//
|
|
|
1063 |
function get_available_width(&$context) {
|
|
|
1064 |
$left_float_width = $context->float_left_x($this->get_left(), $this->_current_y) - $this->get_left();
|
|
|
1065 |
$right_float_width = $this->get_right() - $context->float_right_x($this->get_right(), $this->_current_y);
|
|
|
1066 |
return $this->get_width() - $left_float_width - $right_float_width;
|
|
|
1067 |
}
|
|
|
1068 |
|
|
|
1069 |
function pre_reflow_images() {
|
|
|
1070 |
$size = count($this->content);
|
|
|
1071 |
for ($i=0; $i<$size; $i++) {
|
|
|
1072 |
$this->content[$i]->pre_reflow_images();
|
|
|
1073 |
};
|
|
|
1074 |
}
|
|
|
1075 |
|
|
|
1076 |
function _setupClip(&$driver) {
|
|
|
1077 |
if (!is_null($this->parent)) {
|
|
|
1078 |
$this->parent->_setupClip($driver);
|
|
|
1079 |
};
|
|
|
1080 |
|
|
|
1081 |
$overflow = $this->get_css_property(CSS_OVERFLOW);
|
|
|
1082 |
if ($overflow !== OVERFLOW_VISIBLE && !$GLOBALS['g_config']['debugnoclip']) {
|
|
|
1083 |
$driver->moveto( $this->get_left_border() , $this->get_top_border());
|
|
|
1084 |
$driver->lineto( $this->get_right_border(), $this->get_top_border());
|
|
|
1085 |
$driver->lineto( $this->get_right_border(), $this->get_bottom_border());
|
|
|
1086 |
$driver->lineto( $this->get_left_border() , $this->get_bottom_border());
|
|
|
1087 |
$driver->closepath();
|
|
|
1088 |
$driver->clip();
|
|
|
1089 |
};
|
|
|
1090 |
}
|
|
|
1091 |
|
|
|
1092 |
/**
|
|
|
1093 |
* DOMish functions
|
|
|
1094 |
*/
|
|
|
1095 |
function &get_element_by_id($id) {
|
|
|
1096 |
if (isset($GLOBALS['__html_box_id_map'])) {
|
|
|
1097 |
return $GLOBALS['__html_box_id_map'][$id];
|
|
|
1098 |
} else {
|
|
|
1099 |
$dummy = null;
|
|
|
1100 |
return $dummy;
|
|
|
1101 |
};
|
|
|
1102 |
}
|
|
|
1103 |
|
|
|
1104 |
/*
|
|
|
1105 |
* this is just a fake at the moment
|
|
|
1106 |
*/
|
|
|
1107 |
function get_body() {
|
|
|
1108 |
return $this;
|
|
|
1109 |
}
|
|
|
1110 |
|
|
|
1111 |
function getChildNodes() {
|
|
|
1112 |
return $this->content;
|
|
|
1113 |
}
|
|
|
1114 |
}
|
|
|
1115 |
|
|
|
1116 |
?>
|