<?php
/**
* @version	$Id: pdf_table.php 14244 2011-03-16 20:53:41Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class kPDFTableRow extends kPDFElement {

	function LayoutChildren()
	{
		parent::LayoutChildren();

		// http://manual.prod.intechnic.lv/css21/tables.html#height-layout

		// vertical-alignment
		$row_height = $this->GetCSSProperty('height');
		if ($row_height == 'auto') {
			$row_height = 0;
		}
		$baseline = 0;
		foreach ($this->Children as $elem) {
			if ($elem->GetCSSProperty('vertical-align') == 'baseline') {
//				$ascent = $elem->FindChild('_LINE_')->Ascent;
				$ascent = $elem->FirstChild->Ascent;
				if ($ascent > $baseline) {
					$baseline = $ascent;
				}
			}
			$dim = $elem->GetBoxDimensions();
			$elem_height = $dim[1];
			if ($elem_height > $row_height) {
				$row_height = $elem_height;
			}
		}

		foreach ($this->Children as $elem) {
			$vertical_align = $elem->GetCSSProperty('vertical-align');
			$dim = $elem->GetBoxDimensions();
			$elem_height = $dim[1];
			$add_top = 0;
			$add_bottom = 0;
			if ($vertical_align == 'top') {
				$add_bottom = $row_height - $elem_height;
			}
			elseif ($vertical_align == 'middle') {
				$add_top = ($row_height - $elem_height)/2;
				$add_bottom = $add_top;
			}
			elseif ($vertical_align == 'bottom') {
				$add_top = $row_height - $elem_height;
			}
			else { // baseline and all other
				if ($elem->Ascent < $baseline) {
					$add_top = $baseline - $elem->Ascent;
				}
				$add_bottom = $row_height - $elem_height - $add_top;
			}

			$elem->SetCSSProperty('padding-top', $elem->GetCSSProperty('padding-top') + $add_top);
			$elem->SetCSSProperty('padding-bottom', $elem->GetCSSProperty('padding-bottom') + $add_bottom);
		}
	}

}

class kPDFTable extends kPDFElement {
	public $InitialMode = null;

	public $Table = array();
	public $TableBuild = false;
	public $CurCol = 0;
	public $CurRow = 0;

	public $ColWidths = array();

	function __construct($node, $helper)
	{
		parent::__construct($node, $helper);
	}

	function Init()
	{
		parent::Init();
		$this->InitialMode = $this->Helper->DimensionsMode;
		$this->Helper->DimensionsMode = kPDFHelper::DM_SKIP;
	}

	function Closed()
	{
		$this->BuildTable($this);
		parent::Closed();
		/*$this->Helper->DimensionsMode = kPDFHelper::DM_NORMAL ;
//		$this->LayoutChildren();
		$this->Helper->DimensionsMode = $this->InitialMode;*/
	}

	function ComputeWidthAndMargins()
	{
		if (!$this->TableBuild || !$this->GetContainingBlock()->WidthComputed || $this->WidthComputed) return ;
		$this->ComputeCSSProperty('WIDTH', $this->CSSSpecifiedProperties['WIDTH']);

		if ($this->GetCSSProperty('table-layout') == 'fixed') {
			$this->CalculateWidthsFixed();
		}
		if ($this->GetCSSProperty('table-layout') == 'auto') {
			$this->CalculateWidthsAuto(); //CalculateWidthsAuto();
		}
		$this->WidthComputed = true;
	}

	function CalculateWidthsFixed()
	{
		if (count($this->Table) == 0) return ;

		$specified_width = 0;
		$specified_count = 0;
		$i = 0;
		foreach ($this->Table[1] as $cell) {
			$i++;
			$cur_w = $cell->GetCSSProperty('width');
			$colspan = isset($cell->Node->Attributes['COLSPAN']) ? $cell->Node->Attributes['COLSPAN'] : 1;
			if ($cur_w != 'auto') {
				$specified_width += $cur_w;
				$specified_count += $colspan;
				$cur_w = $cur_w/$colspan;
			}
			for ($col=$i; $col < $i+$colspan; $col++) {
				$this->ColWidths[$col] = $cur_w;
			}
			$i += $colspan-1;
		}
		$max_cols = $i;
		$remaining_w = $this->GetCSSProperty('width') - $specified_width;
		$auto_width = $remaining_w/($max_cols-$specified_count);
		foreach ($this->ColWidths as $i => $width) {
			if ($width == 'auto') {
				$this->ColWidths[$i] = $auto_width;
			}
		}

		$this->SetColWidths($max_cols);
	}

	function SetColWidths($max_cols)
	{
		foreach ($this->Table as $row) {
			$i = 0;
			foreach ($row as $cell) {
				$i++;
				if ($i > $max_cols) {
					break;
				}
				$cell_extra_width =
					$cell->GetCSSProperty('border-left-width') +
					$cell->GetCSSProperty('padding-left') +
					$cell->GetCSSProperty('padding-right') +
					$cell->GetCSSProperty('border-right-width');
				if (isset($cell->Node->Attributes['COLSPAN'])) {
					$colspan = $cell->Node->Attributes['COLSPAN'];
					$z = $i;
					$width = 0;
					do {
						$width += $this->ColWidths[$z];
					} while ($z++ < $i+$colspan-1);
//					$width = array_sum( array_slice($this->ColWidths, $i-1, $colspan) );
					$i += $colspan-1;
				}
				else {
					$width = $this->ColWidths[$i];
				}
				$cell->SetCSSProperty('width', $width - $cell_extra_width);
				$cell->WidthComputed = true;
			}
		}
		$this->SetCSSProperty('width', array_sum($this->ColWidths));
	}

	function BuildTable($node)
	{
		$this->ColWidths = array();
		foreach ($node->Children as $child) {
			if ($child->GetCSSProperty('display') == 'table-row') {
				$this->CurRow++;
				$this->CurCol = 0;
			}
			if ($child->GetCSSProperty('display') == 'table-cell') {
				$this->CurCol++;
				$this->Table[$this->CurRow][$this->CurCol] = $child;
				if (isset($child->Node->Attributes['COLSPAN'])) {
					$this->CurCol += $child->Node->Attributes['COLSPAN']-1;
				}
			}
			else { // otherwise it will find all nested table cells
				$this->BuildTable($child);
			}
		}
		$this->TableBuild = true;
	}

	function CalculateWidthsAuto()
	{
		if (count($this->Table) == 0) return ;

		$min_col_widths = array();
		$max_col_widths = array();
		$specified_widths = array();
		$percent_widths = array();

		$row = 0;
		$max_cols = 0;
		foreach ($this->Table as $row)
		{
			$row++;
			$col = 0;
			foreach ($row as $cell)	{
				$col++;
				$cell_w = $cell->GetCSSProperty('width');
				$percent_w = false;
				if (preg_match('/([.0-9]+)%/', $cell_w, $regs)) {
					$percent_w = $regs[1];
				}
				$min_cw = $cell_w == 'auto' && !$percent_w ? $cell->MinContentWidth : max($cell_w, $cell->MinContentWidth);
				$max_cw = $cell->MaxContentWidth;

				if ($cell_w != 'auto' && is_numeric($cell_w)) {
					if ($cell_w < $min_cw) {
						$cell_w = $min_cw;
					}
					if (!isset($specified_widths[$col]) || $cell_w > $specified_widths[$col]) {
						$specified_widths[$col] = $cell_w;
					}
				}

				$colspan = isset($cell->Node->Attributes['COLSPAN']) ? $cell->Node->Attributes['COLSPAN'] : 1;
				if ($colspan == 1) {
					if (!isset($min_col_widths[$col]) || $min_cw > $min_col_widths[$col]) {
						$min_col_widths[$col] = $min_cw;
					}
					if (!isset($max_col_widths[$col]) || $max_cw > $max_col_widths[$col]) {
						$max_col_widths[$col] = $max_cw;
					}
					if ($percent_w && (!isset($percent_widths[$col]) || $percent_w > $percent_widths[$col])) {
						$percent_widths[$col] = $percent_w;
					}
				}
				else {
					$spans_min = array_slice($min_col_widths, $col-1, $colspan);
					$spans_max = array_slice($max_col_widths, $col-1, $colspan);
					$spans_percent = array_slice($percent_widths, $col-1, $colspan);

					$min_diff = ($min_cw - array_sum($spans_min));
					if ($min_diff > 0) {
						for($i=$col; $i < $col+$colspan; $i++) {
							if (!isset($min_col_widths[$i])) $min_col_widths[$i] = 0;
							$min_col_widths[$i] += $min_diff/$colspan;
						}
					}

					$max_diff = ($max_cw - array_sum($spans_max));
					if ($max_diff > 0) {
						for($i=$col; $i < $col+$colspan; $i++) {
							if (!isset($max_col_widths[$i])) $max_col_widths[$i] = 0;
							$max_col_widths[$i] += $max_diff/$colspan;
						}
					}

					$percent_diff = ($percent_w - array_sum($spans_percent));
					if ($percent_diff > 0) {
						for($i=$col; $i < $col+$colspan; $i++) {
							$percent_widths[$i] += $percent_diff/$colspan;
						}
					}

					$col += $colspan-1;
				}
			}
			if ($col > $max_cols) {
				$max_cols = $col;
			}
		}
		$table_min_width = array_sum($min_col_widths);
		$table_max_width = array_sum($max_col_widths) - $this->GetCSSProperty('border-left-width') - $this->GetCSSProperty('border-right-width');
		$table_specifid_width = array_sum($specified_widths);

		$cb_width = $this->GetContainingBlock()->GetCSSProperty('width');
		$computed_width = $this->ComputeCSSProperty('WIDTH', $this->GetCSSProperty('width'));
		if ($computed_width == 'auto') {
			$computed_width = $cb_width;
		}
		else {
			$computed_width -= $this->GetCSSProperty('border-left-width') + $this->GetCSSProperty('border-right-width');
		}
		if ($this->CSSSpecifiedProperties['WIDTH'] == 'auto' && $table_max_width < $computed_width) {
			$width = $table_max_width;
			$this->ColWidths = $max_col_widths;
		}
		else {
			$width = max($table_min_width, $computed_width);

			// try to satisfy percent widths
			$reset_to_min = false;
			$percent_sum = 0;
			for ($i=1; $i<=$max_cols; $i++) {
				if (isset($percent_widths[$i])) {
					$adjusted_percent = $reset_to_min ? 0 : min($percent_widths[$i], 100-$percent_sum);
					$percent_sum += $adjusted_percent;
					if ($adjusted_percent > 0) {
						$min_col_widths[$i] = $width*$adjusted_percent/100;
					}
				}
				if ($percent_sum >= 100) {
					$reset_to_min = true;
				}
			}
			$distribute = $width - array_sum($min_col_widths); // recalc min width here, because of % changes

			$total_max = 0;
			for ($i=1; $i<=$max_cols; $i++) {
				if (isset($specified_widths[$i])) {
					$this->ColWidths[$i] = $specified_widths[$i];
//					$distribute -= $specified_widths[$i];
					continue;
				}
				else {
					$total_max += $max_col_widths[$i];
				}
			}

			for ($i=1; $i<=$max_cols; $i++) {
				if (isset($this->ColWidths[$i])) {
					continue;
				}
				$coeff = isset($percent_widths[$i]) ? $percent_widths[$i] / $percent_sum : $max_col_widths[$i]/$total_max;
				$add = $coeff * $distribute;
				$this->ColWidths[$i] = $min_col_widths[$i] + $add;
			}
		}

		$this->SetColWidths($max_cols);
	}
}