PEAR_Frontend_Gtk
[ class tree: PEAR_Frontend_Gtk ] [ index: PEAR_Frontend_Gtk ] [ all elements ]

Source for file WidgetHTML.php

Documentation is available at WidgetHTML.php

  1. <?php
  2.  
  3. /*
  4.   +----------------------------------------------------------------------+
  5.   | PHP Version 4                                                        |
  6.   +----------------------------------------------------------------------+
  7.   | Copyright (c) 1997-2003 The PHP Group                                |
  8.   +----------------------------------------------------------------------+
  9.   | This source file is subject to version 2.02 of the PHP license,      |
  10.   | that is bundled with this package in the file LICENSE, and is        |
  11.   | available at through the world-wide-web at                           |
  12.   | http://www.php.net/license/2_02.txt.                                 |
  13.   | If you did not receive a copy of the PHP license and are unable to   |
  14.   | obtain it through the world-wide-web, please send a note to          |
  15.   | license@php.net so we can mail you a copy immediately.               |
  16.   +----------------------------------------------------------------------+
  17.   | Author: Alan Knowles <alan@akbkhome.com>                             |
  18.   +----------------------------------------------------------------------+
  19.  
  20.   $Id: WidgetHTML.php,v 1.21 2003/01/04 11:55:55 mj Exp $
  21. */
  22.  
  23. /**
  24.  * WidgetHTML - the HTML rendering widget
  25.  *
  26.  * Displays HTML/web pages
  27.  *
  28.  * @author Alan Knowles <alan@akbkhome.com>
  29.  */
  30.  
  31. /*
  32.  
  33. Notes about this:
  34.  
  35. 1. why is this all in one big class?
  36.  - well, this is the second effort at making a web browser in pure php,
  37.  the first effort was multiclassed, however it proved impossible to solve
  38.  all the fundimental issues while messing around with all those files/references
  39.  etc.
  40.  
  41.  As this class grows (and all those issues are worked out) it probably will
  42.  be broken apart
  43.  
  44. 2. How does it work.
  45. Rendering:
  46.  - First Load the data $this->loadURL($url); , stores it in $_source
  47.  - Second tokenize the html
  48.     tags become an array($tag, $attributes_string)
  49.     raw text is just  a string in the token array. - similar to the php tokenizer.
  50.     # TODO - this is a down and dirty tokenizer - either write it in C or break it into
  51.     it's own class. - which does arrays of attributes as well..
  52.  
  53.  - Read the HTML and generate 3 key bits of data...
  54.     $this->table = tables and td tags, the body is one big table (table[0])
  55.     $this->_lines[], an array of line data (each bit of text is assigned a line, lines
  56.         contain information like height and default left/right
  57.     $this->_textParts[] an associative array of details about a particular item of text
  58.          to be drawn after processing. - like font,color names etc.
  59.     current state is stored in the stack / simple push/pop array for each item, and certain
  60.         attributes, $this->in returns the attributes, $this->inID returns the token pos.
  61.  
  62.  
  63.  - Recalculate the heights (post process)
  64.    - goest through the table/lines and recaculates each lines real y location.
  65.    - recalcs td heights etc.
  66.  
  67.  - Render objects - tables, td's then textparts..
  68.  
  69. User Interaction:
  70.   - links are done with mouse motion detection and the $_links array.
  71.   - page resize is based around the expose event, and testing the layout allocation.
  72.     It does a delayed 1/2 second check to see if you are still resizing..
  73.   - cursor changes: timer etc. in $this->_cursors[]
  74.  
  75.  
  76. Well, if in doubt email me and I'll add more notes...
  77.  
  78.  
  79. ---------------------- USAGE EXAMPLE ------------------------------
  80.  
  81. include('PEAR/Frontend/Gtk/WidgetHTML.php');
  82. dl('php_gtk.dll');
  83. error_reporting(E_ALL);
  84.  
  85. $window = &new GtkWindow();
  86. $window->set_name('Test Input');
  87. $window->set_position(GTK_WIN_POS_CENTER);
  88. $window->set_usize(600,400);
  89. $window->connect_object('destroy', array('gtk', 'main_quit'));
  90. $vbox = &new GtkVBox();
  91. $window->add($vbox);
  92. $vbox->show();
  93.  
  94. $t = new PEAR_Frontend_Gtk_WidgetHTML;
  95.  //$t->test(dirname(__FILE__).'/tests/test3.html');
  96.  
  97. $t->loadURL("http://www.php.net");
  98. $t->tokenize();
  99. $t->Interface();
  100. $vbox->pack_start($t->widget);
  101.  
  102. $button = &new GtkButton('Quit');
  103. $vbox->pack_start($button, false, false);
  104. $button->connect_object('clicked', array($window, 'destroy'));
  105. $button->show();
  106.  
  107. $window->show();
  108.  
  109. gtk::main();
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121. */
  122.  
  123.  
  124.  
  125. class PEAR_Frontend_Gtk_WidgetHTML {
  126.     var $widget// what you add to your interface (as it's too complex to extend Scrolled window)
  127.     var $_pixmap_area_x =1024;
  128.     var $_pixmap_area_y = 5000;
  129.     var $_source// raw HTML
  130.     var $_URL// the URL
  131.     function loadURL($URL// load a file into source - for testing only
  132.         //echo "OPENING URL $URL\n";
  133.         $this->_URL trim($URL);
  134.         $this->_URLparse parse_url(trim($URL));
  135.         $this->_source @str_replace("\r"" ",implode('',file(trim($URL))));
  136.         if ($this->_sourcereturn TRUE;
  137.         //$fh = fopen('/tmp/test','w'); fwrite($fh,$this->_source ); fclose($fh);
  138.     }
  139.     function loadTEXT($text{
  140.         $this->_source $text;
  141.     }
  142.     
  143.     var $_tokens = array()// HTML Tokens id => array($tag,$attribute_string) | $string
  144.     function tokenize(// tokenize the HTML into $this->tokens
  145.         //echo "TOKENIZING\n";
  146.         $tok strtok($this->_source,'<');
  147.         $a[0$tok;
  148.         while$tok !== FALSE {
  149.             $tok strtok('<');
  150.             $a[$tok;
  151.         }
  152.         $in_comment '';
  153.         $this->_tokens = array();
  154.         foreach($a as $i=>$b{
  155.             if (trim($b=== ''continue;
  156.  
  157.             if ((substr($b,0,3== '!--'&& !preg_match('/-->/m',$b)) {
  158.                 $in_comment $b;
  159.                 continue;
  160.             }
  161.             if ($in_comment)
  162.                 if (preg_match("/\-\-\>/m",$b)) {
  163.                     $tmp explode('-->',$b);
  164.                     $this->_tokens[= array('<!--',$in_comment.$tmp[0]);
  165.                     $this->_tokens[$tmp[1];
  166.                     $in_comment '';
  167.                     continue;
  168.                 else {
  169.                     $in_comment .= $b;
  170.                     continue;
  171.                 }
  172.  
  173.             $l strlen($b)-1;
  174.             if ($b{$l== '>'{
  175.                 if (($s strcspn($b," \n")) == strlen($b)) {
  176.                     $this->_tokens[= array(strtoupper(substr($b,0,-1)));
  177.                     continue;
  178.                 }
  179.                 $tag strtoupper(substr($b,0,strcspn($b," \n")));
  180.                 $attribs substr($b,strcspn($b," \n"),-1);
  181.                 $this->_tokens[= array($tag,trim($attribs));
  182.                 continue;
  183.             }
  184.             if (strcspn($b," \n"== $l+1{
  185.                 $this->_tokens[= array(strtoupper(substr($b,0,strpos($b,'>'))));
  186.                 $this->_tokens[substr($b,strpos($b,'>')+1;
  187.                 continue;
  188.             }
  189.             if (strcspn($b," \n"strpos($b,'>')) {
  190.                 $tag substr($b,0,strpos($b,'>'));
  191.                 $this->_tokens[= array(strtoupper($tag));
  192.                 $this->_tokens[substr($b,strpos($b,'>')+1);
  193.                 continue;
  194.             }
  195.             $tag strtoupper(substr($b,0,strcspn($b," \n")));
  196.             $attribs substr($b,strcspn($b," \n")+1,strpos($b,'>')-strlen($tag)-1);
  197.             $this->_tokens[= array($tag,$attribs);
  198.             $this->_tokens[substr($b,strpos($b,'>')+1);
  199.         }
  200.         /*
  201.         ob_start();
  202.         print_r($this->_tokens);
  203.         $test = ob_get_contents();
  204.         ob_end_clean();
  205.         $fh = fopen('/tmp/tokens', 'w'); fwrite($fh,$test); fclose($fh);
  206.         */
  207.         //exit;
  208.     }
  209.     var $Start = FALSE; // start rering (eg. ignore headers)
  210.     var $_Building=FALSE;
  211.     function build(// read the tokens and build the line/table arrays - then display
  212.         if($this->Buildingreturn;
  213.         if(!$this->Realizedreturn;
  214.         if (!$this->_tokensreturn;
  215.          
  216.         $this->_Building=TRUE;
  217.           $this->Start = FALSE;
  218.         // reset all major variables;
  219.         $this->_line_y = array();
  220.         $this->stack = array();
  221.         $this->cur = array();
  222.         $this->curid = array();
  223.         $this->_links = array();
  224.         $this->tables = array();
  225.         $this->td = array();
  226.         $this->_lines = array();
  227.         $this->_line =0;
  228.         $this->_textParts = array();
  229.  
  230.         // make a fake first line
  231.  
  232.         $this->_makeFont();
  233.         $this->_lines[0]['top'=0;
  234.         $this->_lines[0]['ascent'=0;
  235.         $this->_lines[0]['descent'=0;
  236.         $this->_updateLine('','START');
  237.         $this->_lines[$this->_line]['bottom'=0;
  238.         $this->_lines[0]['left'= 0;
  239.         $this->_lines[0]['right'$this->_area_x;
  240.         $this->tables[0]['left'= 0;
  241.         $this->tables[0]['right'$this->_area_x;
  242.         $this->tables[0]['top'= 0;
  243.         $this->tables[0]['line'= 0;
  244.  
  245.         $this->tables[0]['table'][1][1]['span'= 1;
  246.         $this->tables[0]['table'][1][1]['rowspan'= 1;
  247.         $this->tables[0]['table'][1][1]['width'$this->_area_x;
  248.         $this->td[0]['tag''BODY';
  249.         $this->td[0]['row'= 1;
  250.         $this->td[0]['col'= 1;
  251.         $this->td[0]['colspan'= 1;
  252.         $this->td[0]['rowspan'= 1;
  253.         $this->td[0]['table'= 0;
  254.         $this->td[0]['colwidth'= 1;
  255.         $this->td[0]['left'= 0;
  256.         $this->td[0]['right'$this->_area_x;
  257.         $this->td[0]['totalcols'= 1;
  258.         $this->td[0]['lines'= array();
  259.         $this->td[0]['line_items'= array();
  260.  
  261.         $this->_makeColors('#000000','#FFFFFF');
  262.         $this->td[0]['gc'=   '#FFFFFF'// background
  263.         $this->td[0]['bggc'=  '#000000';
  264.         $this->tables[0]['gc'=   '#FFFFFF'// background
  265.         $this->tables[0]['bggc'=  '#000000';
  266.  
  267.         $this->td[0]['height'= 0;
  268.         $this->td[0]['top'= 0;
  269.         $this->td[0]['bottom'= 0;
  270.         $this->tables[0]['table'][1][1]['td'&$this->td[0];
  271.         $this->tables[0]['cells'= array(0);
  272.         $this->_nextLine("BEGIN");
  273.         $endpos count($this->_tokens);
  274.         for($pos = 0;$pos $endpos;$pos++{
  275.             while(gtk::events_pending()) gtk::main_iteration();
  276.             $item $this->_tokens[$pos];
  277.             if (is_Array($item)) {
  278.                 $method "push";
  279.                 if (!$item[0]continue;
  280.                 //echo $pos;
  281.                 //echo "\nIN:".serialize($item)."\n";
  282.                 //$this->outputTAG($item[0]);
  283.                 if ($item[0]{0== '/'{
  284.                     $method "pop";
  285.                     $item[0substr($item[0],1);
  286.                     $item[1'/';
  287.                 }
  288.                 //if (!$draw)
  289.  
  290.  
  291.                 switch (trim($item[0])) {
  292.                     case '!DOCTYPE':
  293.                     case 'HTML':
  294.                     case 'META':
  295.                     case 'LINK':
  296.                     case 'HEAD':
  297.                     case 'SCRIPT':
  298.                     case 'STYLE':
  299.                     case 'TITLE':
  300.  
  301.                     case 'FORM':
  302.  
  303.                     case 'INPUT':
  304.                     case 'OPTION':
  305.                     case 'TBODY':
  306.  
  307.                     case 'IMG':
  308.  
  309.                     case '!--':
  310.                         break;
  311.  
  312.                     case 'UL':
  313.                     case 'DL':
  314.                         if ($method ==  'push'{
  315.                             $this->_nextLine('UL')// clear old line...
  316.                             $this->_TDaddLine('UL');
  317.                             $this->_lines[$this->_line]['indent'= 20;
  318.                         else {
  319.                             $this->_nextLine('UL')// clear old line...
  320.                             $this->_TDaddLine('UL');
  321.                             $this->_lines[$this->_line]['indent'= 0;
  322.                             $this->_nextLine('UL2')// clear old line...
  323.                             $this->_updateLine('','UL2');
  324.                             $this->_TDaddLine('UL');
  325.                             $this->_lines[$this->_line]['indent'= 0;
  326.  
  327.                         }
  328.                         break;
  329.  
  330.  
  331.  
  332.                     case 'TABLE':               // unhandled stauff
  333.                         if ($method == 'push'// start
  334.                             // move us down abit and start a new row
  335.                             $this->_nextLine('CLEARPRETABLE')// clear old line...
  336.                             $this->_TDaddLine('TABLE start')// add it to the table?
  337.                             $this->tables[$pos]['from last']   serialize($this->_lines[$this->_line]);
  338.  
  339.                             $this->tables[$pos]['pos'$pos;
  340.                             $this->tables[$pos]['left']  =  $this->_lines[$this->_line]['left'];
  341.                             $this->tables[$pos]['right'=  $this->_lines[$this->_line]['right'];
  342.                             $this->tables[$pos]['startright'=  $this->_lines[$this->_line]['right'];
  343.                             $this->tables[$pos]['top']   $this->_lines[$this->_line]['top'];
  344.                             $this->_nextLine('TABLE')// new line object that contains the table information.
  345.                             $this->_TDaddLine('TABLE');
  346.                             $this->_lines[$this->_line]['top'$this->tables[$pos]['top'];
  347.  
  348.  
  349.                             $this->tables[$pos]['line'$this->_line;
  350.  
  351.                             $this->_lines[$this->_line]['table'$pos;
  352.                             $this->_lines[$this->_line]['top'$this->tables[$pos]['top'];
  353.                             /*
  354.                             if ($id = $this->inID('TD')) {
  355.                                 $this->td[$id]['lines'][] = $this->_line;
  356.                                 $this->td[$id]['line_items'][$this->_line] = &$this->_lines[$this->_line];
  357.                             }
  358.                             */
  359.                             //$this->_updateLine("TABLE:{$pos}");// new line that is going to be the content.
  360.  
  361.  
  362.  
  363.                             $this->_TABLEcalc($pos);
  364.  
  365.                             $this->push($item[0],$pos,@$item[1]);
  366.                             //$this->output("TABLE:$pos");
  367.                             $this->_makeColors();
  368.                             $this->tables[$pos]['gc'$this->_gc;
  369.                             $this->tables[$pos]['bggc'=$this->_bggc;
  370.  
  371.                         else {  //
  372.  
  373.  
  374.  
  375.                             $table $this->pop('TABLE',$pos);
  376.  
  377.  
  378.                             $this->_TABLErecalc($table,$pos);
  379.  
  380.  
  381.  
  382.                             // update the container line
  383.                             $line $this->tables[$table]['line'];
  384.                             $this->_lines[$line]['top'$this->tables[$table]['top'];
  385.                             $this->_lines[$line]['descent'$this->tables[$table]['height'];
  386.                             $this->_lines[$line]['ascent'= 0;
  387.                             $this->_lines[$line]['height'$this->tables[$table]['height'];
  388.                             $this->_lines[$line]['bottom'$this->tables[$table]['bottom'];
  389.  
  390.                             //$this->_updateLine();
  391.                             $this->_nextLine('ENDTABLE - clear')// start a new line
  392.                             $this->_TDaddLine('TABLE END');
  393.  
  394.  
  395.  
  396.                             // move Y xursor.
  397.                             $this->_lines[$this->_line]['top'$this->tables[$table]['bottom'];
  398.                             // move X xursor.
  399.                             $this->_lines[$this->_line]['left'$this->tables[$table]['left'];
  400.  
  401.                             $this->_lines[$this->_line]['right'$this->tables[$table]['startright'];
  402.  
  403.  
  404.                             $this->_lines[$this->_line]['x'$this->tables[$table]['left'];
  405.  
  406.  
  407.                         }
  408.                         break;
  409.  
  410.  
  411.                     //case 'TR':
  412.                     case 'CAPTION':
  413.                     case 'TH':
  414.                         $item[0'TD';
  415.                     case 'TD':
  416.  
  417.                         if ($method == 'push'// start
  418.                             if (!@$this->td[$pos]{
  419.                                 $t $this->inID('TABLE');
  420.                                 print_r($this->tables[$t]);
  421.                                 echo  "LOST TD?:$pos";
  422.                                 exit;
  423.                             }
  424.                             $this->td[$pos]['lines'= array();
  425.                             $this->td[$pos]['line_items'= array();
  426.  
  427.  
  428.                             $this->push($item[0],$pos,@$item[1]);
  429.  
  430.                             $this->_nextLine("TD - start");
  431.                             $this->_TDaddLine("TD - start");
  432.  
  433.                             $this->_lines[$this->_line]['left'$this->td[$pos]['left'];
  434.                             $this->_lines[$this->_line]['right'$this->td[$pos]['right'];
  435.                             $this->_lines[$this->_line]['x'$this->td[$pos]['left'];
  436.  
  437.                             // this doesnt matter -   gets changed later...
  438.                             //$this->_lines[$this->_line]['top'] = $this->td[$pos]['top'];
  439.  
  440.                             $this->_makeColors();
  441.  
  442.                             $this->td[$pos]['gc'$this->_gc;
  443.                             $this->td[$pos]['bggc'=$this->_bggc;
  444.  
  445.                         else {
  446.                             // if the td is before the table - dont over pop the stack
  447.                             if ($this->inID('TD'$this->inID('TABLE'))
  448.                                break;
  449.                             $this->_nextLine('TD - END');
  450.                             $this->_TDaddLine('TD - END');
  451.                             $td $this->pop('TD',$pos);
  452.                         }
  453.  
  454.                         break;
  455.  
  456.  
  457.                     case 'BODY':
  458.                         $this->Start = TRUE;
  459.                         $this->$method($item[0],$pos);
  460.                         if ($method == 'push'{
  461.                             $backgroundcolor $this->in('BGCOLOR');
  462.                             if (!$backgroundcolor)
  463.                                 $backgroundcolor "#000000";
  464.                             $this->_makeColors('#000000',$backgroundcolor);
  465.                             $this->td[0]['bggc'=  $backgroundcolor;
  466.                             $this->tables[0]['bggc'=   $backgroundcolor;
  467.  
  468.  
  469.  
  470.                         }
  471.                         break;
  472.                     case 'PRE':
  473.                         //$this->output($item[0]);
  474.                         $this->linebr($this->_tokens[$pos][0],$pos);
  475.                         $this->$method($item[0],$pos);
  476.                         break;
  477.                     case 'SELECT'// hide this stuff
  478.                     case 'TEXTAREA':
  479.                     case 'T':
  480.  
  481.                     case 'TT':
  482.                     case 'CODE':
  483.                     case 'B':
  484.                     case 'I':
  485.                         $this->$method($item[0],$pos);
  486.                         break;
  487.  
  488.                     case 'SMALL':
  489.                         $this->$method('H6',$pos,$item[0]{1});
  490.                         break;
  491.                     case 'H1':
  492.                     case 'H2':
  493.                     case 'H3':
  494.                     case 'H4':
  495.                     case 'H5':
  496.                     case 'H6':
  497.                         //$this->output($item[0]);
  498.                         //if ($method == 'pop')
  499.                             $this->linebr($this->_tokens[$pos][0],$pos);
  500.                         $this->$method('H',$pos,$item[0]{1});
  501.                         //if ($method == 'push')
  502.                         //    $this->linebr($this->_tokens[$pos][0],$pos);
  503.                         break;
  504.                     case 'DIV'// ?? this stuf could contain formating?
  505.                     case 'FONT':
  506.                     case 'TR':
  507.                         $this->$method($item[0],$pos,@$item[1]);
  508.                         break;
  509.                     case 'A':
  510.                         if (@$item[1&& preg_match('/name=/i',@$item[1])) {
  511.                             // add anchor
  512.                         else {
  513.                             $ret $this->$method($item[0],$pos,@$item[1]);
  514.                         }
  515.  
  516.  
  517.                         break;
  518.                     case 'HR':
  519.                         $this->linebr($this->_tokens[$pos][0],$pos);
  520.                         $this->linebr($this->_tokens[$pos][0],$pos);
  521.                         break;
  522.                     case 'LI':
  523.                     case 'DT':
  524.                     case 'DD':
  525.                         $this->$method($item[0],$pos,@$item[1]);
  526.                         if ($method == 'push')
  527.                             $this->linebr($this->_tokens[$pos][0],$pos);
  528.                         break;
  529.  
  530.                     case 'BR':
  531.                     case 'P':
  532.  
  533.                         //$this->output($item[0]);
  534.                         //if ($method == 'push')
  535.                         $this->linebr($this->_tokens[$pos][0],$pos);
  536.                         break;
  537.                     default:
  538.                         echo "\nNOT HANDLED: -{$item[0]}-\n";
  539.                         break;
  540.                 }
  541.  
  542.                 continue;
  543.             }
  544.             //echo $pos;
  545.             // strings only!
  546.             if ($t $this->inID('TABLE'))
  547.                 if ($t ($td $this->inID('TD'))) {
  548.                     if (trim($item)) {
  549.                         echo "TABLE : $t, TD: $td skipping $item\n";
  550.                         exit;
  551.                     }
  552.                 }
  553.             $this->output($item,$pos);
  554.         }
  555.         $this->linebr('LAST LINE',$pos);
  556.  
  557.  
  558.         $this->_TABLEmovelines(0);
  559.         $this->_DrawingAreaRepaint();
  560.         $this->_area_y $this->_lines[$this->_line]['bottom';
  561.         $this->layout->set_size($this->_area_x $this->_area_y );
  562.         $this->drawing_area->size($this->_area_x,$this->_area_y);
  563.         //$this->layout->thaw();
  564.         //print_r($this->td);
  565.         if (!@$this->pixmap || ($this->_area_x $this->_pixmap_area_x|| ($this->_area_y $this->_pixmap_area_y)) {
  566.             if ($this->_area_x $this->_pixmap_area_x$this->_pixmap_area_x $this->_area_x;
  567.             if ($this->_area_y $this->_pixmap_area_y$this->_pixmap_area_y $this->_area_y;
  568.             if (@$this->pixmapunset($this->pixmap);
  569.             //echo "REMAKING PIXMAP: {$this->_pixmap_area_x},{$this->_pixmap_area_y}\n";
  570.             $this->pixmap = new GdkPixmap($this->drawing_area->window,
  571.                 $this->_pixmap_area_x ,$this->_pixmap_area_y,
  572.                 -1);
  573.         }
  574.         $this->_DrawingAreaClear();
  575.         foreach(array_keys($this->tablesas $pos)
  576.             $this->_drawBlock($this->tables[$pos]);
  577.         foreach(array_keys($this->tdas $pos)
  578.             $this->_drawBlock($this->td[$pos]);
  579.         foreach(array_keys($this->_textPartsas $id)
  580.             $this->_drawPart($id);
  581.  
  582.         $this->_DrawingAreaRepaint();
  583.  
  584.         $vadj $this->layout->get_vadjustment();
  585.         $vadj->set_value(0);
  586.         $vadj->changed();
  587.  
  588.         $this->_Building=FALSE;
  589.         //print_r($this->tables);
  590.         //print_r($this->_lines);
  591.  
  592.     }
  593.  
  594.     /*-----------------------------STACK STUFF--------------------------------*/
  595.  
  596.     var $check ""// put TABLE|TD to monitor the stack
  597.  
  598.  
  599.     var $stack;// array of item stack
  600.     var $cur// top of stack
  601.     var $curid// id of top of stack
  602.     function push($what,$pos,$attributes=''// push a token or attributes onto the stack
  603.         //echo "PUSH: $what, $attributes\n";
  604.         if ($attributes && $attributes{strlen($attributes)-1== '/'{
  605.             //echo "SKIP";
  606.             return;
  607.         }
  608.         if (!@$this->stack[$what])
  609.             $this->stack[$what= array();
  610.         if (!$attributes$attributes ":";
  611.         $this->stack[$what][= array($pos,trim($attributes));
  612.         $this->cur[$whattrim($attributes);
  613.         $this->curid[$what$pos;
  614.         $this->_stackAttributes($attributes,$pos,'push');
  615.  
  616.         if ($this->check  && preg_match("/^(".$this->check.")$/",$what)) {
  617.             echo "\nPUSH:$what:";print_r($this->stack[$what]);
  618.             echo "\nCUR:{$this->cur[$what]}\n";
  619.         }
  620.     }
  621.     function pop($what,$pos=0,$attributes=array()) // pull a token or attributes off the stack
  622.         if (!@$this->stack[$what]return;
  623.         list($id,$removearray_pop($this->stack[$what]);
  624.         $this->cur[$what"";
  625.         $this->curid[$what= 0;
  626.         if ($this->stack[$what])
  627.             list($this->curid[$what],$this->cur[$what]$this->stack[$what][count($this->stack[$what])-1];
  628.         $this->_stackAttributes($remove,$pos,'pop');
  629.         /* debugging*/
  630.         if ($this->check  && preg_match("/^(".$this->check.")$/",$what)) {
  631.             echo "\nPOP:$what:AT$pos:";print_r($this->stack[$what]);
  632.             echo "\nCUR:{$this->cur[$what]}\n";
  633.         }
  634.         return $id;
  635.     }
  636.     function clearStack($what,$pos// clear the stack 'what' of all items greater or equal than $pos
  637.         //echo "CLEARING STACK OF $what for $pos\n ";
  638.         if (!@$this->stack[$what]return;
  639.         $c count(@$this->stack[$what]-1;
  640.         for($i=$c;$i>-1;$i--{
  641.             list($id,$attr$this->stack[$what][$i];
  642.             if ($id >= $pos$this->pop($what);
  643.         }
  644.     }
  645.     function _stackAttributes($attributes,$pos,$method// add/remove from  stack attributes like color or font
  646.         if ($attributes == ":"return;
  647.         if ($attributes == '/'return;
  648.         if ($attributes{0== "#"return;
  649.  
  650.         $args = array();
  651.  
  652.         if (preg_match("/\scolor\=[\"\']?\#?([0-9A-F]{6})[\"\']?/mi",' '.$attributes,$args))
  653.             $this->$method("FGCOLOR",$pos,"#".$args[1]);
  654.  
  655.         $args = array();
  656.         if (preg_match("/\sbgcolor\=[\"\']?\#?([0-9A-F]{6})[\"\']?/mi",' '.$attributes,$args)) {
  657.             $this->$method("BGCOLOR",$pos,"#".$args[1]);
  658.         else if (preg_match("/\sbgcolor\=[\"\']?([a-z]+)[\"\']?/mi",' '.$attributes,$args)) {
  659.             $this->$method("BGCOLOR",$pos,strtolower($args[1]));
  660.         }
  661.  
  662.         $args = array();
  663.         if (preg_match("/\stext\=[\"\']?([a-z]+)[\"\']?/mi",' '.$attributes,$args))
  664.             $this->$method("FGCOLOR",$pos,$args[1]);
  665.         $args = array();
  666.         if (preg_match("/\sclass\=[\"\']?([a-z]+)[\"\']?/mi",' '.$attributes,$args))
  667.             $this->_stackStyle($method,$pos,$args[1]);
  668.  
  669.         $args = array();
  670.         if (preg_match("/\shref\=[\"\']?([^\"\']+)[\"\']?/mi",' '.$attributes,$args)) {
  671.             $col $this->in('LINKCOLOR');
  672.             if (!$col$col 'blue';
  673.             $this->$method('FGCOLOR',$pos,$col);
  674.             $this->$method('U',$pos,":");
  675.             $this->$method('HREF',$pos,$args[1]' ');
  676.             //echo "LINK: {$args[1]}\n";
  677.         }
  678.         $args = array();
  679.         if (preg_match("/\slink\=[\"\']?\#?([0-9A-F]{6})[\"\']?/mi",' '.$attributes,$args))
  680.               $this->$method('LINKCOLOR',$pos,"#".$args[1]);
  681.  
  682.         $args = array();
  683.         if (preg_match("/\salign\=[\"\']?([a-z]+)[\"\']?/mi",' '.$attributes,$args))
  684.             $this->$method("ALIGN",$pos,strtolower($args[1]));
  685.  
  686.  
  687.  
  688.  
  689.  
  690.     }
  691.     function _stackStyle($method,$pos,$class//add/remove from stack class='xxx' (not implemented yet)
  692.         return;
  693.         /* TODO?? */
  694.         switch ($class{
  695.             case 'programlisting':
  696.                 $this->$method("BGCOLOR",'');
  697.         }
  698.  
  699.     }
  700.     function in($what{  // get the attributes for a tag from top of stack
  701.         return @$this->cur[$what];
  702.     }
  703.     function inID($what// get the id for a tag from top of stack
  704.         return @$this->curid[$what];
  705.     }
  706.  
  707.  
  708.     /*-----------------------------TEXT STUFF --------------------------------*/
  709.     var $_links = array();
  710.     var $_textParts = array()// associative array of all the text parts to be drawn
  711.     function linebr($item='',$reason// add a line break
  712.         //
  713.         if ($item == "/P"{
  714.            // echo "LASTBR: {$this->lastbr} : CURRENT TEXTPARTS" . count($this->_textParts) . "\n";
  715.             return;
  716.         }
  717.             //&& ($this->lastbr == ("P". count($this->_textParts)))) return;
  718.         //if ($item && $this->lastbr && ($this->lastbr != $item)) return;
  719.         //$this->widget->insert($this->_font,$this->_fgcolor,$this->bg_color,":$item:\n");
  720.         //$this->outputTAG($item);
  721.         $this->_makeFont();
  722.         $this->_nextLine('LINEBREAK');
  723.         $this->_updateLine('','LINEBR');
  724.         $this->_TDaddLine("LINE BR $reason");
  725.         $this->lastbr count($this->_textParts);
  726.     }
  727.     function output($string,$pos// output a string
  728.  
  729.         if (!$this->Startreturn;
  730.         $string $this->unhtmlentities($string);
  731.         if (!$this->inID('PRE')) {
  732.             $string trim(preg_replace("/[\n \t\r]+/m"' ',$string)) ' ';
  733.         else {
  734.             if (!trim($string)) return// except PRE stuff!q
  735.         }
  736.  
  737.         // invisible stuff
  738.         if ($this->inID('SELECT')) return;
  739.         if ($this->inID('TEXTAREA')) return;
  740.  
  741.         $this->_makeFont();
  742.         $this->_makeColors();
  743.         //echo $this->inID('PRE') ."output  {$string}\n";
  744.  
  745.         if ($this->inID('PRE')) {
  746.             $this->outputPRE($string,$pos);
  747.             return;
  748.         }
  749.  
  750.         $this->outputTEXT($string,$pos);
  751.  
  752.     }
  753.     function outputTAG($tag{     // output a tag (for debugging)
  754.  
  755.         $this->_makeFont('-adobe-helvetica-bold-r-normal-*-*-80-*-*-p-*-iso8859-1');
  756.         $this->_makeColors("#000000","#FFFF00",FALSE);
  757.         $this->outputTEXT("<{$tag}>","tag");
  758.     }
  759.     function outputTEXT($string,$pos// really add to $this->_textParts a text item
  760.  
  761.         /*echo "outputTEXT ".
  762.  
  763.             "X:".$this->_lines[$this->_line]['x'] .
  764.             "L:". $this->_lines[$this->_line]['left'].
  765.             "R:". $this->_lines[$this->_line]['right'].
  766.             "\n";
  767.         */
  768.         $array $this->_breakString(
  769.             $this->_lines[$this->_line]['x'],
  770.             $this->_lines[$this->_line]['left'],
  771.             $this->_lines[$this->_line]['right'],
  772.             $string);
  773.         // array of lines (startpos,len,text)
  774.         //echo serialize($array);
  775.         $c count($array-1;
  776.         foreach($array as $i=>$data{
  777.  
  778.             if ($data[2!== ''{
  779.  
  780.                 $this->_updateLine($data[2],'outputTEXT');
  781.  
  782.                 $this->_textParts[= array(
  783.                     'string' => $data[2],
  784.                     'line' =>   $this->_line,
  785.                     'left' =>   $data[0],
  786.                     'width' =>  $data[1],
  787.                     'bggc' =>   $this->_bggc,
  788.                     'gc'   =>   $this->_gc,
  789.                     'font' =>   $this->_font,
  790.                     'u'    =>   $this->inID('U'),
  791.                     'href'    =>   $this->in('HREF')
  792.                 );
  793.                 //if ($this->inID('U')) echo "ADDING? " . $this->in('HREF') . "\n";
  794.                 $this->_lines[$this->_line]['textwidth'@$this->_lines[$this->_line]['textwidth'$data[1];
  795.                 $this->_lines[$this->_line]['align'$this->in('ALIGN');
  796.                 //$widget->show();
  797.                 $this->_updateLine("POS:$pos"'outputTEXT2');
  798.                 $this->_lines[$this->_line]['x'$data[0$data[1];
  799.                 if ($c != $i{
  800.                     $this->_nextLine('TEXTout');
  801.                     $this->_updateLine('','OutputTEXT NEWLINE');
  802.                     $this->_TDaddLine('TEXT out');
  803.                 }
  804.             }
  805.         }
  806.  
  807.     }
  808.     function outputPRE($string,$pos// output preformated text
  809.  
  810.  
  811.         if (strpos($string,"\n"=== FALSE{
  812.  
  813.             $this->outputTEXT($string,$pos);
  814.             return;
  815.         }
  816.  
  817.         $array explode("\n"$string;
  818.         // array of lines (startpos,len,text)
  819.         $c count($array-1;
  820.         foreach($array as $i=>$line{
  821.             $this->_TDaddLine('PRE out');
  822.             $this->_updateLine($line'outputPRE');
  823.             //echo "OUTPUT PRE: $line\n";
  824.             $this->outputTEXT($line,$pos);
  825.             if ($c != $i{
  826.                 $this->_nextLine('PREout');
  827.  
  828.             }
  829.         }
  830.  
  831.     }
  832.     function _drawPart($id{  // draw a textPart on the pixmap
  833.         $part $this->_textParts[$id];
  834.         $line $this->_lines[$part['line']];
  835.         //print_r($part);
  836.         //print_r($line);
  837.         //exit;
  838.         while(gtk::events_pending()) gtk::main_iteration();
  839.         if ($url $part['href']{
  840.             $link = array(
  841.                 'left' => $part['left'$line['leftshift'],
  842.                 'right' => $part['left'$line['leftshift'$part['width'],
  843.                 'top' => $line['top'],
  844.                 'bottom' => $line['bottom'],
  845.                 'url' => $url
  846.             );
  847.             $this->_links[$link;
  848.         }
  849.         if (trim($part['string']))
  850.             gdk::draw_rectangle($this->pixmap,
  851.                 $this->_gcs[$part['bggc']],true,
  852.                 $part['left'$line['leftshift'],  $line['top'],
  853.                 $part['width']$line['height']
  854.             );
  855.  
  856.         gdk::draw_text($this->pixmap,
  857.             $this->_fonts[$part['font']]$this->_gcs[$part['gc']] ,
  858.             $part['left'$line['leftshift'],  $line['y'],
  859.             $part['string']strlen($part['string'])
  860.         );
  861.         if ($part['u'])
  862.             gdk::draw_rectangle($this->pixmap,
  863.                 $this->_gcs[$part['gc']],true,
  864.                 $part['left'$line['leftshift'],  $line['top'$line['height'-1,
  865.                 $part['width']1
  866.             );
  867.         /*
  868.         $this->drawing_area->draw(
  869.             new GdkRectangle(
  870.                 $part['left'] + $line['leftshift'],  $line['bottom'],
  871.                 $part['width'], $line['height']
  872.             )
  873.         );
  874.         */
  875.  
  876.  
  877.  
  878.  
  879.  
  880.     }
  881.     function _drawBlock($ar{  // draw a block (eg. TABLE or TD)
  882.         if (!$ar['bggc']{
  883.             print_r($ar);
  884.             echo 'NO BGGC';
  885.             return;
  886.         }
  887.         while(gtk::events_pending()) gtk::main_iteration();
  888.         gdk::draw_rectangle($this->pixmap,
  889.             $this->_gcs[$ar['bggc']],true,
  890.             $ar['left']$ar['top'],
  891.             $ar['right']$ar['bottom'-$ar['top']
  892.         );
  893.         /*
  894.         $this->drawing_area->draw(
  895.             new GdkRectangle(
  896.                 $ar['left'], $ar['top'],
  897.                 $ar['right'], $ar['bottom'] - $ar['top']));
  898.             */
  899.     }
  900.     function _breakString($start,$left,$right,$string// break a string into lines and return an array
  901.  
  902.         $l @$this->_fonts[$this->_font]->extents($string);
  903.         //echo serialize($l);
  904.         //echo "\nSTRINGLEN: $string =  {$l[2]} \n";
  905.         if ($l[2($right $start)) {
  906.             return array(array($start,$l[2],$string));
  907.         }
  908.         $ret = array();
  909.         $buf "";
  910.         $words explode(" ",$string);
  911.         foreach ($words as $w{
  912.  
  913.             $l @$this->_fonts[$this->_font]->extents($buf " " $w);
  914.             if ($l[2]($right $start)) {
  915.                 $buf .= " " $w;
  916.                 continue;
  917.             }
  918.             // its longer! and buffer is empty.. (and it's the first line!
  919.             if ($buf == "" && ($start != $left)) {
  920.                 $ret[= array($start,0,' ');
  921.                 $start $left;
  922.                 if ($l[2($right $start)) {
  923.                     $buf =  $w;
  924.                     continue;
  925.                 }
  926.                 // it's longer and - just add it as a new line
  927.                 // even though it's too big!
  928.                 $ret[= array($start,$l[2],$w);
  929.                 continue;
  930.             }
  931.             // its longer, add the buffer to stack, clear buffer
  932.             $l @$this->_fonts[$this->_font]->extents($buf);
  933.             $ret[= array($start$l[2,$buf);
  934.             $buf $w;
  935.             $start $left;
  936.         }
  937.         if ($buf{
  938.             $l @$this->_fonts[$this->_font]->extents($buf);
  939.             $ret[= array($start$l[2,$buf);
  940.         }
  941.         return $ret;
  942.  
  943.  
  944.  
  945.     }
  946.     function unhtmlentities ($string)  // convert 'html entities' back into text
  947.         $trans_tbl get_html_translation_table (HTML_ENTITIES);
  948.         $trans_tbl array_flip ($trans_tbl);
  949.         $ret strtr ($string$trans_tbl);
  950.         return  preg_replace('/\&\#([0-9]{2,3})\;/me',"chr('\\1')",$ret);
  951.     }
  952.  
  953.     /* -----------------------------LINE SUTFF -------------------*/
  954.  
  955.     var $_line// current line
  956.     var $_lines = array()// array of all the lines in the document (used to work out 'y' locations
  957.     function _updateLine($string '',$updateReason// update line height based on current font
  958.         // store the line height of the current line..
  959.  
  960.         //if (!@$this->_lines[$this->_line]['ascent']) $this->_lines[$this->_line]['ascent']=1;
  961.         //if (!@$this->_lines[$this->_line]['decent']) $this->_lines[$this->_line]['decent']=5;
  962.  
  963.         if (@$this->_lines[$this->_line]['ascent'$this->_fonts[$this->_font]->c_ascent )
  964.             $this->_lines[$this->_line]['ascent'$this->_fonts[$this->_font]->c_ascent;
  965.         if (@$this->_lines[$this->_line]['descent'$this->_fonts[$this->_font]->c_descent )
  966.             $this->_lines[$this->_line]['descent'$this->_fonts[$this->_font]->c_descent;
  967.         if (!isset($this->_lines[$this->_line]['descent'])) {
  968.             echo "FAILED TO FIND DESCENT {$this->_line}\n";
  969.             echo serialize($this->_fonts[$this->_font]->descent);
  970.             exit;
  971.         }
  972.         if (!@$this->_lines[$this->_line]['updateReason'])
  973.             $this->_lines[$this->_line]['updateReason''';
  974.         $this->_lines[$this->_line]['updateReason'.= $updateReason;
  975.  
  976.         $this->_lines[$this->_line]['height'=
  977.             $this->_lines[$this->_line]['descent'$this->_lines[$this->_line]['ascent'];
  978.  
  979.         $this->_calcLine($this->_line);
  980.  
  981.         // store the active block heights....
  982.  
  983.  
  984.  
  985.         if ($string)
  986.             $this->_lines[$this->_line]['string'.= $string;
  987.  
  988.     }
  989.     function _calcLine($l) { // create line's y and bottom based on top + font stuff
  990.         $this->_lines[$l]['y'] = $this->_lines[$l]['top'] + $this->_lines[$l]['ascent'];
  991.         $this->_lines[$l]['bottom'] = $this->_lines[$l]['top'] + $this->_lines[$l]['height'];
  992.         $this->_lines[$l]['leftshift'] = $this->_lines[$l]['indent'];
  993.         if ($this->_lines[$l]['align'] == 'right')
  994.             $this->_lines[$l]['leftshift'] = $this->_lines[$l]['right'] - $this->_lines[$l]['left'] - $this->_lines[$l]['textwidth'];
  995.         if ($this->_lines[$l]['align'] == 'center')
  996.             $this->_lines[$l]['leftshift'] = ($this->_lines[$l]['right'] - $this->_lines[$l]['left'] - $this->_lines[$l]['textwidth']) /2;
  997.  
  998.  
  999.  
  1000.     }
  1001.     function _nextLine($reason) {  // create a new line (set up left/right,x top,y... etc.
  1002.         $this->_line++;
  1003.         if (!isset($this->_lines[$this->_line-1]['bottom'])) {
  1004.             print_r($this->_lines);
  1005.             echo "NO BOTTOM ON NEXT LINE";
  1006.             exit;
  1007.         }
  1008.  
  1009.         $this->_lines[$this->_line]['left']            =  $this->_lines[$this->_line-1]['left'];
  1010.         $this->_lines[$this->_line]['indent']          = @$this->_lines[$this->_line-1]['indent'];
  1011.         $this->_lines[$this->_line]['indentfromlast']  = @$this->_lines[$this->_line-1]['indent'];
  1012.         $this->_lines[$this->_line]['right_from_last'] =  $this->_lines[$this->_line-1]['right'];
  1013.         $this->_lines[$this->_line]['right']           =  $this->_lines[$this->_line-1]['right'];
  1014.         $this->_lines[$this->_line]['x']               =  $this->_lines[$this->_line]['left'];
  1015.  
  1016.  
  1017.         $this->_lines[$this->_line]['top'] = $this->_lines[$this->_line-1]['bottom'];
  1018.         $this->_lines[$this->_line]['y'] = $this->_lines[$this->_line-1]['bottom'];
  1019.         $this->_lines[$this->_line]['string'] = '';
  1020.         $this->_lines[$this->_line]['reason'] = $reason;
  1021.         $this->_lines[$this->_line]['ascent'] =0;
  1022.         $this->_lines[$this->_line]['descent'] =0;
  1023.         $this->_lines[$this->_line]['height'] =0;
  1024.         $this->_lines[$this->_line]['align'] ='left';
  1025.         //$this->_updateLine();
  1026.         $this->_calcLine($this->_line);
  1027.  
  1028.     }
  1029.  
  1030.     function _TDaddLine($reason = '') { // add a line to a block if it is inside one
  1031.         if ($id = $this->inID('TD')) {
  1032.             $table = $this->inID('TABLE');
  1033.             if ($table > $id) {
  1034.                 echo "TRIED $reason TO ADD TD:$id to TABLE:$table\n";
  1035.                 return;
  1036.             }
  1037.             if (!isset($this->td[$id]['lines'])) {
  1038.                 print_r($this->td);
  1039.                 echo "NO TD FOR $id\n";
  1040.                 exit;
  1041.             }
  1042.             if (!in_array($this->_line,$this->td[$id]['lines'])) {
  1043.                 $this->td[$id]['lines'][] = $this->_line;
  1044.                 $this->td[$id]['line_items'][$this->_line] = &$this->_lines[$this->_line];
  1045.             }
  1046.         } else {
  1047.             if (!in_array($this->_line,$this->td[0]['lines'])) {
  1048.                 $this->td[0]['lines'][] = $this->_line;
  1049.                 $this->td[0]['line_items'][$this->_line] = &$this->_lines[$this->_line];
  1050.             }
  1051.         }
  1052.     }
  1053.  
  1054.     /*------------------------------FONTS AND COLORS --------------------------*/
  1055.  
  1056.     var $_fonts = array()// associative array of fonts
  1057.     var $_font = NULL; // name (string) of current font
  1058.     function _makeFont($default='') { // make the font based on the stack information
  1059.         $font['pointsize'] = 100;
  1060.         $font['space'] = 'p';  // or 'm' for monospaced
  1061.         $font['family'] = 'times'// or helvetica, courier
  1062.         $font['weight'] = 'medium'// or bold
  1063.         $font['slant'] = 'r'// or o
  1064.         /* not used? */
  1065.         $font['setwidth'] = 'normal';
  1066.  
  1067.         //PRE:
  1068.         if ($this->inID('PRE') || $this->inID('TT') || $this->inID('CODE') ) {
  1069.             //echo "IN PRE?" . $this->inID('PRE') . "\n";
  1070.             $font['family']  = 'courier';
  1071.             $font['space'] = 'm';
  1072.             $font['pointsize'] = 80;
  1073.         }
  1074.         if ($this->inID('B'))
  1075.             $font['weight']  = 'bold';
  1076.         if ($this->inID('I'))
  1077.             $font['slant']  = 'i';
  1078.  
  1079.         if ($v = $this->in('H')) {
  1080.             //&& is_int($v)
  1081.             // mapping 1=large = eg. 160 , 3=100 ok 5 = small
  1082.             // 20 * $v  would give 1=20, 2=40 .. 5=100
  1083.             // now 160-$v;; would give 1 = 140, 3=100
  1084.             $font['weight']  = 'bold';
  1085.             $font['pointsize'] = 180 - ($v * 20);
  1086.             //echo "setting point size = {$font['pointsize']}";
  1087.         }
  1088.  
  1089.  
  1090.  
  1091.  
  1092.         $fontname = $this->_getFontString($font);
  1093.         //echo $fontname;
  1094.         if ($default) $fontname =  $default;
  1095.  
  1096.         if (@!$this->_fonts[$fontname]) {
  1097.             $this->_fonts[$fontname] = gdk::font_load($fontname);
  1098.             //cached font size details
  1099.             $this->_fonts[$fontname]->c_ascent= $this->_fonts[$fontname]->ascent;
  1100.             $this->_fonts[$fontname]->c_descent= $this->_fonts[$fontname]->descent;
  1101.         }
  1102.         if (!$this->_fonts[$fontname])
  1103.             echo "FAIL: $fontname\n";
  1104.         //echo "SET FONT: $fontname\n";
  1105.         $this->_font =  $fontname;
  1106.  
  1107.     }
  1108.     function _getFontString($array) { // create a font string based on an associatve array describing the font
  1109.         $ret = "";
  1110.         foreach(array(
  1111.             'foundary','family','weight','slant','setwidth',
  1112.             'addedstyle', 'pixelsize','pointsize','resx','resy',
  1113.             'space','averagewidth','registry','encoding') as $i) {
  1114.             $a = '*';
  1115.             if (@$array[$i]) $a = $array[$i];
  1116.             $ret .= "-{$a}";
  1117.         }
  1118.         return $ret;
  1119.     }
  1120.     var $_gcs = array()// associative array fo fgcolor:bgcolor to GC object
  1121.     var $_gc = NULL; // forground string eg. "#FFFFFF#000000" describing FG/BG color
  1122.     var $_bggc = NULL; //bacground string eg. "#FFFFFF#000000" describing FG/BG color
  1123.     var $_colors = array()// associative array of colors id's to GtkColors
  1124.     function _makeColors($fgcolor = "#000000",$bgcolor = "#FFFFFF",$read=TRUE) {  // set the current colour based on stack, or overiden data
  1125.         if ($read) {
  1126.             if ($c = $this->in('FGCOLOR'))
  1127.                 $fgcolor = $c;
  1128.             if ($c = $this->in('BGCOLOR'))
  1129.                 $bgcolor = $c;
  1130.         }
  1131.         /* GC STUFF */
  1132.  
  1133.         if (!@$this->_gcs[$fgcolor] || !@$this->_gcs[$bgcolor]) {
  1134.             $window = $this->drawing_area->window;
  1135.             $cmap = $this->drawing_area->get_colormap();
  1136.         }
  1137.  
  1138.         if (!@$this->_gcs[$fgcolor]) {
  1139.             $this->_gcs[$fgcolor] = $window->new_gc();
  1140.             $this->_gcs[$fgcolor]->foreground =  $cmap->alloc($fgcolor);
  1141.         }
  1142.         if (!@$this->_gcs[$bgcolor]) {
  1143.             $this->_gcs[$bgcolor] = $window->new_gc();
  1144.             $this->_gcs[$bgcolor]->foreground =  $cmap->alloc($bgcolor);
  1145.         }
  1146.         $this->_gc = $fgcolor;
  1147.         $this->_bggc = $bgcolor;
  1148.  
  1149.     }
  1150.  
  1151.     /* ------------------------------ BASIC WIDGET STUFF ------------------*/
  1152.     //
  1153.     var $_area_x= 600; // default X width of Widget
  1154.     var $_area_y= 400; // default Y width of Widget
  1155.     var $layout// GtkLayout
  1156.     var $drawing_area// GtkDrawingArea
  1157.     var $scrolledwindow// GtkScrolledwindow.
  1158.     function Interface() { // Create the Drawing Area
  1159.         $this->widget = &new GtkScrolledWindow();
  1160.         $hadj = $this->widget->get_hadjustment();
  1161.         $vadj = $this->widget->get_vadjustment();
  1162.         $hadj->connect('value-changed', array(&$this,'_LayoutScrolled'));
  1163.         $vadj->connect('value-changed', array(&$this,'_LayoutScrolled'));
  1164.  
  1165.         $this->layout =  &new GtkLayout($hadj,$vadj);
  1166.         $this->layout->set_size($this->_area_x,$this->_area_y);
  1167.         $this->layout->show();
  1168.         $this->widget->show();
  1169.         $this->widget->set_policy(GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
  1170.         $this->widget->add($this->layout);
  1171.         //$this->scrolledwindow->add_events(  GDK_EXPOSURE_MASK  );
  1172.  
  1173.         define('GDK_HAND2',60);
  1174.         define('GDK_ARROW',68);
  1175.         define('GDK_CLOCK',26);
  1176.  
  1177.         $this->_cursors[GDK_HAND2] = gdk::cursor_new(GDK_HAND2);
  1178.         $this->_cursors[GDK_ARROW] = gdk::cursor_new(GDK_ARROW);
  1179.         $this->_cursors[GDK_CLOCK] = gdk::cursor_new(GDK_CLOCK);
  1180.  
  1181.         $this->drawing_area  = &new GtkDrawingArea();
  1182.         $this->drawing_area->size($this->_area_x,$this->_area_y);
  1183.         $this->layout->put($this->drawing_area,0,0);
  1184.         //$this->drawing_area->set_events( GDK_ALL_EVENTS_MASK);
  1185.         $this->drawing_area->show();
  1186.  
  1187.  
  1188.         $this->drawing_area->connect("configure_event",        array(&$this,"_DrawingAreaCallbackConfigure"));
  1189.         $this->drawing_area->connect("expose_event",           array(&$this,"_DrawingAreaCallbackExpose"));
  1190.         $this->drawing_area->connect('motion_notify_event',array(&$this,"_DrawingAreaMotion"));
  1191.         $this->drawing_area->connect('button_press_event', array(&$this,"_DrawingAreaPress"));
  1192.  
  1193.  
  1194.         $this->drawing_area->set_events(
  1195.               GDK_EXPOSURE_MASK
  1196.             | GDK_LEAVE_NOTIFY_MASK
  1197.             | GDK_BUTTON_PRESS_MASK
  1198.             | GDK_POINTER_MOTION_MASK
  1199.             | GDK_POINTER_MOTION_HINT_MASK
  1200.         );
  1201.         //$this->drawing_area->set_events( GDK_ALL_EVENTS_MASK );
  1202.        // $this->layout->add_events(   GDK_KEY_PRESS_MASK   );
  1203.         $this->drawing_area->set_flags( GTK_CAN_FOCUS);
  1204.         
  1205.  
  1206.         //$this->drawing_area->connect("key_press_event",        array(&$this,"_DrawingAreaCallbackKeyPress"));
  1207.         //$this->drawing_area->connect("button_release_event",   array(&$this,"_DrawingAreaCallbackBtnPress"));
  1208.         //$this->drawing_area->connect("button_press_event",     array(&$this,"_DrawingAreacallbackBtnPress"));
  1209.         //$this->drawing_area->connect_after("event",    array(&$this,"_DrawingAreaMotion"));
  1210.         //$this->html->drawing_area->connect("expose_event",
  1211.         //    array(&$this,"callback_expose_event"));
  1212.  
  1213.  
  1214.  
  1215.     }
  1216.     var $_LayoutRefresh =0;
  1217.     function _LayoutScrolled() { // windows bug fixmo
  1218.         //echo "SCROLL";
  1219.         $this->layout->queue_draw();
  1220.         $vadj$this->widget->get_vadjustment();
  1221.         if (@!$this->drawing_area) return;
  1222.         if ($this->_LayoutRefesh==time()) return;
  1223.         $this->_LayoutRefesh=time();
  1224.         $this->drawing_area->hide();
  1225.         $this->drawing_area->show();
  1226.  
  1227.  
  1228.     }
  1229.     var $Realized = FALSE;
  1230.     var $pixmap// the pixmap that everything gets drawn on
  1231.     function _DrawingAreaCallbackConfigure($widget, $event) { // the callback to create the pixmap & start building
  1232.         if (@$this->pixmap) return true;
  1233.  
  1234.         $this->Realized = TRUE;
  1235.  
  1236.         
  1237.         $this->build();
  1238.         $this->_setCursor(GDK_ARROW);
  1239.  
  1240.         return true;
  1241.     }
  1242.  
  1243.  
  1244.     function _DrawingAreaClear() { // draw a big rectangle over the drawing area..
  1245.         //return; // not needed
  1246.         gdk::draw_rectangle($this->pixmap,
  1247.             //$this->_gcs["#FFFFFF{$this->backgroundcolor}"],
  1248.             $this->drawing_area->style->white_gc,
  1249.             true, 0, 0,
  1250.             $this->_area_x,$this->_area_y);
  1251.  
  1252.  
  1253.  
  1254.         // draw somethin on it.
  1255.         //$this->drawing_area->realize();
  1256.     }
  1257.     function _DrawingAreaRepaint() {
  1258.         if (!$this->pixmap) return;
  1259.         gdk::draw_pixmap($this->drawing_area->window,
  1260.             $this->drawing_area->style->fg_gc[$this->drawing_area->state],
  1261.             $this->pixmap,
  1262.             0,0,0,0,$this->_area_x,$this->_area_y);
  1263.     }
  1264.  
  1265.  
  1266.     var $_new_area_x// proposed new area!
  1267.     function _DrawingAreaCallbackExpose($widget,$event) { // standard callback to repaint a drawing area
  1268.         if (!$this->pixmap) return;
  1269.  
  1270.         if (!$this->_flag_rebuild  && ($this->layout->allocation->width > 400) && ($this->_area_x != $this->layout->allocation->width )) {
  1271.  
  1272.             if (  abs($this->_area_x - $this->layout->allocation->width) > 15) {
  1273.                 $this->_new_area_x = $this->layout->allocation->width ;
  1274.  
  1275.                 gtk::timeout_add(500, array(&$this,'_ChangeSize'), $this->layout->allocation->width);
  1276.             }
  1277.  
  1278.         }
  1279.  
  1280.         gdk::draw_pixmap($this->drawing_area->window,
  1281.             $widget->style->fg_gc[$widget->state],
  1282.             $this->pixmap,
  1283.             $event->area->x, $event->area->y,
  1284.             $event->area->x, $event->area->y,
  1285.             $event->area->width, $event->area->height);
  1286.         return false;
  1287.     }
  1288.     var $_flag_rebuild = FALSE;
  1289.     function _ChangeSize($newsize) {
  1290.  
  1291.         if (!$this->_flag_rebuild && ($newsize == $this->_new_area_x) && ($this->_area_x != $this->_new_area_x)) {
  1292.             //echo "BUILD? {$this->_area_x} = {$this->_new_area_x} \n";
  1293.             $this->_area_x = $this->_new_area_x;
  1294.             $this->_flag_rebuild = TRUE;
  1295.             $this->_setCursor(GDK_CLOCK);
  1296.             while(gtk::events_pending()) gtk::main_iteration();
  1297.             $this->build();
  1298.             $this->_setCursor(GDK_ARROW);
  1299.             while(gtk::events_pending()) gtk::main_iteration();
  1300.             $this->_flag_rebuild = FALSE;
  1301.  
  1302.         }
  1303.     }
  1304.  
  1305.  
  1306.     function _DrawingAreaMotion($widget,$event) {
  1307.         if ($event->is_hint) {
  1308.             $window = $event->window;
  1309.             $pointer = $window->get_pointer();
  1310.             $x = (int)$pointer[0];
  1311.             $y = (int)$pointer[1];
  1312.             $state = $pointer[2];
  1313.         } else {
  1314.             $x = (int)$event->x;
  1315.             $y = (int)$event->y;
  1316.             $state = $event->state;
  1317.         }
  1318.         $this->active_link  = $this->_getLink($x,$y);
  1319.         if ($this->active_link) {
  1320.             $this->_setCursor(GDK_HAND2);
  1321.         } else {
  1322.             $this->_setCursor(GDK_ARROW);
  1323.         }
  1324.  
  1325.         return true;
  1326.  
  1327.     }
  1328.  
  1329.     function _DrawingAreaPress() {
  1330.         if ($this->active_link) {
  1331.             if ($this->active_link{0} == "/") {
  1332.                 $url = $this->_URLparse['scheme'] . "://".
  1333.                     $this->_URLparse['host'] .
  1334.                     $this->active_link;
  1335.             } else if (preg_match('/[a-z]+:/', $this->active_link)) {
  1336.                 $url = $this->active_link;
  1337.  
  1338.             } else {
  1339.                 $path = dirname($this->_URLparse['path']) . '/';
  1340.                 if ($this->_URLparse['path']{strlen($this->_URLparse['path']) -1} == '/')
  1341.                     $path = $this->_URLparse['path'];
  1342.                 $url = $this->_URLparse['scheme'] . "://".
  1343.                     $this->_URLparse['host'] .
  1344.                     $path.$this->active_link;
  1345.             }
  1346.  
  1347.             $this->_setCursor(GDK_CLOCK);
  1348.             while(gtk::events_pending()) gtk::main_iteration();
  1349.             $this->_DrawingAreaClear();
  1350.             $this->loadURL($url);
  1351.             $this->tokenize();
  1352.             $this->build();
  1353.             $this->_setCursor(GDK_ARROW);
  1354.         }
  1355.     }
  1356.  
  1357.     var $_cursor =0;
  1358.     function _setCursor($newcursor) {
  1359.         if ($this->_cursor == $newcursor) return;
  1360.         $w = $this->drawing_area->window;
  1361.         $w->set_cursor($this->_cursors[$newcursor]);
  1362.         $this->_cursor = $newcursor;
  1363.     }
  1364.  
  1365.     function _getLink($x,$y) {
  1366.         foreach($this->_links as $link) {
  1367.             if ($y < $link['top']) continue;
  1368.             if ($y > $link['bottom']) continue;
  1369.             if ($x < $link['left']) continue;
  1370.             if ($x > $link['right']) continue;
  1371.             return $link['url'];
  1372.         }
  1373.     }
  1374.  
  1375.     /* ------------------------------ TABLE STUFF  ------------------*/
  1376.  
  1377.     /*
  1378.     tables : complex stuff:
  1379.     got a table : look ahead to find
  1380.     tr = number of rows
  1381.     td = no of colums and how big they are...
  1382.     */
  1383.  
  1384.  
  1385.     function _findSubTables($pos) { // find subtables (used to skip them when calculating widths.
  1386.         $pos++;
  1387.         $table[] = array();
  1388.         $c = count($this->_tokens);
  1389.         while ($pos < $c) {
  1390.             if (!is_array($this->_tokens[$pos])) {
  1391.                 $pos++;
  1392.                 continue;
  1393.             }
  1394.             if ($this->_tokens[$pos][0] == "TABLE")
  1395.                 $table[] = $pos;
  1396.             if ($this->_tokens[$pos][0] == "/TABLE") {
  1397.                 array_pop($table);
  1398.                 if (!$table) return $pos;
  1399.             }
  1400.             $pos++;
  1401.         }
  1402.         return $pos;
  1403.     }
  1404.     /* first version just divides available area! */
  1405.     var $td// associative array of [posid]['width'|'start']
  1406.     function _TABLEcalc($pos) { // read a table and guess it's widths (left/right)
  1407.         $left = $this->tables[$pos]['left'];
  1408.         $right = $this->tables[$pos]['right'];
  1409.         $maxwidth = $right-$left;
  1410.         $tableid = $pos;
  1411.         if (preg_match("/\swidth\=[\"\']?([0-9]+)([%]?)[\"\']?/mi",' '.$this->_tokens[$pos][1],$args)) {
  1412.             if ($args[2]) {
  1413.                 $right = $left + (int) (0.01 * $args[1]  * ($right - $left));
  1414.             } else {
  1415.                 $right = $left + $args[1];
  1416.             }
  1417.  
  1418.         }
  1419.  
  1420.  
  1421.         $pos++;
  1422.  
  1423.  
  1424.         $table = array()// table[row][col]
  1425.         $cells = array();
  1426.         $colsizes = array();
  1427.         $totalcols= 1;
  1428.         $totalrows = 1;
  1429.         $done = 0;
  1430.         $col =1;
  1431.         $row =0;
  1432.         $hasCaption = 0;
  1433.         $c = count($this->_tokens);
  1434.         while ($pos < $c) {
  1435.             if (!is_array($this->_tokens[$pos])) {
  1436.                 $pos++;
  1437.                 continue;
  1438.             }
  1439.             switch ($this->_tokens[$pos][0]) {
  1440.  
  1441.  
  1442.                 case "TR":
  1443.                     $row++;
  1444.                     if ($col > $totalcols) $totalcols = $col-1;
  1445.                     if ($row > $totalrows) $totalrows = $row;
  1446.                     $col = 1;
  1447.                     break;
  1448.  
  1449.                 case "CAPTION":
  1450.                     $hasCaption = $pos;
  1451.                     $table[$row][$col]['pos'] = $pos;
  1452.                     $table[$row][$col]['span'] = 1;
  1453.                     $table[$row][$col]['rowspan'] = 1;
  1454.                     $this->td[$pos]['pos'] = $pos;
  1455.                     $this->td[$pos]['row'] = $row;
  1456.                     $this->td[$pos]['col'] = $col;
  1457.                     $this->td[$pos]['colspan'] = 1;
  1458.                     $this->td[$pos]['rowspan'] = 1;
  1459.                     $this->td[$pos]['iscaption'] = 1;
  1460.                     $this->td[$pos]['table'] = $tableid;
  1461.                     $this->td[$pos]['tag'] =  $this->_tokens[$pos][0];
  1462.                     $cells[] = $pos;
  1463.                     $row++;
  1464.                     break;
  1465.                 case "TD";
  1466.                 case "TH";
  1467.  
  1468.                     while (@isset($table[$row][$col]['pos'])) // find next empty col.
  1469.                         $col++;
  1470.                     $args = array();
  1471.                     $span =1;
  1472.                     $rowspan =1;
  1473.                     $args = array();
  1474.                     if (!@$colsizes[$col]  && preg_match("/\swidth\=[\"\']?([0-9]+)([%]*)[\"\']?/mi",' '.@$this->_tokens[$pos][1],$args)) {
  1475.                         if ($args[2]) {
  1476.                             $colsizes[$col] = (int) (0.01 * $args[1]  * ($right - $left));
  1477.                         } else {
  1478.                             $colsizes[$col] = $args[1];
  1479.                         }
  1480.                     }
  1481.  
  1482.  
  1483.                     if (preg_match("/\scolspan\=[\"\']?([0-9]+)[\"\']?/mi",' '.@$this->_tokens[$pos][1],$args))
  1484.                         $span = $args[1];
  1485.                     if (preg_match("/\srowspan\=[\"\']?([0-9]+)[\"\']?/mi",' '.@$this->_tokens[$pos][1],$args))
  1486.                         $rowspan = $args[1];
  1487.  
  1488.                     $table[$row][$col]['pos'] = $pos;
  1489.                     $table[$row][$col]['span'] = $span;
  1490.                     $table[$row][$col]['rowspan'] = $rowspan;
  1491.  
  1492.  
  1493.                     for ($i=1;$i<$span;$i++)
  1494.                         $table[$row][$col+$i]['pos'] = $pos;
  1495.                     for ($i=1;$i<$rowspan;$i++)
  1496.                         for ($j=0;$j<$span;$j++)
  1497.                         $table[$row+$i][$col+$j]['pos'] = $pos;
  1498.  
  1499.                     $this->td[$pos]['tag'] =  $this->_tokens[$pos][0];
  1500.                     $this->td[$pos]['row'] = $row;
  1501.                     $this->td[$pos]['col'] = $col;
  1502.                     $this->td[$pos]['colspan'] = $span;
  1503.                     $this->td[$pos]['rowspan'] = $rowspan;
  1504.                     $this->td[$pos]['table'] = $tableid;
  1505.                     $this->td[$pos]['colwidth'] = 0;
  1506.                     $cells[] = $pos;
  1507.  
  1508.                     $col += $span;
  1509.                     break;
  1510.                 case "/TR":
  1511.                     break;
  1512.                 case "TABLE":
  1513.                     $spos = $pos;
  1514.                     $pos = $this->_findSubTables($pos)// skip sub tables
  1515.                     //echo "SKIPPED: $spos:$pos\n";
  1516.                     break;
  1517.                 case "/TABLE":
  1518.                     $done = 1;
  1519.                     break;
  1520.             }
  1521.             //echo "$pos\n";
  1522.             if ($done) break;
  1523.             $pos++;
  1524.         }
  1525.         // I now have 2 arrays: $table[row][col][pos|span|rowspan] and $td[pos][col|row]
  1526.         // and totalcols;
  1527.         //print_r($table); exit;
  1528.         // do do a guess on the stuff...
  1529.         if ($col > $totalcols) $totalcols = $col-1;
  1530.  
  1531.  
  1532.  
  1533.         if (!$totalcols) return;
  1534.  
  1535.  
  1536.         if ($hasCaption) {
  1537.             $pos = $hasCaption;
  1538.             $row = $this->td[$pos]['row'];
  1539.             $col = $this->td[$pos]['col'];
  1540.             $table[$row][$col]['span'] = $totalcols;
  1541.             $this->td[$pos]['colspan'] = $totalcols;
  1542.             $this->td[$pos]['table'] = $tableid;
  1543.             for ($i=1;$i<$totalcols;$i++)
  1544.                 $table[$row][$col+$i]['pos'] = $pos;
  1545.         }
  1546.  
  1547.  
  1548.         /* calculate the width */
  1549.         $colsizes = $this->_TABLEcalcWidth($colsizes, $maxwidth, $totalcols,$tableid);
  1550.  
  1551.  
  1552.  
  1553.  
  1554.         $x=$left;
  1555.         $row =0;
  1556.         for ($row =1; $row < ($totalrows + 1) ; $row++) {
  1557.             $cols = $table[$row];
  1558.             $x = $left;
  1559.             for ($col =1; $col < ($totalcols +1)$col++) {
  1560.                 $data = $cols[$col];
  1561.  
  1562.                 $td_id = $data['pos'];
  1563.                 // if it's the first occurance set left and right
  1564.                 if (($this->td[$td_id]['row'] == $row)
  1565.                     && ($this->td[$td_id]['col'] == $col)) {
  1566.                     $this->td[$td_id]['left'] = $x;
  1567.                     $this->td[$td_id]['right'] = $x;
  1568.                 }
  1569.  
  1570.                 if ($this->td[$td_id]['row'] == $row) {
  1571.                     $this->td[$td_id]['colwidth'] += $colsizes[$col];
  1572.                     $this->td[$td_id]['right']    += $colsizes[$col];
  1573.                 }
  1574.                 $table[$row][$col]['width'] =  $colsizes[$col];
  1575.                 //echo "R{$row}:C:{$col}:{$this->td[$td_id]['left']},{$this->td[$td_id]['right']}\n";
  1576.                 $x +=  $colsizes[$col];
  1577.  
  1578.                 /// for reference only
  1579.                 $this->td[$td_id]['totalcols'] = $totalcols;
  1580.             }
  1581.  
  1582.         }
  1583.         //if ($tableid ==142 )  {
  1584.         //     print_r($this->td);
  1585.         //       exit;
  1586.         //  }
  1587.         $this->tables[$tableid]['table'] =$table;
  1588.         $this->tables[$tableid]['cells'] =$cells;
  1589.         $this->tables[$tableid]['colsizes'] =$colsizes;
  1590.         $this->tables[$tableid]['left'] =$left;
  1591.         $this->tables[$tableid]['right'] =$left + $maxwidth;
  1592.         $this->tables[$tableid]['totalrows'] =$totalrows;
  1593.         $this->tables[$tableid]['totalcols'] =$totalcols;
  1594.         //print_r($this->tables); exit;
  1595.  
  1596.     }
  1597.  
  1598.     function _TABLEcalcWidth($cols,$width,$total,$tableid) { // calculate a tables column sizes
  1599.         $res = array();
  1600.         // add up the widths
  1601.         // and how many cells are used
  1602.         $sum =0;
  1603.         $empty =0;
  1604.         for ($i=1;$i<($total+1);$i++) {
  1605.             if (@$cols[$i]) {
  1606.                 $sum += $cols[$i];
  1607.             } else {
  1608.                 $empty++;
  1609.             }
  1610.         }
  1611.         $available = $width-$sum;
  1612.         $default =0;
  1613.         $factor = 1;
  1614.  
  1615.         if ($empty)  {
  1616.             $default = (int) ($available / $empty);
  1617.         } else {
  1618.             $factor = $width/$sum;
  1619.         }
  1620.         for ($i=1;$i<($total+1);$i++) {
  1621.             if (@$cols[$i]) {
  1622.                 $res[$i] = (int) ($cols[$i] * $factor);
  1623.             } else {
  1624.                 $res[$i] = (int) ($default * $factor);
  1625.             }
  1626.         }
  1627.         /*
  1628.         print_r(
  1629.             array(
  1630.                 'tableid' => $tableid,
  1631.                 'cols' =>$cols,
  1632.                 'width' =>$width,
  1633.                 'total' =>$total,
  1634.                 'result' =>$res,
  1635.                 'available' => $available,
  1636.                 'empty' => $empty,
  1637.                 'factor' => $factor,
  1638.                 'sum' => $sum
  1639.                 ));
  1640.         */
  1641.         return $res;
  1642.     }
  1643.  
  1644.  
  1645.     function _TABLErecalc($id,$end) { // recalculate a tables cell heights (called at </table>
  1646.         //$rowx[$row]['top'] =
  1647.         //$rowx[$row]['bottom'] =
  1648.         $table = $this->tables[$id]['table'];
  1649.         $top = $this->tables[$id]['top'];
  1650.         $totalrows = $this->tables[$id]['totalrows'];
  1651.         $totalcols = $this->tables[$id]['totalcols'];
  1652.  
  1653.         $this->tables[$id]['end'] = $end;
  1654.         //if ($id == 85) echo "$top"; exit;
  1655.         $rows[1]['top'] = $top;
  1656.         for ($row =1; $row < ( $totalrows + 1) ; $row++) {
  1657.             $cols = $table[$row];
  1658.  
  1659.             // row
  1660.             $height  = 0;
  1661.             for ($col =1; $col < ($totalcols + 1) ; $col++) {
  1662.                 $data = $cols[$col];
  1663.                 $td_id = $data['pos'];
  1664.                 $this->_TDcalcHeight($td_id);
  1665.  
  1666.                 // top - is it the first row
  1667.                 if (@$table[$row-1][$col]['pos'] != $td_id)
  1668.                     $this->td[$td_id]['top'] = $top;
  1669.  
  1670.  
  1671.                 // bottom = ?
  1672.                 if (@$table[$row+1][$col]['pos'] != $data['pos'])
  1673.                     if ($height < @$this->td[$td_id]['height'])
  1674.                         $height = $this->td[$td_id]['height'];
  1675.  
  1676.                 $bottom = $top + $height;
  1677.             }
  1678.  
  1679.             // set the bottom for all cols.
  1680.             for ($col =1; $col < ($totalcols+1)$col++) {
  1681.                 $data = $cols[$col];
  1682.                 $td_id = $data['pos'];
  1683.  
  1684.                 if (@$table[$row+1][$col]['pos'] != $td_id)
  1685.                     $this->td[$td_id]['bottom']  = $bottom;
  1686.  
  1687.                 $this->tables[$id]['table'][$row][$col]['td'] = &$this->td[$td_id];
  1688.             }
  1689.  
  1690.             //echo "ROW:$row:TOP:$top\n";
  1691.             $top = $bottom;
  1692.  
  1693.         }
  1694.         $this->tables[$id]['height'] = $bottom - $this->tables[$id]['top'];
  1695.         $this->tables[$id]['bottom'] = $bottom;
  1696.         $this->_TABLEmovelines($id);
  1697.         //print_r($this->tables); exit;
  1698.         //if ($end > 160) {
  1699.          //    print_r($this->tables);
  1700.         //    echo "$id::$end\n";
  1701.             //exit;
  1702.        // }
  1703.     }
  1704.     function _TDcalcHeight($id) { // calculate a TD's height based on the lines inside it.
  1705.         //if ($this->td[$id]['height']) return;
  1706.         $h=0;
  1707.         foreach ($this->td[$id]['lines'] as $lineid)
  1708.             $h += @$this->_lines[$lineid]['ascent'] + @$this->_lines[$lineid]['descent'];
  1709.         //if (!$h) $h=16;
  1710.         $this->td[$id]['height'] = $h;
  1711.     }
  1712.     function _TABLEmovelines($table) { // move all the lines in a TABLE & TD to correct locations (recursively)
  1713.         $cells = $this->tables[$table]['cells'];
  1714.         foreach($cells as $td) {
  1715.             $lines = $this->td[$td]['lines'];
  1716.             $top = $this->td[$td]['top'];
  1717.             foreach($lines as $line) {
  1718.                 //echo "UpdateLine:$line\n";
  1719.                 $this->_lines[$line]['top'] = $top;
  1720.                 $this->_calcLine($line);
  1721.  
  1722.                 if (@$subtable = $this->_lines[$line]['table']) {
  1723.                     $this->tables[$subtable ]['top'] = $top;
  1724.                     $this->_TABLErecalc($subtable, $this->tables[$subtable ]['end']);
  1725.                     $this->_calcLine($line);
  1726.                 }
  1727.                 if ($this->_lines[$line]['bottom'] < $this->_lines[$line]['top'] ) {
  1728.                     echo "BOTTOM LESS THAN TOP!\n";
  1729.                     print_r($lines);
  1730.                     exit;
  1731.                 }
  1732.                 $top = $this->_lines[$line]['bottom'];
  1733.             }
  1734.         }
  1735.         //$this->tables[$table]['bottom'] = $top;
  1736.         //$this->tables[$table]['height'] = $top - $this->tables[$table]['top'] ;
  1737.     }
  1738.  
  1739. }
  1740.  
  1741.  
  1742.  
  1743.  

Documentation generated on Mon, 11 Mar 2019 14:28:58 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.