<?php
/**
 * HTML Report Generator
 *
 * used by the SAX parser to generate HTML reports from the XML report file.
 *
 * webtrees: Web based Family History software
 * Copyright (C) 2010 webtrees development team.
 *
 * Derived from PhpGedView
 * Copyright (C) 2002 to 2009  PGV Development Team.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * @package webtrees
 * @subpackage Reports
 * @version $Id: class_reporthtml.php 9768 2010-11-05 17:54:39Z lukasz $
 */

if (!defined('WT_WEBTREES')) {
	header('HTTP/1.0 403 Forbidden');
	exit;
}

define('WT_CLASS_REPORTHTML_PHP', '');

require_once WT_ROOT."includes/classes/class_reportbase.php";

/**
* Main WT Report Class for HTML
*
* @package webtrees
* @subpackage Reports
*/
class ReportBaseHTML extends ReportBase {
	/**
	* Cell padding
	* @var int
	*/
	public $cPadding = 2;
	/**
	* Cell height ratio
	* @var float
	*/
	public $cellHeightRatio = 1.3;
	/**
	* Current horizontal position
	* @var int
	*/
	public $X = 0;
	/**
	* Current vertical position
	* @var int
	*/
	public $Y = 0;
	/**
	* Currently used style name
	* @var string
	*/
	public $currentStyle = '';
	/**
	* Page number counter
	* @var int
	*/
	public $pageN = 1;
	/**
	* Store the page width without left and right margins
	* In HTML, we don't need this
	* @var int
	*/
	public $noMarginWidth = 0;
	/**
	* Last cell height
	* @var int
	*/
	public $lastCellHeight = 0;
	/**
	* LTR or RTL alignement
	* "left" on LTR, "right" on RTL
	* Used in <div >
	* @var string
	*/
	public $alignRTL = 'left';
	/**
	* LTR or RTL entity
	*
	* @var string
	*/
	public $entityRTL = '&lrm;';
	/**
	* Largest Font Height is used by TextBox etc.
	* Use this to calculate a the text height.
	* This makes sure that the text fits into the cell/box when different font sizes are used
	* @var int
	*/
	public $largestFontHeight = 0;
	/**
	* Keep track of the highest Y position
	* Used with Header div / Body div / Footer div / "addpage" / The bottom of the last image etc.
	* @var float
	*/
	public $maxY = 0;

	public $headerElements = array();
	public $pageHeaderElements = array();
	public $footerElements = array();
	public $bodyElements = array();
	public $printedfootnotes = array();


	/**
	* HTML Setup - ReportBaseHTML
	*/
	function setup() {
		parent::setup();

		// Setting up the correct dimensions if Portrait (default) or Landscape
		if ($this->orientation == "landscape") {
			$tmpw = $this->pagew;
			$this->pagew = $this->pageh;
			$this->pageh = $tmpw;
		}
		// Store the pagewidth without margins
		$this->noMarginWidth = floor($this->pagew - $this->leftmargin - $this->rightmargin);
		// If RTL
		if ($this->rtl) {
			$this->alignRTL = "right";
			$this->entityRTL = "&rlm;";
		}
		// Change the default HTML font name
		$this->defaultFont = "Arial";

		if ($this->showGenText) {
			// The default style name for Generated by.... is 'genby'
			$element = new CellHTML(0, 10, 0, 'C', '', 'genby', 1, '.', '.', 0, 0, '', '', true);
			$element->addText($this->generatedby);
			$element->setUrl(parent::pgv_url);
			$this->footerElements[] = $element;
		}
	}

	function addElement($element) {
		if ($this->processing=="B") {
			return $this->bodyElements[] = $element;
		} elseif ($this->processing=="H") {
			return $this->headerElements[] = $element;
		} elseif ($this->processing=="F") {
			return $this->footerElements[] = $element;
		}
	}

	function runPageHeader() {
		foreach ($this->pageHeaderElements as $element) {
			if (is_object($element)) {
				$element->render($this);
			} elseif (is_string($element) && $element=="footnotetexts") {
				$this->Footnotes();
			} elseif (is_string($element) && $element=="addpage") {
				$this->AddPage();
			}
		}
	}

	function Footnotes() {
		$this->currentStyle = "";
		if (!empty($this->printedfootnotes)) {
			foreach ($this->printedfootnotes as $element) {
				$element->renderFootnote($this);
			}
		}
	}

