top = $this->top;
$box->right = $this->right;
$box->bottom = $this->bottom;
$box->left = $this->left;
return $box;
}
function offset($dx, $dy) {
$this->top += $dy;
$this->bottom += $dy;
$this->left += $dx;
$this->right += $dx;
}
function create(&$box) {
$lbox = new LineBox;
$lbox->top = $box->get_top();
$lbox->right = $box->get_right();
$lbox->bottom = $box->get_bottom();
$lbox->left = $box->get_left();
// $lbox->bottom = $box->get_top() - $box->get_baseline() - $box->get_descender();
// $lbox->top = $box->get_top() - $box->get_baseline() + $box->get_ascender();
return $lbox;
}
function extend(&$box) {
$base = $box->get_top() - $box->get_baseline();
$this->top = max($this->top, $base + $box->get_ascender());
$this->right = max($this->right, $box->get_right());
$this->bottom = min($this->bottom, $base - $box->get_descender());
// Left edge of the line box should never be modified
}
function fake_box(&$box) {
// Create the fake box object
$fake_state = new CSSState(CSS::get());
$fake_state->pushState();
$fake = null;
$fake_box = new BlockBox($fake);
$fake_box->readCSS($fake_state);
// Setup fake box size
$fake_box->put_left($this->left);
$fake_box->put_width($this->right - $this->left);
$fake_box->put_top($this->top - $box->baseline);
$fake_box->put_height($this->top - $this->bottom);
// Setup padding value
$fake_box->setCSSProperty(CSS_PADDING, $box->get_css_property(CSS_PADDING));
// Setup fake box border and background
$fake_box->setCSSProperty(CSS_BACKGROUND, $box->get_css_property(CSS_BACKGROUND));
$fake_box->setCSSProperty(CSS_BORDER, $box->get_css_property(CSS_BORDER));
return $fake_box;
}
}
class InlineBox extends GenericInlineBox {
var $_lines;
function InlineBox() {
// Call parent's constructor
$this->GenericInlineBox();
// Clear the list of line boxes inside this box
$this->_lines = array();
}
function &create(&$root, &$pipeline) {
// Create contents of this inline box
if ($root->node_type() == XML_TEXT_NODE) {
$css_state =& $pipeline->get_current_css_state();
$box = InlineBox::create_from_text($root->content,
$css_state->get_property(CSS_WHITE_SPACE),
$pipeline);
return $box;
} else {
$box =& new InlineBox();
$css_state =& $pipeline->get_current_css_state();
$box->readCSS($css_state);
// Initialize content
$child = $root->first_child();
while ($child) {
$child_box =& create_pdf_box($child, $pipeline);
$box->add_child($child_box);
$child = $child->next_sibling();
};
// Add fake whitespace box with zero size for the anchor spans
// We need this, as "reflow" functions will automatically remove empty inline boxes from the
// document tree
//
if ($box->is_null()) {
$css_state->pushState();
$css_state->set_property(CSS_FONT_SIZE, Value::fromData(0.01, UNIT_PT));
$whitespace = WhitespaceBox::create($pipeline);
$whitespace->readCSS($css_state);
$box->add_child($whitespace);
$css_state->popState();
};
}
return $box;
}
function &create_from_text($text, $white_space, &$pipeline) {
$box =& new InlineBox();
$box->readCSS($pipeline->get_current_css_state());
// Apply/inherit text-related CSS properties
$css_state =& $pipeline->get_current_css_state();
$css_state->pushDefaultTextState();
require_once(HTML2PS_DIR.'inline.content.builder.factory.php');
$inline_content_builder =& InlineContentBuilderFactory::get($white_space);
$inline_content_builder->build($box, $text, $pipeline);
// Clear the CSS stack
$css_state->popState();
return $box;
}
function &get_line_box($index) {
$line_box =& $this->_lines[$index];
return $line_box;
}
function get_line_box_count() {
return count($this->_lines);
}
// Inherited from GenericFormattedBox
function process_word($raw_content, &$pipeline) {
if ($raw_content === '') {
return false;
}
$ptr = 0;
$word = '';
$hyphens = array();
$encoding = 'iso-8859-1';
$manager_encoding =& ManagerEncoding::get();
$text_box =& TextBox::create_empty($pipeline);
$len = strlen($raw_content);
while ($ptr < $len) {
$char = $manager_encoding->get_next_utf8_char($raw_content, $ptr);
// Check if current char is a soft hyphen character. It it is,
// remove it from the word (as it should not be drawn normally)
// and store its location
if ($char == SYMBOL_SHY) {
$hyphens[] = strlen($word);
} else {
$mapping = $manager_encoding->get_mapping($char);
/**
* If this character is not found in predefined encoding vectors,
* we'll use "Custom" encoding and add single-character TextBox
*
* @TODO: handle characters without known glyph names
*/
if (is_null($mapping)) {
/**
* No mapping to default encoding vectors found for this character
*/
/**
* Add last word
*/
if ($word !== '') {
$text_box->add_subword($word, $encoding, $hyphens);
};
/**
* Add current symbol
*/
$custom_char = $manager_encoding->add_custom_char(utf8_to_code($char));
$text_box->add_subword($custom_char, $manager_encoding->get_current_custom_encoding_name(), $hyphens);
$word = '';
} else {
if (isset($mapping[$encoding])) {
$word .= $mapping[$encoding];
} else {
// This condition prevents empty text boxes from appearing; say, if word starts with a national
// character, an () - text box with no letters will be generated, in rare case causing a random line
// wraps, if container is narrow
if ($word !== '') {
$text_box->add_subword($word, $encoding, $hyphens);
};
reset($mapping);
list($encoding, $add) = each($mapping);
$word = $mapping[$encoding];
$hyphens = array();
};
};
};
};
if ($word !== '') {
$text_box->add_subword($word, $encoding, $hyphens);
};
$this->add_child($text_box);
return true;
}
function show(&$driver) {
if ($this->get_css_property(CSS_POSITION) == POSITION_RELATIVE) {
// Postpone
return true;
};
return $this->_show($driver);
}
function show_postponed(&$driver) {
return $this->_show($driver);
}
function _show(&$driver) {
// Show line boxes background and borders
$size = $this->get_line_box_count();
for ($i=0; $i<$size; $i++) {
$line_box = $this->get_line_box($i);
$fake_box = $line_box->fake_box($this);
$background = $this->get_css_property(CSS_BACKGROUND);
$border = $this->get_css_property(CSS_BORDER);
$background->show($driver, $fake_box);
$border->show($driver, $fake_box);
};
// Show content
$size = count($this->content);
for ($i=0; $i < $size; $i++) {
if (is_null($this->content[$i]->show($driver))) {
return null;
};
}
return true;
}
// Initialize next line box inside this inline
//
// Adds the next element to _lines array inside the current object and initializes it with the
// $box parameters
//
// @param $box child box which will be first in this line box
// @param $line_no number of line box
//
function init_line(&$box, &$line_no) {
$line_box = LineBox::create($box);
$this->_lines[$line_no] = $line_box;
}
// Extends the existing line box to include the given child
// OR starts new line box, if current child is to the left of the box right edge
// (which should not happen white the line box is filled)
//
// @param $box child box which will be first in this line box
// @param $line_no number of line box
//
function extend_line(&$box, $line_no) {
if (!isset($this->_lines[$line_no])) {
// New line box started
$this->init_line($box, $line_no);
return $line_no;
};
// Check if this box starts a new line
if ($box->get_left() < $this->_lines[$line_no]->right) {
$line_no++;
$this->init_line($box, $line_no);
return $line_no;
};
$this->_lines[$line_no]->extend($box);
return $line_no;
}
function merge_line(&$box, $line_no) {
$start_line = 0;
if ($line_no > 0 && count($box->_lines) > 0) {
if ($this->_lines[$line_no-1]->right + EPSILON > $box->_lines[0]->left) {
$this->_lines[$line_no-1]->right = max($box->_lines[0]->right, $this->_lines[$line_no-1]->right);
$this->_lines[$line_no-1]->top = max($box->_lines[0]->top, $this->_lines[$line_no-1]->top);
$this->_lines[$line_no-1]->bottom = min($box->_lines[0]->bottom, $this->_lines[$line_no-1]->bottom);
$start_line = 1;
};
};
$size = count($box->_lines);
for ($i=$start_line; $i<$size; $i++) {
$this->_lines[] = $box->_lines[$i]->copy();
};
return count($this->_lines);
}
function reflow_static(&$parent, &$context) {
GenericFormattedBox::reflow($parent, $context);
// Note that inline boxes (actually SPANS)
// are never added to the parent's line boxes
// Move current box to the parent's current coordinates
// Note that span box will start at the far left of the parent, NOT on its current X!
// Also, note that inline box can have margins, padding and borders!
$this->put_left($parent->get_left());
$this->put_top($parent->get_top() - $this->get_extra_top());
// first line of the SPAN will be offset to its parent current-x
// PLUS the left padding of current span!
$parent->_current_x += $this->get_extra_left();
$this->_current_x = $parent->_current_x;
// Note that the same operation IS NOT applied to parent current-y!
// The padding space is just extended to the top possibly OVERLAPPING the above boxes.
$this->width = 0;
// Reflow contents
$size = count($this->content);
for ($i=0; $i<$size; $i++) {
$child =& $this->content[$i];
// Add current element into _parent_ line box and reflow it
$child->reflow($parent, $context);
// In general, if inline box centained whitespace box only,
// it could be removed during reflow function call;
// let's check it and skip to next child
//
// if no children left AT ALL (so this box is empty), just exit
// Track the real height of the inline box; it will be used by other functions
// (say, functions calculating content height)
$this->extend_height($child->get_bottom_margin());
};
// Apply right extra space value (padding + border + margin)
$parent->_current_x += $this->get_extra_right();
// Margins of inline boxes are not collapsed
if ($this->get_first_data()) {
$context->pop_collapsed_margin();
$context->push_collapsed_margin( 0 );
};
}
function reflow_inline() {
$line_no = 0;
$size = count($this->content);
for ($i=0; $i<$size; $i++) {
$child =& $this->content[$i];
$child->reflow_inline();
if (!$child->is_null()) {
if (is_a($child,'InlineBox')) {
$line_no = $this->merge_line($child, $line_no);
} else {
$line_no = $this->extend_line($child, $line_no);
};
};
};
}
function reflow_whitespace(&$linebox_started, &$previous_whitespace) {
/**
* Anchors could have no content at all (like ).
* We should not remove such anchors, as this will break internal links
* in the document.
*/
$dest = $this->get_css_property(CSS_HTML2PS_LINK_DESTINATION);
if (!is_null($dest)) {
return;
};
$size = count($this->content);
for ($i=0; $i<$size; $i++) {
$child =& $this->content[$i];
$child->reflow_whitespace($linebox_started, $previous_whitespace);
};
if ($this->is_null()) {
$this->parent->remove($this);
};
}
function get_extra_line_left() {
return $this->get_extra_left() + ($this->parent ? $this->parent->get_extra_line_left() : 0);
}
function get_extra_line_right() {
return $this->get_extra_right() + ($this->parent ? $this->parent->get_extra_line_right() : 0);
}
/**
* As "nowrap" properties applied to block-level boxes only, we may use simplified version of
* 'get_min_width' here
*/
function get_min_width(&$context) {
if (isset($this->_cache[CACHE_MIN_WIDTH])) {
return $this->_cache[CACHE_MIN_WIDTH];
}
$content_size = count($this->content);
/**
* If box does not have any content, its minimal width is determined by extra horizontal space
*/
if ($content_size == 0) {
return $this->_get_hor_extra();
};
$minw = $this->content[0]->get_min_width($context);
for ($i=1; $i<$content_size; $i++) {
$item = $this->content[$i];
if (!$item->out_of_flow()) {
$minw = max($minw, $item->get_min_width($context));
};
}
// Apply width constraint to min width. Return maximal value
$wc = $this->get_css_property(CSS_WIDTH);
$min_width = max($minw, $wc->apply($minw, $this->parent->get_width())) + $this->_get_hor_extra();
$this->_cache[CACHE_MIN_WIDTH] = $min_width;
return $min_width;
}
// Restore default behaviour, as this class is a ContainerBox descendant
function get_max_width_natural(&$context, $limit=10E6) {
return $this->get_max_width($context, $limit);
}
function offset($dx, $dy) {
$size = count($this->_lines);
for ($i=0; $i<$size; $i++) {
$this->_lines[$i]->offset($dx, $dy);
};
GenericInlineBox::offset($dx, $dy);
}
/**
* Deprecated
*/
function getLineBoxCount() {
return $this->get_line_box_count();
}
function &getLineBox($index) {
return $this->get_line_box($index);
}
};
?>