	function run() {
		global $rtl_stylesheet, $stylesheet, $TEXT_DIRECTION;

		echo "
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
<html xmlns=\"http://www.w3.org/1999/xhtml\" ", i18n::html_markup(), ">
<head>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=", $this->charset, "\" />
<meta name=Generator content=\"", $this->generatedby, "\" />
<meta name=\"keywords\" content=\"", $this->rkeywords, "\" />
<meta name=\"description\" content=\"", $this->rsubject, "\" />
<title>", $this->title, "</title>
<link rel=\"stylesheet\" href=\"", $stylesheet, "\" type=\"text/css\" media=\"all\" />";

		// Check first if RTL
		if ($this->rtl) {
			if (!empty($rtl_stylesheet)) {
				echo "\n<link rel=\"stylesheet\" href=\"", $rtl_stylesheet, "\" type=\"text/css\" media=\"all\" />";
			}
		}

		// Setting up the styles
		echo "\n<style type=\"text/css\">\n";
		foreach ($this->Styles as $class => $style) {
			echo ".", $class, "{\n";
			if ($style["font"]=="dejavusans") {
				$style["font"] = $this->defaultFont;
			}
			echo "font-family: ", $style["font"], ";\n";
			echo "font-size: ", $style["size"], "pt;\n";
			// Case-insensitive
			if (stripos($style["style"], "B")!==false) echo "font-weight: bold;\n";
			if (stripos($style["style"], "I")!==false) echo "font-style: italic;\n";
			if (stripos($style["style"], "U")!==false) echo "text-decoration: underline;\n";
			if (stripos($style["style"], "D")!==false) echo "text-decoration: line-through;\n";
			echo "}\n";
		}
		unset($class, $style);
		//-- header divider
		echo "
</style>
</head>\n<body>
<div id=\"headermargin\" style=\"position:relative; top:auto; height:", $this->headermargin, "pt; width:", $this->noMarginWidth, "pt;\"></div>
<div id=\"headerdiv\" style=\"position:relative; top:auto; width:", $this->noMarginWidth, "pt;\">";
		foreach ($this->headerElements as $element) {
			if (is_object($element)) {
				$element->render($this);
			} elseif (is_string($element) && $element=="footnotetexts") {
				$this->Footnotes();
			} elseif (is_string($element) && $element=="addpage") {
				$this->AddPage();
			}
		}
		//-- body
		echo "
</div>", WT_JS_START, "document.getElementById('headerdiv').style.height='", $this->topmargin + $this->headermargin + 10, "pt';", WT_JS_END,
"<div id=\"bodydiv\" style=\"position:relative; top:auto; width:", $this->noMarginWidth, "pt; height:100%;\">";
		$this->Y = 0;
		$this->maxY = 0;
		$this->runPageHeader();
		foreach ($this->bodyElements as $element) {
			if (is_object($element)) {
				$element->render($this);
			} elseif (is_string($element) && $element=="footnotetexts") {
				$this->Footnotes();
			} elseif (is_string($element) && $element=="addpage") {
				$this->AddPage();
			}
		}
		//-- footer
		echo "
</div>", WT_JS_START, "document.getElementById('bodydiv').style.height='", $this->maxY, "pt';", WT_JS_END,
"<div id=\"bottommargin\" style=\"position:relative; top:auto; height:", $this->bottommargin - $this->footermargin, "pt; width:", $this->noMarginWidth, "pt;\"></div>
<div id=\"footerdiv\" style=\"position:relative; top:auto; width: ", $this->noMarginWidth, "pt; height:auto;\">";
		$this->Y = 0;
		$this->X = 0;
		$this->maxY = 0;
		foreach ($this->footerElements as $element) {
			if (is_object($element)) {
				$element->render($this);
			} elseif (is_string($element) && $element=="footnotetexts") {
				$this->Footnotes();
			} elseif (is_string($element) && $element=="addpage") {
				$this->AddPage();
			}
		}
		echo "
</div>", WT_JS_START, "document.getElementById('footerdiv').style.height='", $this->maxY, "pt';", WT_JS_END,
"<div id=\"footermargin\" style=\"position:relative; top:auto; height:", $this->footermargin, "pt; width:", $this->noMarginWidth, "pt;\"></div>
</body>\n</html>\n";
	}

	/**
	* Create a new Cell object - ReportBaseHTML
	*
	* @param int $width cell width (expressed in points)
	* @param int $height cell height (expressed in points)
	* @param mixed $border Border style
	* @param string $align Text alignement
	* @param string $bgcolor Background color code
	* @param string $style The name of the text style
	* @param int $ln Indicates where the current position should go after the call
	* @param mixed $top Y-position
	* @param mixed $left X-position
	* @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
	* @param int $stretch Stretch carachter mode
	* @param string $bocolor Border color
	* @param string $tcolor Text color
	* @param boolean $reseth
	* @return object CellHTML
	*/
	function createCell($width, $height, $border, $align, $bgcolor, $style, $ln, $top, $left, $fill, $stretch, $bocolor, $tcolor, $reseth) {
		return new CellHTML($width, $height, $border, $align, $bgcolor, $style, $ln, $top, $left, $fill, $stretch, $bocolor, $tcolor, $reseth);
	}

	function createTextBox($width, $height, $border, $bgcolor, $newline, $left, $top, $pagecheck, $style, $fill, $padding, $reseth) {
		return new TextBoxHTML($width, $height, $border, $bgcolor, $newline, $left, $top, $pagecheck, $style, $fill, $padding, $reseth);
	}

	function createText($style, $color) {
		return new TextHTML($style, $color);
	}

	function createFootnote($style="") {
		return new FootnoteHTML($style);
	}

	function createPageHeader() {
		return new PageHeaderHTML();
	}

	function createImage($file, $x, $y, $w, $h, $align, $ln) {
		return new ImageHTML($file, $x, $y, $w, $h, $align, $ln);
	}

	function createLine($x1, $y1, $x2, $y2) {
		return new LineHTML($x1, $y1, $x2, $y2);
	}

	function createHTML($tag, $attrs) {
		return new HtmlHTML($tag, $attrs);
	}

	/**
	* Clear the Header - ReportBaseHTML
	*/
	function clearHeader() {
		$this->headerElements = array();
	}


	/****************************
	* Local HTML Report functions
	****************************/


	/**
	* Update the Page Number and set a new Y if max Y is larger - ReportBaseHTML
	* @todo add page break - <p style='page-break-before:always' />
	*/
	function AddPage() {
		//echo("\n\n<p style=\"page-break-before:always;\" /><p/>\n");
		$this->pageN++;
		// Add a little margin to max Y "between pages"
		$this->maxY += 10;
		// If Y is still heigher by any reason...
		if ($this->maxY < $this->Y) {
			// ... update max Y
			$this->maxY = $this->Y;
		}
		// else update Y so that nothing will be overwritten, like images or cells...
		else {
			$this->Y = $this->maxY;
		}
	}

	/**
	* Uppdate max Y to keep track it incase of a pagebreak - ReportBaseHTML
	* @param float $y
	*/
	function addMaxY($y) {
		if ($this->maxY < $y) {
			$this->maxY = $y;
		}
	}

	function addPageHeader($element) {
		$this->pageHeaderElements[] = $element;
		return count($this->headerElements)-1;
	}

	/**
	* Checks the Footnote and numbers them - ReportBaseHTML
	* @param object &$footnote
	* @return boolen false if not numbered before | object if already numbered
	*/
	function checkFootnote(&$footnote) {
		$ct = count($this->printedfootnotes);
		$i = 0;
		$val = $footnote->getValue();
		while ($i < $ct) {
			if ($this->printedfootnotes[$i]->getValue() == $val) {
				// If this footnote already exist then set up the numbers for this object
				$footnote->setNum($i + 1);
				$footnote->setAddlink($i + 1);
				return $this->printedfootnotes[$i];
			}
			$i++;
		}
		// If this Footnote has not been set up yet
		$footnote->setNum($ct + 1);
		$footnote->setAddlink($ct + 1);
		$this->printedfootnotes[] = $footnote;
		return false;
	}

	/**
	* Clear the Page Header - ReportBaseHTML
	*/
	function clearPageHeader() {
		$this->pageHeaderElements = array();
	}

	/**
	* Count the number of lines - ReportBaseHTML
	* @param string &$str
	* @return int Number of lines. 0 if empty line
	*/
	function countLines(&$str) {
		if ($str == "") {
			return 0;
		}
		return (substr_count($str, "\n") + 1);
	}

	function getCurrentStyle() {
		return $this->currentStyle;
	}

	function getCurrentStyleHeight() {
		if (empty($this->currentStyle)) {
			return $this->defaultFontSize;
		}
		$style = $this->getStyle($this->currentStyle);
		return $style["size"];
	}

	function getFootnotesHeight($cellWidth) {
		$h = 0;
		foreach ($this->printedfootnotes as $element) {
			$h += $element->getFootnoteHeight($this, $cellWidth);
		}
		return $h;
	}

	/**
	* Get the maximum width from current position to the margin - ReportBaseHTML
	* @return float
	*/
	function getRemainingWidth() {
		return floor($this->noMarginWidth - $this->X);
	}

	function getPageHeight() {
		return $this->pageh - $this->topmargin;
	}

	function getStringWidth($text) {
		$style = $this->getStyle($this->currentStyle);
		return utf8_strlen($text) * ($style["size"]/2);
	}

	/**
	* Get a text height in points - ReportBaseHTML
	* @param &$str
	* @return int
	*/
	function getTextCellHeight(&$str) {
		// Count the number of lines to calculate the height
		$nl = $this->countLines($str);
		// Calculate the cell height
		return ceil(($this->getCurrentStyleHeight() * $this->cellHeightRatio) * $nl);
	}

	/**
	* Get the current X position - ReportBaseHTML
	* @return float
	*/
	function GetX() {
		return $this->X;
	}

	/**
	* Get the current Y position - ReportBaseHTML
	* @return float
	*/
	function GetY() {
		return $this->Y;
	}

	/**
	* Get the current page number - ReportBaseHTML
	* @return int
	*/
	function PageNo() {
		return $this->pageN;
	}

	function setCurrentStyle($s) {
		$this->currentStyle = $s;
	}

	/**
	* Set the X position - ReportBaseHTML
	* @param float $x
	*/
	function SetX($x) {
		$this->X = $x;
	}

	/**
	* Set the Y position - ReportBaseHTML
	* Also updates Max Y position
	* @param float $y
	*/
	function SetY($y) {
		$this->Y = $y;
		if ($this->maxY < $y) {
			$this->maxY = $y;
		}
	}

	/**
	* Set the X and Y position - ReportBaseHTML
	* Also updates Max Y position
	* @param float $x
	* @param float $y
	*/
	function SetXY($x, $y) {
		$this->X = $x;
		// Don't reinvent the wheel, use this instead
		$this->SetY($y);
	}

	/**
	* Wrap text - ReportBaseHTML
	* @param string &$str Text to wrap
	* @param int $width Width in points the text has to fit into
	* @return string
	*/
	function textWrap(&$str, $width) {
		// Calculate the line width
		$lw = floor($width / ($this->getCurrentStyleHeight() / 2));
		// Wordwrap each line
		$lines = explode("\n", $str);
		// Line Feed counter
		$lfct = count($lines);
		$wraptext = '';
		foreach ($lines as $line) {
			$wtext = '';
			$wtext = utf8_wordwrap($line, $lw, "\n", true);
			$wraptext .= $wtext;
			// Add a new line as long as it's not the last line
			if ($lfct > 1) {
				$wraptext.= "\n";
			}
			$lfct--;
		}
		return $wraptext;
	}

	/**
	* Write text - ReportBaseHTML
	* @param string $text Text to print
	* @param string $color HTML RGB color code (Ex: #001122)
	*/
	function write($text, $color="", $useclass=true) {
		global $TEXT_DIRECTION;

		$style = $this->getStyle($this->getCurrentStyle());
		$htmlcode = "<span dir=\"$TEXT_DIRECTION\"";
		if ($useclass) {
			$htmlcode .= " class=\"". $style["name"]. "\"";
		}
		if (!empty($color)) {
			// Check if Text Color is set and if it's valid HTML color
			if (preg_match("/#?(..)(..)(..)/", $color)) {
				$htmlcode .= " style=\"color:$color;\"";
			}
		}
		$htmlcode .= ">$text</span>";
		$htmlcode = str_replace(array("\n", "> ", " <", "+", ","), array("<br />", ">&nbsp;", "&nbsp;<", $this->entityRTL."+", $this->entityRTL.","), $htmlcode);
		echo $htmlcode;
	}

} //-- end Report


/**
* Report Base class of ReportBaseHTML
*
* @global class $wt_report
* @ignore
*/
$wt_report = new ReportBaseHTML();

$ReportRoot = $wt_report;


/**
* Cell element - HTML
*
* @package webtrees
* @subpackage Reports
* @todo add info and fix border and filling
*/
class CellHTML extends Cell {
	/**
	* Create a class CELL for HTML
	* @param int $width cell width (expressed in points)
	* @param int $height cell height (expressed in points)
	* @param mixed $border Border style
	* @param string $align Text alignement
	* @param string $bgcolor Background color code
	* @param string $style The name of the text style
	* @param int $ln Indicates where the current position should go after the call
	* @param mixed $top Y-position
	* @param mixed $left X-position
	* @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
	* @param int $stretch Stretch carachter mode
	* @param string $bocolor Border color
	* @param string $tcolor Text color
	* @param boolean $reseth
	*/
	function CellHTML($width, $height, $border, $align, $bgcolor, $style, $ln, $top, $left, $fill, $stretch, $bocolor, $tcolor, $reseth) {
		parent::Cell($width, $height, $border, $align, $bgcolor, $style, $ln, $top, $left, $fill, $stretch, $bocolor, $tcolor, $reseth);
	}

	/**
	* HTML Cell renderer
	* @param ReportBaseHTML &$html
	*/
	function render(&$html) {

		if (strpos($this->text, "{{nb}}") !== false) {
			return;
		}
		$temptext = str_replace("#PAGENUM#", $html->PageNo(), $this->text);

		// Setup the style name
		if ($html->getCurrentStyle() != $this->styleName) {
			$html->setCurrentStyle($this->styleName);
		}

		/**
		* Keep the original values, use these local variables
		*/
		$cW = 0; // Class Width
		$cP = 0; // Class Padding

		// If (Future-feature-enable/disable cell padding)
		$cP = $html->cPadding;

		// Adjust the positions
		if ($this->left == ".") {
			$this->left = $html->GetX();
		} else {
			$html->SetX($this->left);
		}

		if ($this->top == ".") {
			$this->top = $html->GetY();
		} else {
			$html->SetY($this->top);
		}

		// Start collecting the HTML code
		echo "<div class=\"", $this->styleName, "\" style=\"position:absolute; top:", $this->top, "pt; ";
		// Use Cell around padding to support RTL also
		echo "padding:", $cP, "pt; ";
		// LTR (left) or RTL (right)
		echo $html->alignRTL, ":", $this->left, "pt; ";
		// Background color
		if (!empty($this->bgcolor)) {
			echo "background-color:", $this->bgcolor, "; ";
		}
		// Border setup
		$bpixX = 0;
		$bpixY = 0;
		if (!empty($this->border)) {
			// Border all around
			if ($this->border == 1) {
				echo " border:solid ";
				if (!empty($this->bocolor)) {
					echo $this->bocolor;
				} else {
					echo "black";
				}
				echo " 1pt;";
				$bpixX = 1;
				$bpixY = 1;
			} else {
				if (stripos($this->border, "T") !== false) {
					echo " border-top:solid ";
					if (!empty($this->bocolor)) {
						echo $this->bocolor;
					} else {
						echo "black";
					}
					echo " 1pt;";
					$bpixY = 1;
				}
				if (stripos($this->border, "B") !== false) {
					echo " border-bottom:solid ";
					if (!empty($this->bocolor)) {
						echo $this->bocolor;
					} else {
						echo "black";
					}
					echo " 1pt;";
					$bpixY = 1;
				}
				if (stripos($this->border, "R") !== false) {
					echo " border-right:solid ";
					if (!empty($this->bocolor)) {
						echo $this->bocolor;
					} else {
						echo "black";
					}
					echo " 1pt;";
					$bpixX = 1;
				}
				if (stripos($this->border, "L") !== false) {
					echo " border-left:solid ";
					if (!empty($this->bocolor)) {
						echo $this->bocolor;
					} else {
						echo "black";
					}
					echo " 1pt;";
					$bpixX = 1;
				}
			}
		}
		// Check the width if set to page wide OR set by xml to larger then page wide
		if (($this->width == 0) or $this->width > $html->getRemainingWidth()) {
			$this->width = $html->getRemainingWidth();
		}
		// We have to calculate a different width for the padding, counting on both side
		$cW = $this->width - ($cP * 2);

		// If there is any text
		if (!empty($temptext)) {
			// Wrap the text
			$temptext = $html->textWrap($temptext, $cW);
			$tmph = $html->getTextCellHeight($temptext);
			// Add some cell padding
			$this->height += $cP;
			if ($tmph > $this->height) {
				$this->height = $tmph;
			}
		}
		// Check the last cell height and ajust with the current cell height
		if ($html->lastCellHeight > $this->height) {
			$this->height = $html->lastCellHeight;
		}
		echo " width:", $cW - $bpixX, "pt; height:", $this->height - $bpixY, "pt;";

		// Text alignment
		switch($this->align) {
			case "C":
				echo " text-align:center; ";
				break;
			case "L":
				echo " text-align:left; ";
				break;
			case "R":
				echo " text-align:right; ";
				break;
		}

		// Print the collected HTML code
		echo "\">";

		// Print URL
		if (!empty($this->url)) {
			echo "<a href=\"", $this->url, "\">";
		}
		// Print any text if exists
		if (!empty($temptext)) {
			$html->write($temptext, $this->tcolor, false);
		}
		if (!empty($this->url)) {
			echo "</a>";
		}
		// Finish the cell printing and start to clean up
		echo "</div>\n";
		// Where to place the next position
		// -> Next to this cell in the same line
		if ($this->newline == 0) {
			$html->SetXY($this->left + $this->width, $this->top);
			$html->lastCellHeight = $this->height;
		}
		// -> On a new line at the margin - Default
		elseif ($this->newline == 1) {
			$html->SetXY(0, $html->GetY() + $this->height + ($cP * 2));
			// Reset the last cell height for the next line
			$html->lastCellHeight = 0;
		}
		// -> On a new line at the end of this cell
		elseif ($this->newline == 2) {
			$html->SetXY($html->GetX() + $this->width, $html->GetY() + $this->height + ($cP * 2));
			// Reset the last cell height for the next line
			$html->lastCellHeight = 0;
		}
	}
}

/**
* HTML element - HTML Report
*
* @package webtrees
* @subpackage Reports
* @todo add info
*/
class HtmlHTML extends Html {

	function HtmlHTML($tag, $attrs) {
		parent::Html($tag, $attrs);
	}

	/**
	*  @todo temporary fix
	*/
	function render(&$html, $sub = false, $inat=true) {

		if (!empty($this->attrs["pgvrstyle"])) $html->setCurrentStyle($this->attrs["pgvrstyle"]);

		$this->text = $this->getStart(). $this->text;
		foreach ($this->elements as $element) {
			if (is_string($element) && $element=="footnotetexts") {
				$html->Footnotes();
			} elseif (is_string($element) && $element=="addpage") {
				$html->AddPage();
			} elseif ($element->get_type()=="Html") {
				$this->text .= $element->render($html, true, false);
			} else {
				$element->render($html);
			}
		}
		$this->text .= $this->getEnd();
		if ($sub) return $this->text;

		// If not called by an other attribute
		if ($inat) {
			$startX = $html->GetX();
			$startY = $html->GetY();
			$width = $html->getRemainingWidth();
			echo "<div style=\"position: absolute; top: ", $startY, "pt; ", $html->alignRTL, ": ", $startX, "pt; width: ", $width, "pt;\">";
			$startY += $html->getCurrentStyleHeight() + 2;
			$html->SetY($startY);
		}

		echo $this->text;

		if ($inat) {
			echo "</div>\n";
		}
	}
}

/**
* TextBox element - HTML Report
*
* @package webtrees
* @subpackage Reports
*/
class TextBoxHTML extends TextBox {

	function TextBoxHTML($width, $height, $border, $bgcolor, $newline, $left, $top, $pagecheck, $style, $fill, $padding, $reseth) {
		parent::TextBox($width, $height, $border, $bgcolor, $newline, $left, $top, $pagecheck, $style, $fill, $padding, $reseth);
	}
	function render(&$html) {
// checkFootnote
		$newelements = array();
		$lastelement = array();
		$footnote_element = array();
		// Element counter
		$cE = count($this->elements);
		//-- collapse duplicate elements
		for ($i = 0; $i < $cE; $i++) {
			$element = $this->elements[$i];
			if (is_object($element)) {
				if ($element->get_type() == "Text") {
					if (!empty($footnote_element)) {
						ksort($footnote_element);
						foreach ($footnote_element as $links) {
							$newelements[] = $links;
						}
						$footnote_element = array();
					}
					if (empty($lastelement)) {
						$lastelement = $element;
					} else {
						// Checking if the Text has the same style
						if ($element->getStyleName() == $lastelement->getStyleName()) {
							$lastelement->addText(str_replace("\n", "<br />", $element->getValue()));
						} elseif (!empty($lastelement)) {
							$newelements[] = $lastelement;
							$lastelement = $element;
						}
					}
				}
				// Collect the Footnote links
				elseif ($element->get_type() == "Footnote") {
					// Check if the Footnote has been set with it's link number
					$html->checkFootnote($element);
					// Save first the last element if any
					if (!empty($lastelement)) {
						$newelements[] = $lastelement;
						$lastelement = array();
					}
					// Save the Footnote with it's link number as key for sorting later
					$footnote_element[$element->num] = $element;
				}
				//-- do not keep empty footnotes
				elseif (($element->get_type() != "Footnote") || (trim($element->getValue()) != "")) {
					if (!empty($footnote_element)) {
						ksort($footnote_element);
						foreach ($footnote_element as $links) {
							$newelements[] = $links;
						}
						$footnote_element = array();
					}
					if (!empty($lastelement)) {
						$newelements[] = $lastelement;
						$lastelement = array();
					}
					$newelements[] = $element;
				}
			} else {
				if (!empty($lastelement)) {
					$newelements[] = $lastelement;
					$lastelement = array();
				}
				if (!empty($footnote_element)) {
					ksort($footnote_element);
					foreach ($footnote_element as $links) {
						$newelements[] = $links;
					}
					$footnote_element = array();
				}
				$newelements[] = $element;
			}
		}
		if (!empty($lastelement)) {
			$newelements[] = $lastelement;
		}
		if (!empty($footnote_element)) {
			ksort($footnote_element);
			foreach ($footnote_element as $links) {
				$newelements[] = $links;
			}
		}
		$this->elements = $newelements;
		unset($footnote_element, $lastelement, $links, $newelements);

		/**
		* Use these variables to update/manipulate values
		* Repeted classes would reupdate all their class variables again, Header/Page Header/Footer
		* This is the bugfree version
		*/
		$cH = 0; // Class Height
		$cX = 0; // Class Left
		// Protect height, width, lastheight from padding
		$cP = 0; // Class Padding
		$cW = 0; // Class Width
		// Used with line breaks and cell height calculation within this box only
		$html->largestFontHeight = 0;

		// Current position
		if ($this->left == ".") {
			$cX = $html->GetX();
		} else {
			$cX = $this->left;
			$html->SetX($cX);
		}
		// Current position (top)
		if ($this->top == ".") {
			$this->top = $html->GetY();
		} else {
			$html->SetY($this->top);
		}

		// Check the width if set to page wide OR set by xml to larger then page wide
		if (($this->width == 0) or ($this->width > $html->getRemainingWidth())) {
			$this->width = $html->getRemainingWidth();
		}
		// Setup the CellPadding
		if ($this->padding) {
			$cP = $html->cPadding;
		}

		// For padding, we have to use less wrap width
		$cW = $this->width - ($cP * 2);

		//-- calculate the text box height
		// Number of lines, will be converted to height
		$cHT = 0;
		// Element height (exept text)
		$eH = 0;
		// Footnote height (in points)
		$fH = 0;
		$w = 0;
		//-- $lw is an array
		// 0 => last line width
		// 1 => 1 if text was wrapped, 0 if text did not wrap
		// 2 => number of LF
		$lw = array();
		// Element counter
		$cE = count($this->elements);
		for ($i = 0; $i < $cE; $i++) {
			if (is_object($this->elements[$i])) {
				$ew = $this->elements[$i]->setWrapWidth($cW - $w - 2, $cW);
				if ($ew == $cW)
					$w = 0;
				$lw = $this->elements[$i]->getWidth($html);
				// Text is already gets the # LF
				$cHT += $lw[2];
				if ($lw[1] == 1) {
					$w = $lw[0];
				} elseif ($lw[1] == 2) {
					$w = 0;
				} else {
					$w += $lw[0];
				}
				if ($w > $cW) {
					$w = $lw[0];
				}
				// For anything else but text (images), get the height
				$eH += $this->elements[$i]->getHeight($html);
			} else {
				//if (is_string($element) and $element == "footnotetexts") $html->Footnotes();
				$fH += abs($html->getFootnotesHeight($cW));
			}
		}
		// Add up what's the final height
		$cH = $this->height;
		// If any element exist
		if ($cE > 0) {
			// Check if this is text or some other element, like images
			if ($eH == 0) {
				// Number of LF but at least one line
				$cHT = ($cHT + 1) * $html->cellHeightRatio;
				// Calculate the cell hight with the largest font size used
				$cHT = $cHT * $html->largestFontHeight;
				if ($cH < $cHT) {
					$cH = $cHT;
				}
			}
			// This is any other element
			else {
				if ($cH < $eH) {
					$cH = $eH;
				}
				// Add Footnote height to the rest of the height
				$cH += $fH;
			}
		}

		unset($lw, $cHT, $fH, $w);

		// Finaly, check the last cells height
		if ($html->lastCellHeight > $cH) {
			$cH = $html->lastCellHeight;
		}
		// Update max Y incase of a pagebreak
		// We don't want to over write any images or other stuff
		$html->addMaxY($this->top + $cH);

		// Start to print HTML
		echo "<div style=\"position:absolute; top:", $this->top, "pt; ";
		// LTR (left) or RTL (right)
		echo $html->alignRTL, ":", $cX, "pt; ";
		// Background color
		if ($this->fill) {
			if (!empty($this->bgcolor)) {
				echo " background-color:", $this->bgcolor, "; ";
			}
		}
		// Print padding only when it's set
		if ($this->padding) {
			// Use Cell around padding to support RTL also
			echo "padding:", $cP, "pt; ";
		}
		// Border setup
		if ($this->border) {
			echo " border:solid black 1pt; ";
			echo "width:", ($this->width - 1 - ($cP * 2)), "pt; height:", $cH - 1, "pt; ";
		} else {
			echo "width:", ($this->width - ($cP * 2)), "pt; height:", $cH, "pt; ";
		}
		echo "\">";

		// Do a little "margin" trick before print
		// to get the correct current position => "."
		$cXT = $html->GetX();
		$cYT = $html->GetY();
		$html->SetXY(0, 0);

		// Print the text elements
		foreach ($this->elements as $element) {
			if (is_object($element)) {
				$element->render($html, $cX, false);
			} elseif (is_string($element) && $element=="footnotetexts") {
				$html->Footnotes();
			} elseif (is_string($element) && $element == "addpage") {
				$html->AddPage();
			}
		}
		echo "</div>\n";

		// Reset "margins"
		$html->SetXY($cXT, $cYT);
		// This will be mostly used to trick the multiple images last height
		if ($this->reseth) {
			$cH = 0;
		}
		// New line and some clean up
		if (!$this->newline) {
			$html->SetXY($cX + $this->width, $this->top);
			$html->lastCellHeight = $cH;
		} else {
			$html->SetXY(0, $this->top + $cH + ($cP * 2));
			$html->lastCellHeight = 0;
		}
	}
}

/**
* Text element - HTML Report
*
* @package webtrees
* @subpackage Reports
* @todo add info
*/
class TextHTML extends Text {

	function TextHTML($style, $color) {
		parent::Text($style, $color);
	}

	/**
	* @todo temporary fix
	* @param ReportBaseHTML &$html
	* @param int $curx
	* @param boolean $attrib Is is called from a different element?
	*/
	function render(&$html, $curx=0, $attrib=true) {

		// Setup the style name
		if ($html->getCurrentStyle() != $this->styleName) {
			$html->setCurrentStyle($this->styleName);
		}
		$temptext = str_replace("#PAGENUM#", $html->PageNo(), $this->text);

		// If any text at all
		if (!empty($temptext)) {
			// If called by an other element
			if (!$attrib) {
				$html->write($temptext, $this->color);
			} else {
				// Save the start positions
				$startX = $html->GetX();
				$startY = $html->GetY();
				$width = $html->getRemainingWidth();
				// If text is wider then page width then wrap it
				if ($html->GetStringWidth($temptext) > $width) {
					$lines = explode("\n", $temptext);
					foreach ($lines as $line) {
						echo "<div style=\"position:absolute; top:", $startY, "pt; ", $html->alignRTL, ":", $startX, "pt; width:", $width, "pt;\">";
						$line = $html->textWrap($line, $width);
						$startY += $html->getTextCellHeight($line);
						$html->SetY($startY);
						$html->write($line, $this->color);
						echo "</div>\n";
					}
				} else {
					echo "<div style=\"position:absolute; top:", $startY, "pt; ", $html->alignRTL, ":", $startX, "pt; width:", $width, "pt;\">";
					$html->write($temptext, $this->color);
					echo "</div>\n";
					$html->SetX($startX + $html->GetStringWidth($temptext));
					if ($html->countLines($temptext) != 1) {
						$html->SetXY(0, ($startY + $html->getTextCellHeight($temptext)));
					}
				}
			}
		}
	}

	/**
	* Returns the height in points of the text element
	*
	* The height is already calculated in getWidth()
	* @param ReportBaseHTML &$html
	* @return float 0
	*/
	function getHeight(&$html) {
		$ct = substr_count($this->text, "\n");
		if ($ct>0) {
			$ct += 1;
		}
		$style = $html->getStyle($this->styleName);
		return ($style["size"] * $ct) * $html->cellHeightRatio;
		//return 0;
	}

	/**
	* Get the width of text and wrap it too
	* @param ReportBaseHTML &$html
	* @return array
	*/
	function getWidth(&$html) {
		// Setup the style name
		if ($html->getCurrentStyle() != $this->styleName) {
			$html->setCurrentStyle($this->styleName);
		}

		// Check for the largest font size in the box
		$fsize = $html->getCurrentStyleHeight();
		if ($fsize > $html->largestFontHeight) {
			$html->largestFontHeight = $fsize;
		}

		// Get the line width for the text in points
		$lw = $html->GetStringWidth($this->text);
		// Line Feed counter - Number of lines in the text
		$lfct = $html->countLines($this->text);
		// If there is still remaining wrap width...
		if ($this->wrapWidthRemaining > 0) {
			// Check with line counter too!
			if (($lw >= $this->wrapWidthRemaining) or ($lfct > 1)) {
				$newtext = "";
				$wrapWidthRemaining = $this->wrapWidthRemaining;
				$lines = explode("\n", $this->text);
				// Go throught the text line by line
				foreach ($lines as $line) {
					// Line width in points + a little margin
					$lw = $html->GetStringWidth($line);
					// If the line has to be wraped
					if ($lw > $wrapWidthRemaining) {
						$words = explode(" ", $line);
						$addspace = count($words);
						$lw = 0;
						foreach ($words as $word) {
							$addspace--;
							$lw += $html->GetStringWidth($word." ");
							if ($lw <= $wrapWidthRemaining) {
								$newtext .= $word;
								if ($addspace != 0) {
									$newtext .= " ";
								}
							} else {
								$lw = $html->GetStringWidth($word." ");
								$newtext .= "\n$word";
								if ($addspace != 0) {
									$newtext .= " ";
								}
								// Reset the wrap width to the cell width
								$wrapWidthRemaining = $this->wrapWidthCell;
							}
						}
					} else {
						$newtext .= $line;
					}
					// Check the Line Feed counter
					if ($lfct > 1) {
						// Add a new line feed as long as it's not the last line
						$newtext.= "\n";
						// Reset the line width
						$lw = 0;
						// Reset the wrap width to the cell width
						$wrapWidthRemaining = $this->wrapWidthCell;
					}
					$lfct--;
				}
				$this->text = $newtext;
				$lfct = substr_count($this->text, "\n");
				return array($lw, 1, $lfct);
			}
		}
		$l = 0;
		$lfct = substr_count($this->text, "\n");
		if ($lfct > 0) {
			$l = 2;
		}
		return array($lw, $l, $lfct);
	}
}

/**
* Footnote element
*
* @package webtrees
* @subpackage Reports
* @todo add info
*/
class FootnoteHTML extends Footnote {

	function FootnoteHTML($style="") {
		parent::Footnote($style);
	}

	/**
	* HTML Footnotes number renderer
	* @param ReportBaseHTML &$html
	*/
	function render(&$html) {
		$html->setCurrentStyle("footnotenum");
		// 'vertical-align:super' is not working properly in IE 8
		// @deprecated
		echo "<a href=\"#footnote", $this->num, "\"><sup>";
		// working
		$html->write($html->entityRTL. $this->num);
		echo "</sup></a>\n";
	}

	/**
	* Write the Footnote text
	* Uses style name "footnote" by default
	*
	* @param ReportBaseHTML &$html
	*/
	function renderFootnote(&$html) {

		if ($html->getCurrentStyle() != $this->styleName) {
			$html->setCurrentStyle($this->styleName);
		}

		$temptext = str_replace("#PAGENUM#", $html->PageNo(), $this->text);
		echo "\n<div><a name=\"footnote", $this->num, "\"></a>";
		$html->write($this->num. ". ". $temptext);
		echo "</div>";

		$html->SetXY(0, $html->GetY() + $this->getFootnoteHeight($html));
	}

	/**
	* Calculates the Footnotes height
	*
	* @param ReportBaseHTML &$html
	* @param $cellWidth The width of the cell to use it for text wraping
	* @return Footnote height in points
	*/
	function getFootnoteHeight(&$html, $cellWidth=0) {
		if ($html->getCurrentStyle() != $this->styleName) {
			$html->setCurrentStyle($this->styleName);
		}

		if ($cellWidth > 0) {
			$this->text = $html->textWrap($this->text, $cellWidth);
		}
		$this->text = $this->text. "\n\n";
		$ct = substr_count($this->text, "\n");
		$fsize = $html->getCurrentStyleHeight();
		return ($fsize * $ct) * $html->cellHeightRatio;
	}

	/**
	* Get the width of text
	* Breaks up a text into lines if needed
	*
	* @param ReportBaseHTML &$html
	* @return array
	*/
	function getWidth(&$html) {
		// Setup the style name
		$html->setCurrentStyle("footnotenum");

		// Check for the largest font size in the box
		$fsize = $html->getCurrentStyleHeight();
		if ($fsize > $html->largestFontHeight) {
			$html->largestFontHeight = $fsize;
		}

		// Returns the Object if already numbered else false
		if (empty($this->num)) {
			$html->checkFootnote($this);
		}

		// Get the line width for the text in points + a little margin
		$lw = $html->GetStringWidth($this->numText);
		// Line Feed counter - Number of lines in the text
		$lfct = $html->countLines($this->numText);
		// If there is still remaining wrap width...
		if ($this->wrapWidthRemaining > 0) {
			// Check with line counter too!
			if (($lw >= $this->wrapWidthRemaining) or ($lfct > 1)) {
				$newtext = "";
				$wrapWidthRemaining = $this->wrapWidthRemaining;
				$lines = explode("\n", $this->numText);
				// Go throught the text line by line
				foreach ($lines as $line) {
					// Line width in points + a little margin
					$lw = $html->GetStringWidth($line);
					// If the line has to be wraped
					if ($lw > $wrapWidthRemaining) {
						$words = explode(" ", $line);
						$addspace = count($words);
						$lw = 0;
						foreach ($words as $word) {
							$addspace--;
							$lw += $html->GetStringWidth($word." ");
							if ($lw <= $wrapWidthRemaining) {
								$newtext .= $word;
								if ($addspace != 0) {
									$newtext .= " ";
								}
							} else {
								$lw = $html->GetStringWidth($word." ");
								$newtext .= "\n$word";
								if ($addspace != 0) {
									$newtext .= " ";
								}
								// Reset the wrap width to the cell width
								$wrapWidthRemaining = $this->wrapWidthCell;
							}
						}
					} else {
						$newtext .= $line;
					}
					// Check the Line Feed counter
					if ($lfct > 1) {
						// Add a new line feed as long as it's not the last line
						$newtext.= "\n";
						// Reset the line width
						$lw = 0;
						// Reset the wrap width to the cell width
						$wrapWidthRemaining = $this->wrapWidthCell;
					}
					$lfct--;
				}
				$this->numText = $newtext;
				$lfct = substr_count($this->numText, "\n");
				return array($lw, 1, $lfct);
			}
		}
		$l = 0;
		$lfct = substr_count($this->numText, "\n");
		if ($lfct > 0) {
			$l = 2;
		}
		return array($lw, $l, $lfct);
	}
}

/**
* PageHeader element
*
* @package webtrees
* @subpackage Reports
* @todo add info
*/
class PageHeaderHTML extends PageHeader {
	function PageHeaderHTML() {
		parent::PageHeader();
	}

	function render(&$html) {
		$html->clearPageHeader();
		foreach ($this->elements as $element) {
			$html->addPageHeader($element);
		}
	}
}

/**
* Image element
*
* @package webtrees
* @subpackage Reports
* @todo add info
*/
class ImageHTML extends Image {

	function ImageHTML($file, $x, $y, $w, $h, $align, $ln) {
		parent::Image($file, $x, $y, $w, $h, $align, $ln);
	}

	/**
	* Image renderer
	* @param ReportBaseHTML &$html
	*/
	function render(&$html) {
		global $lastpicbottom, $lastpicpage, $lastpicleft, $lastpicright;

		// Get the current positions
		if ($this->x == ".") {
			$this->x=$html->GetX();
		}
		if ($this->y == ".") {
			//-- first check for a collision with the last picture
			if (isset($lastpicbottom)) {
				if (($html->PageNo() == $lastpicpage) && ($lastpicbottom >= $html->GetY()) && ($this->x >= $lastpicleft) && ($this->x <= $lastpicright)) {
					$html->SetY($lastpicbottom + ($html->cPadding * 2));
				}
			}
			$this->y = $html->GetY();
		}

		// Image alignment
		switch($this->align) {
			case "L":
				echo "<div style=\"position:absolute; top:", $this->y, "pt; left:0pt; width:", $html->getRemainingWidth(), "pt; text-align:left;\">\n";
				echo "<img src=\"", $this->file, "\" style=\"width:", $this->width, "pt; height:", $this->height, "pt;\" alt=\"\" />\n</div>\n";
				break;
			case "C":
				echo "<div style=\"position:absolute; top:", $this->y, "pt; left:0pt; width:", $html->getRemainingWidth(), "pt; text-align:center;\">\n";
				echo "<img src=\"", $this->file, "\" style=\"width:", $this->width, "pt; height:", $this->height, "pt;\" alt=\"\" />\n</div>\n";
				break;
			case "R":
				echo "<div style=\"position:absolute; top:", $this->y, "pt; left:0pt; width:", $html->getRemainingWidth(), "pt; text-align:right;\">\n";
				echo "<img src=\"", $this->file, "\" style=\"width:", $this->width, "pt; height:", $this->height, "pt;\" alt=\"\" />\n</div>\n";
				break;
			default:
				echo "<img src=\"", $this->file, "\" style=\"position:absolute; ", $html->alignRTL, ":", $this->x, "pt; top:", $this->y, "pt; width:", $this->width, "pt; height:", $this->height, "pt;\" alt=\"\" />\n";
		}

		$lastpicpage = $html->PageNo();
		$lastpicleft = $this->x;
		$lastpicright = $this->x + $this->width;
		$lastpicbottom = $this->y + $this->height;
		// Setup for the next line
		if ($this->line == "N") {
			$html->SetY($lastpicbottom);
		}
		// Keep max Y updated
		$html->addMaxY($lastpicbottom);
	}

	/**
	* Get the image height
	* This would be called from the TextBox only for multiple images
	* so we add a bit bottom space between the images
	* @param ReportBaseHTML &$html
	* @return float
	*/
	function getHeight(&$html) {
		return $this->height + ($html->cPadding * 2);
	}

} //-- END Image

/**
* Line element - HTML Report
*
* @package webtrees
* @subpackage Reports
* @todo add info
*/
class LineHTML extends Line {
	/**
	* Create a line class -HTML
	* @param mixed $x1
	* @param mixed $y1
	* @param mixed $x2
	* @param mixed $y2
	*/
	function LineHTML($x1, $y1, $x2, $y2) {
		parent::Line($x1, $y1, $x2, $y2);
	}

	/**
	* HTML line renderer
	* @param ReportBaseHTML &$html
	*/
	function render(&$html) {
		if ($this->x1==".") $this->x1=$html->GetX();
		if ($this->y1==".") $this->y1=$html->GetY();
		if ($this->x2==".") {
			$this->x2 = $html->getRemainingWidth();
		}
		if ($this->y2==".") $this->y2=$html->GetY();
		// TODO Non verticle or horizontal lines can use a series of divs absolutely positioned
		// Vertical line
		if ($this->x1 == $this->x2) {
			echo "<div style=\"position:absolute; overflow:hidden; border-", $html->alignRTL, ":solid black 1pt; ", $html->alignRTL, ":", $this->x1, "pt; top:", $this->y1 + 1, "pt; width:1pt; height:", $this->y2 - $this->y1, "pt;\"> </div>\n";
		}
		// Horizontal line
		if ($this->y1 == $this->y2) {
			echo "<div style=\"position:absolute; overflow:hidden; border-top:solid black 1pt; ", $html->alignRTL, ":", $this->x1, "pt; top:", $this->y1 + 1, "pt; width:", $this->x2 - $this->x1, "pt; height:1pt;\"> </div>\n";
		}
		// Keep max Y updated
		// One or the other will be higher... lasy mans way...
		$html->addMaxY($this->y1);
		$html->addMaxY($this->y2);
	}
} //-- END Line
