cvs: phpdoc /scripts/rtlpatch HtmlExtParser.class.php HtmlParser.class.php rtlpatch.php
| From: | Moshe Doron | Date: | Thu, 22 May 2003 16:13:44 +0000 |
| Subject: | cvs: phpdoc /scripts/rtlpatch HtmlExtParser.class.php HtmlParser.class.php rtlpatch.php | ||
| Groups: | php.doc | ||
| Request: | Send a blank email to phpdoc+get-969353748@lists.php.net to get a copy of this message | ||
momo Thu May 22 12:13:44 2003 EDT
Added files:
/phpdoc/scripts/rtlpatch HtmlExtParser.class.php
HtmlParser.class.php rtlpatch.php
Log:
initial solution for the hebrew (arabic can easly use this code) problems, if no comments next
dayes, i'll merge it into the build.
Index: phpdoc/scripts/rtlpatch/HtmlExtParser.class.php +++ phpdoc/scripts/rtlpatch/HtmlExtParser.class.php <? /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP licience, | | that is bundled with this package in the file LICENCE and is | | avalible through the world wide web at | | http://www.php.net/license/2_02.txt. | | If uou did not receive a copy of the PHP license and are unable to | | obtain it through the world wide web, please send a note to | | license@php.net so we can mail you a copy immediately | +----------------------------------------------------------------------+ | Authors: Moshe Doron <momo@php.net> | +----------------------------------------------------------------------+ $Id: HtmlExtParser.class.php,v 1.1 2003/05/22 16:13:44 momo Exp $ */ class CHtmlExtParse extends CHtmlParse{ function CHtmlExtParse($data){ // make the html compatible to the parser: $data = str_replace("\r\n>",">",$data); $data = str_replace("\n>",">",$data); $this->CHtmlParse($data); } function get_hebrew(){ global $EHType,$HEType; $cnt = count($this->ATE); for($a=0;$a<$cnt;$a++){ $ret[$a] = ""; } // if there is hebrew on the page, we make rtl default $wrong_mode = "rtl"; $deflang = __HTML_FREE_ENGLISH__; $html = $HEType["html"]; if(isset($this->EBT[__HTML_FREE_HEBREW__]) && isset($this->EBT[$html])){ $this->ATE[$this->EBT[$html][0]]["dir"] = "rtl"; $wrong_mode = "ltr"; $deflang = __HTML_FREE_HEBREW__; } // fix the embeded php code: $pre = $HEType["pre"]; if(isset($this->EBT[$pre])){ for($a=0;$a<count($this->EBT[$pre]);$a++){ $this->ATE[$this->EBT[$pre][$a]]["dir"] = "ltr"; } } //fix the meta: $meta = $HEType["meta"]; if(isset($this->EBT[$meta])){ for($a=0;$a<count($this->EBT[$meta]);$a++){ $this->ATE[$this->EBT[$meta][$a]]["content"] = "text/html; charset=WINDOWS-1255"; } } for($a=1;$a<$cnt;$a++){ $tg = array_shift($this->ATE[$a]); if($tg<0) continue; if($tg>4) { $tag = $EHType[$tg]; $ret[$a] .= "<$tag"; foreach ($this->ATE[$a] as $key=>$value){ $ret[$a].=" $key='$value'"; } $ret[$a].=">"; if($this->ECE[$a]!=$a) $ret[$this->ECE[$a]] .= " </$tag>"; } else { if($tg != $deflang) { // if($a>1 && isset($ret[$a+1]) && ($this->ECE[$a-1] = $a+1)) { // //hack; remove later try to encapsulate the parent: // $ret[$a-1]="<span dir=$wrong_mode>".$ret[$a-1]; // $ret[$a].= $this->ATE[$a]["data"]; // $ret[$a+1]="</span>".$ret[$a+1]; // } else { // regular case: $ret[$a].="<span dir=$wrong_mode>{$this->ATE[$a]["data"]}</span>"; // } } else { $ret[$a].=$this->ATE[$a]["data"]; } } } return implode($ret,"\r\n"); } function unsetme(){ unset($this->data); unset($this->ATH); unset($this->EC); unset($this->EBT); } } Index: phpdoc/scripts/rtlpatch/HtmlParser.class.php +++ phpdoc/scripts/rtlpatch/HtmlParser.class.php <? /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP licience, | | that is bundled with this package in the file LICENCE and is | | avalible through the world wide web at | | http://www.php.net/license/2_02.txt. | | If uou did not receive a copy of the PHP license and are unable to | | obtain it through the world wide web, please send a note to | | license@php.net so we can mail you a copy immediately | +----------------------------------------------------------------------+ | Authors: Moshe Doron <momo@php.net> | +----------------------------------------------------------------------+ $Id: HtmlParser.class.php,v 1.1 2003/05/22 16:13:44 momo Exp $ */ define("__HTML_FREE_ENGLISH__",1); define("__HTML_FREE_HEBREW__",2); define("__HTML_UNKNOWN__",5); define("__HTML_COMMENT__",6); define("__HTML_PROCESS__",7); $HEType["a"] = 10; $HEType["acronym"] = 20; $HEType["address"] = 30; $HEType["applet"] = 40; $HEType["area"] = 50; $HEType["b"] = 60; $HEType["base"] = 70; $HEType["basefont"] = 80; $HEType["bdo"] = 90; $HEType["bgsound"] = 100; $HEType["big"] = 110; $HEType["blockquote"] = 120; $HEType["body"] = 130; $HEType["br"] = 140; $HEType["button"] = 150; $HEType["caption"] = 160; $HEType["cite"] = 170; $HEType["code"] = 180; $HEType["col"] = 190; $HEType["colgroup"] = 200; $HEType["dd"] = 210; $HEType["del"] = 220; $HEType["dfn"] = 230; $HEType["div"] = 240; $HEType["dl"] = 250; $HEType["dt"] = 260; $HEType["em"] = 270; $HEType["embed"] = 280; $HEType["fieldset"] = 290; $HEType["font"] = 300; $HEType["form"] = 310; $HEType["frame"] = 320; $HEType["frameset"] = 330; $HEType["h1"] = 340; $HEType["h2"] = 350; $HEType["h3"] = 360; $HEType["h4"] = 370; $HEType["h5"] = 380; $HEType["h6"] = 390; $HEType["h7"] = 400; $HEType["hr"] = 410; $HEType["i"] = 420; $HEType["iframe"] = 430; $HEType["ilayer"] = 440; $HEType["img"] = 450; $HEType["input"] = 460; $HEType["ins"] = 470; $HEType["kbd"] = 480; $HEType["label"] = 490; $HEType["layer"] = 500; $HEType["legend"] = 510; $HEType["li"] = 520; $HEType["link"] = 530; $HEType["mailto"] = 540; $HEType["map"] = 550; $HEType["marquee"] = 560; $HEType["meta"] = 570; $HEType["multicol"] = 580; $HEType["noframes"] = 590; $HEType["noscript"] = 600; $HEType["object"] = 610; $HEType["ol"] = 620; $HEType["option"]= 630; $HEType["p"] = 640; $HEType["param"] = 650; $HEType["pre"] = 660; $HEType["q"] = 670; $HEType["samp"] = 680; $HEType["script"] = 690; $HEType["select"] = 700; $HEType["small"] = 710; $HEType["sound"] = 720; $HEType["spacer"] = 730; $HEType["span"] = 740; $HEType["strong"] = 750; $HEType["style"] = 760; $HEType["sub"] = 770; $HEType["sup"] = 780; $HEType["table"] = 790; $HEType["tbody"] = 800; $HEType["td"] = 810; $HEType["textarea"] = 820; $HEType["tfoot"] = 830; $HEType["th"] = 840; $HEType["thead"] = 850; $HEType["tr"] = 860; $HEType["tt"] = 870; $HEType["ul"] = 880; $HEType["var"] = 890; $HEType["blink"] = 900; $HEType["center"] = 910; $HEType["dir"] = 920; $HEType["head"] = 930; $HEType["html"] = 940; $HEType["menu"] = 950; $HEType["nobr"] = 960; $HEType["noembed"] = 970; $HEType["nolayer"] = 980; $HEType["plaintext"] = 990; $HEType["s"] = 1000; $HEType["server"] = 1010; $HEType["strike"] = 1020; $HEType["title"] = 1030; $HEType["u"] = 1040; $HEType["uc"] = 1045; $HEType["wbr"] = 1050; $HEType["xmp"] = 1060; foreach ($HEType as $key=>$value){ $EHType[$value] = $key; } class CHtmlParse{ /* * Parser functions: * * html_parse() - top level entry point. * parse_start() - deals with start tags <A...> * parse_script - deals with <script tag * parse_end() - deals with end tags </...> * plaintext() * plaintext_english() * plaintext_hebrew() * parse_decl() - deals with declarations <!...> * parse_comment() - deals with <!-- ... --> * parse_marked_section - deals with <![ ... [ ... ]]> * parse_process() - deals with process instructions <?...> * parse_null() - deals with anything else <....> */ var $len; var $data; var $pos; var $encoding = "hebrew"; var $ATE; //AllTheElements ordering by location into the document. var $EBT; //ElementsByType. var $ECE; //Elements close by $ATE id. var $NOE; //NumberOfElements. function CHtmlParse($data){ $this->len = strlen($data); $this->data = $data; $this->pos = 0; $this->ATE[] = false; $this->NOE = 0; $this->map_all(); $this->end_map(); // var_dump($this); } function map_all(){ $this->move_to_next_notrim(); while($this->pos < $this->len){ //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); if($this->data{$this->pos} != "<"){ if ($x = $this->plaintext()) $this->map(&$x); }else if(++$this->pos < $this->len){ $p = $this->data{$this->pos}; if($p == "!"){ $this->parse_decl(); //$this->map($this->parse_decl()); }else if($p == "/"){ $this->map($this->parse_end()); $this->pos++; }else if($p == "?"){ $this->map($this->parse_process()); }else{ //debuginfo("opening"); $this->map($this->parse_start(&$endfl),$endfl); $this->pos++; } } $this->move_to_next_notrim(); } } function parse_start($endfl){ global $HEType; //fining the <html> tag type: $last = $this->pos; $this->move_to_prop_end(); $return["w4htype"] = $HEType[strtolower(substr($this->data,$last,$this->pos-$last))]; // looping till getting all the elements: while(1){ //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); //debuginfo($return); $offset = $this->move_to_prop_start(); if($offset < 0) { $endfl = $offset+1; //debuginfo("endfl: $endfl"); return $return; } $last = $this->pos; $offset = $this->move_to_propname_end(); if($offset < 0){ $return[strtolower(substr($this->data,$last,$this->pos-$last-$offset))]="nop"; $endfl = $offset+1; //debuginfo("endfl: $endfl"); return $return; } $prop = strtolower(substr($this->data,$last,$this->pos-$last)); $this->move_to_next_notrim(); if($this->data{$this->pos} == '='){ // if property have value: $this->move_to_next_notrim(); $last=++$this->pos; $pq = $this->data{$this->pos}; if($pq =="\'"||$pq =="\""){//on quotation $last = ++$this->pos; if(!$ret = $this->move_to_character($pq)){ $this->add_error("tag property have no close -$pq- quetation"); } $return[$prop] = substr($this->data,$last,$this->pos-$last); $this->pos++; }else{ //on no quotation $offset = $this->move_to_prop_end(); if($offset < 0){ // '>' found: $return[$prop] = substr($this->data,$last,$this->pos-$last+$offset+1); $endfl = $offset+1; //tag have '/' close. return $return; } $return[$prop] = substr($this->data,$last,$this->pos-$last); } } } } function parse_end(){ global $HEType; $last = ++$this->pos; $this->move_to_character(">"); $tmp = strtolower(substr($this->data,$last,$this->pos-$last)); if(isset($HEType[$tmp])){ $return["w4htype"] = -$HEType[$tmp]; }else{ $return["w4htype"] = -__HTML_UNKNOWN__; } return $return; } function parse_decl(){ $this->pos++; //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); //debuginfo(substr($this->data,$this->pos-10,20)); if($this->pos <$this->len){ if($this->data{$this->pos} == "-" && $this->data{$this->pos+1} == "-"){ return $this->parse_comment(); }else if($this->data{$this->pos} == "["){ return $this->map($this->parse_marked_section()); }else{ $this->parse_null(); } }else{ $this->add_error("broken remark",0); return false; } } function parse_comment(){ $last = $this->pos; while ($this->pos+1 < $this->len){ //debuginfo("yep: ".$this->data{$this->pos}.$this->data{$this->pos+1}.$this->data{$this->pos+2}); if($this->data{$this->pos} == "-" && $this->data{$this->pos+1} == "-" && $this->data{$this->pos+2} == ">"){ $oldlen = $this->len; $this->len = $this->pos; $this->pos = $last; $tmp = array("w4htype"=>__HTML_COMMENT__); $this->map($tmp); $this->map_all(); $this->len = $oldlen; $tmp = array("w4htype"=>__HTML_COMMENT__*(-1)); $this->map($tmp); $this->pos+=3; return 1; } $this->pos++; } $this->add_error("comment on $last char haven't closed"); return array("w4htype"=>__HTML_COMMENT__,"data"=>substr($this->data,$last,$this->pos-$last)); } function parse_marked_section(){ $last = $this->pos; while ($this->pos <= $this->len){ if($this->data{$this->pos} == ">"){ $return = array("w4htype"=>__HTML_MARKED__,"data"=>substr($this->data,$last,$this->pos-$last)); $this->pos++; return $return; } $this->pos++; } $this->add_error("marked on $last char haven't closed"); return array("w4htype"=>__HTML_MARKED__,"data"=>substr($this->data,$last,$this->pos-$last)); } function parse_process(){ $last = $this->pos; while ($this->pos < $this->len){ if($this->data{$this->pos} == "?" && $this->data{$this->pos+1} == ">"){ $return = array("w4htype"=>__HTML_PROCESS__,"data"=>substr($this->data,$last,$this->pos-$last)); $this->pos+=3; return $return; } $this->pos++; } $this->add_error("process on $last char haven't closed"); return array("w4htype"=>__HTML_PROCESS__,"data"=>substr($this->data,$last,$this->pos-$last)); } function parse_null(){ $this->add_error("NULL found",-1); $this->move_to_character(">"); $this->pos++; //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); } function plaintext(){ $this->move_to_next_notrim(); return @call_user_method("plaintext_".$this->encoding,$this); } function plaintext_english(){ $last = $this->pos; $encoding = __HTML_FREE_ENGLISH__; while($this->pos < $this->len && $this->data{$this->pos} != '<'){ $this->pos++; } if($last == $this->pos) return false; return array("w4htype"=>$encoding,"data"=>substr($this->data,$last,$this->pos-$last)); } function plaintext_hebrew(){ $last = $this->pos; $encoding= __HTML_FREE_ENGLISH__; while($this->pos < $this->len && $this->data{$this->pos} != '<'){ if(ord($this->data{$this->pos}) >= 224 && ord($this->data{$this->pos}) <= 250) $encoding = __HTML_FREE_HEBREW__; $this->pos++; } if($last == $this->pos) return false; return array("w4htype"=>$encoding,"data"=>substr($this->data,$last,$this->pos-$last)); } function map(&$element,$endfl=0){ $type = $element["w4htype"]; //debuginfo("<font color=blue>type: $type</font>"); //debuginfo($element); $this->NOE++; $this->ATE[$this->NOE] = $element; if($type>0) $this->EBT[$type][] = $this->NOE; if($endfl){ //echo "<font color=blue>type: $type</font>"; $this->ECE[$this->NOE] = $this->NOE; }else if($type >= 10){ //echo "<font color=blue>type: $type</font>"; $this->maptmp[$type][] = $this->NOE; //debuginfo($this->maptmp[$type]); }else if($type <= -10){ //debuginfo($type); //debuginfo($this->maptmp[$type]); if($id = array_pop($this->maptmp[-$type])){ $this->ECE[$id] = $this->NOE; }else{ //if more close $this->ECE[$this->NOE] = $this->NOE; $this->add_error("more close tag (code $type) then open"); } } //echo "<font color=green>";var_dump($this->maptmp);echo "</font>"; } function end_map(){ for($a=1;$a<=$this->NOE;$a++){ if(!isset($this->ECE[$a])) $this->ECE[$a] = $a; } unset($this->maptmp); } function add_error($msg,$level=0){ $this->errors[] = array("msg"=>$msg,"level"=>$level); if($level > 1000){ $this->show_errors(); //var_dump($this); } //if($level >= -1) exit; } function show_errors(){ $count = count($this->errors); for($a=0;$a<$count;$a++){ echo $this->errors[$a]["msg"]."\r\n<br>"; } } // ---------------------------------------------------------- // pointer checking & moving function to convert into macroz: // ---------------------------------------------------------- function move_to_character($char){ while($this->pos++ < $this->len){ if($this->data{$this->pos} == $char){ return $this->pos; } } $this->add_error("no $char ascii:".ord($char).": found"); return 0; } function move_to_prop_start(){ while($this->pos < $this->len){ //debuginfo($this->data{$this->pos}); if (!is_html_trim($this->data{$this->pos})){ //debuginfo($this->data{$this->pos}); if($this->data{$this->pos}==">"){ //debuginfo("ahe be chigale me"); return -1; // standard html tag. }else if($this->data{$this->pos} == "/"){ if (($this->pos+1 < $this->len) && ($this->data{$this->pos+1} == ">")){ $this->pos++; return -2; // close xhtml\xml tag. }else{ $this->add_error("tag property cant start with '/'"); } } return $this->data{$this->pos}; } $this->pos++; } $this->add_error("string end before prop start"); return 0; } // this function is used to find out if the property name\value have closed // return -2 on simple close // return -1 on '/' style (xhtml\xml) close // return 0 on unexpected string end & generating an error. // return character ascii code else. function move_to_prop_end(){ while($this->pos < $this->len){ //debuginfo($this->data{$this->pos}); if (is_html_trim($this->data{$this->pos})){ return $this->data{$this->pos}; }else if($this->data{$this->pos}==">"){ if($this->data{$this->pos-2} == "/") return -1; // close xhtml\xml tag. else return -1; // standard html tag. } $this->pos++; } $this->add_error("string end before prop end"); return 0; } function move_to_propname_end(){ while($this->pos < $this->len){ //debuginfo($this->data{$this->pos}); if (is_html_trim($this->data{$this->pos})||$this->data{$this->pos}=="="){ return $this->data{$this->pos}; }else if($this->data{$this->pos}==">"){ if($this->data{$this->pos-1} == "/") return -2; // close xhtml\xml tag. else return -1; // standard html tag. } $this->pos++; } $this->add_error("string end before prop end"); return 0; } function move_to_next_notrim(){ while($this->pos < $this->len){ if (!is_html_trim($this->data{$this->pos})){ return $this->data{$this->pos}; } $this->pos++; } return ($this->pos < $this->len)?$this->data{$this->pos}:false; } function move_to_next_trim(){ while($this->pos < $this->len){ if (is_html_trim($this->data{$this->pos})){ return $this->data{$this->pos}; } $this->pos++; } $this->add_error("string end before trim"); return 0; } } //convert to macro function is_html_trim($p){ if ($p==" "||$p=="\n"||$p=="\r"||$p=="\t"||$p=="\v"|| $p=="\0") return true; else return false; } ?> Index: phpdoc/scripts/rtlpatch/rtlpatch.php +++ phpdoc/scripts/rtlpatch/rtlpatch.php <? /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP licience, | | that is bundled with this package in the file LICENCE and is | | avalible through the world wide web at | | http://www.php.net/license/2_02.txt. | | If uou did not receive a copy of the PHP license and are unable to | | obtain it through the world wide web, please send a note to | | license@php.net so we can mail you a copy immediately | +----------------------------------------------------------------------+ | Authors: Moshe Doron <momo@php.net> | +----------------------------------------------------------------------+ $Id: rtlpatch.php,v 1.1 2003/05/22 16:13:44 momo Exp $ */ /* REQUIRES: PHP 4.3.2 or higher This file is temporary patch allow the hebrew and (in the future arabic) html generation. this script have to be run after the build proccess. giving the docs path it's edit the files and add dir=rtl,ltr where needed CAUTION! be careful here! that's 'll replace all the file with "rtl" version, so u may want first try it on partial directory. Usage: php rtlpatch path/to/html/directory script runing time on my box is about 15% then the build time, not so big deal, that'll force me rewrite the parser in C ;) */ error_reporting(2047); $mypath = $_SERVER["PWD"]; $docroot = $_SERVER["argv"][1]; require "$mypath/HtmlParser.class.php"; require "$mypath/HtmlExtParser.class.php"; // clean the new files, use for debuging: //cleanmyfiles($docroot); exit; loopfiles($docroot); //u may choose to comment the following line for not lose the orginal files. replacemyfiles($docroot); // ------------------- Functions: ----------------- function loopfiles($dirName){ $qudir=array(); if(!($d = @dir($dirName))){ mysyslog("Die: $dirName doesn't exists or have no read permission!\n"); exit; } while($entry = $d->read()) { if ($entry != "." && $entry != "..") { if (is_dir($dirName."/".$entry)) { $qudir[] = $entry; }else{ if(eregi(".html",$entry)) { $qufile[] = $dirName."/".$entry; } } } } $count = count($qufile); for($a=0;$a<$count;$a++){ fix_file($qufile[$a]); } $count = count($qudir); for($a=0;$a<$count;$a++){ loopfiles($dirName."/".$qudir[$a]); } $d->close(); } function fix_file($file){ mysyslog("start fixing $file"); $txt = mygetfile($file); $tree = new CHtmlExtParse($txt); //var_dump($tree);exit; $newtext = $tree->get_hebrew(); savemyfile($file,$newtext); $tree->unsetme(); unset($tree); //die("\n---".__LINE__."---".__FILE__); } function mygetfile($file){ if(!($f= @fopen($file,"rb"))){ mysyslog("$file doesn't exists or have no read permission"); return false; } $ret = ""; while($buf = fread($f,4096)){ $ret.=$buf; } fclose($f); return $ret; } function savemyfile($file,$text){ if(!($f= fopen("$file._fixed.html","wb"))){ mysyslog("$file doesn't exists or have no write permission"); return false; } fwrite($f,$text); fclose($f); return true; } function replacemyfiles($dirName){ mysyslog("replaceing the old files with new one; one way ticket..."); if(!($d = @dir($dirName))){ mysyslog("Die: $dirName doesn't exists or have no read permission!\n"); exit; } while($entry = $d->read()) { if ($entry != "." && $entry != "..") { $dentry = $dirName."/".$entry; if (is_dir($dentry)) { replacemyfiles($dentry); }else{ if(strpos($entry,"._fixed.html")) { $newfile = str_replace("._fixed.html","",$dentry); unlink($newfile); rename($dentry,$newfile); } } } } $d->close(); } function mysyslog($s){ echo "$s\n"; } ?>
Index: phpdoc/scripts/rtlpatch/HtmlExtParser.class.php +++ phpdoc/scripts/rtlpatch/HtmlExtParser.class.php <? /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP licience, | | that is bundled with this package in the file LICENCE and is | | avalible through the world wide web at | | http://www.php.net/license/2_02.txt. | | If uou did not receive a copy of the PHP license and are unable to | | obtain it through the world wide web, please send a note to | | license@php.net so we can mail you a copy immediately | +----------------------------------------------------------------------+ | Authors: Moshe Doron <momo@php.net> | +----------------------------------------------------------------------+ $Id: HtmlExtParser.class.php,v 1.1 2003/05/22 16:13:44 momo Exp $ */ class CHtmlExtParse extends CHtmlParse{ function CHtmlExtParse($data){ // make the html compatible to the parser: $data = str_replace("\r\n>",">",$data); $data = str_replace("\n>",">",$data); $this->CHtmlParse($data); } function get_hebrew(){ global $EHType,$HEType; $cnt = count($this->ATE); for($a=0;$a<$cnt;$a++){ $ret[$a] = ""; } // if there is hebrew on the page, we make rtl default $wrong_mode = "rtl"; $deflang = __HTML_FREE_ENGLISH__; $html = $HEType["html"]; if(isset($this->EBT[__HTML_FREE_HEBREW__]) && isset($this->EBT[$html])){ $this->ATE[$this->EBT[$html][0]]["dir"] = "rtl"; $wrong_mode = "ltr"; $deflang = __HTML_FREE_HEBREW__; } // fix the embeded php code: $pre = $HEType["pre"]; if(isset($this->EBT[$pre])){ for($a=0;$a<count($this->EBT[$pre]);$a++){ $this->ATE[$this->EBT[$pre][$a]]["dir"] = "ltr"; } } //fix the meta: $meta = $HEType["meta"]; if(isset($this->EBT[$meta])){ for($a=0;$a<count($this->EBT[$meta]);$a++){ $this->ATE[$this->EBT[$meta][$a]]["content"] = "text/html; charset=WINDOWS-1255"; } } for($a=1;$a<$cnt;$a++){ $tg = array_shift($this->ATE[$a]); if($tg<0) continue; if($tg>4) { $tag = $EHType[$tg]; $ret[$a] .= "<$tag"; foreach ($this->ATE[$a] as $key=>$value){ $ret[$a].=" $key='$value'"; } $ret[$a].=">"; if($this->ECE[$a]!=$a) $ret[$this->ECE[$a]] .= " </$tag>"; } else { if($tg != $deflang) { // if($a>1 && isset($ret[$a+1]) && ($this->ECE[$a-1] = $a+1)) { // //hack; remove later try to encapsulate the parent: // $ret[$a-1]="<span dir=$wrong_mode>".$ret[$a-1]; // $ret[$a].= $this->ATE[$a]["data"]; // $ret[$a+1]="</span>".$ret[$a+1]; // } else { // regular case: $ret[$a].="<span dir=$wrong_mode>{$this->ATE[$a]["data"]}</span>"; // } } else { $ret[$a].=$this->ATE[$a]["data"]; } } } return implode($ret,"\r\n"); } function unsetme(){ unset($this->data); unset($this->ATH); unset($this->EC); unset($this->EBT); } } Index: phpdoc/scripts/rtlpatch/HtmlParser.class.php +++ phpdoc/scripts/rtlpatch/HtmlParser.class.php <? /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP licience, | | that is bundled with this package in the file LICENCE and is | | avalible through the world wide web at | | http://www.php.net/license/2_02.txt. | | If uou did not receive a copy of the PHP license and are unable to | | obtain it through the world wide web, please send a note to | | license@php.net so we can mail you a copy immediately | +----------------------------------------------------------------------+ | Authors: Moshe Doron <momo@php.net> | +----------------------------------------------------------------------+ $Id: HtmlParser.class.php,v 1.1 2003/05/22 16:13:44 momo Exp $ */ define("__HTML_FREE_ENGLISH__",1); define("__HTML_FREE_HEBREW__",2); define("__HTML_UNKNOWN__",5); define("__HTML_COMMENT__",6); define("__HTML_PROCESS__",7); $HEType["a"] = 10; $HEType["acronym"] = 20; $HEType["address"] = 30; $HEType["applet"] = 40; $HEType["area"] = 50; $HEType["b"] = 60; $HEType["base"] = 70; $HEType["basefont"] = 80; $HEType["bdo"] = 90; $HEType["bgsound"] = 100; $HEType["big"] = 110; $HEType["blockquote"] = 120; $HEType["body"] = 130; $HEType["br"] = 140; $HEType["button"] = 150; $HEType["caption"] = 160; $HEType["cite"] = 170; $HEType["code"] = 180; $HEType["col"] = 190; $HEType["colgroup"] = 200; $HEType["dd"] = 210; $HEType["del"] = 220; $HEType["dfn"] = 230; $HEType["div"] = 240; $HEType["dl"] = 250; $HEType["dt"] = 260; $HEType["em"] = 270; $HEType["embed"] = 280; $HEType["fieldset"] = 290; $HEType["font"] = 300; $HEType["form"] = 310; $HEType["frame"] = 320; $HEType["frameset"] = 330; $HEType["h1"] = 340; $HEType["h2"] = 350; $HEType["h3"] = 360; $HEType["h4"] = 370; $HEType["h5"] = 380; $HEType["h6"] = 390; $HEType["h7"] = 400; $HEType["hr"] = 410; $HEType["i"] = 420; $HEType["iframe"] = 430; $HEType["ilayer"] = 440; $HEType["img"] = 450; $HEType["input"] = 460; $HEType["ins"] = 470; $HEType["kbd"] = 480; $HEType["label"] = 490; $HEType["layer"] = 500; $HEType["legend"] = 510; $HEType["li"] = 520; $HEType["link"] = 530; $HEType["mailto"] = 540; $HEType["map"] = 550; $HEType["marquee"] = 560; $HEType["meta"] = 570; $HEType["multicol"] = 580; $HEType["noframes"] = 590; $HEType["noscript"] = 600; $HEType["object"] = 610; $HEType["ol"] = 620; $HEType["option"]= 630; $HEType["p"] = 640; $HEType["param"] = 650; $HEType["pre"] = 660; $HEType["q"] = 670; $HEType["samp"] = 680; $HEType["script"] = 690; $HEType["select"] = 700; $HEType["small"] = 710; $HEType["sound"] = 720; $HEType["spacer"] = 730; $HEType["span"] = 740; $HEType["strong"] = 750; $HEType["style"] = 760; $HEType["sub"] = 770; $HEType["sup"] = 780; $HEType["table"] = 790; $HEType["tbody"] = 800; $HEType["td"] = 810; $HEType["textarea"] = 820; $HEType["tfoot"] = 830; $HEType["th"] = 840; $HEType["thead"] = 850; $HEType["tr"] = 860; $HEType["tt"] = 870; $HEType["ul"] = 880; $HEType["var"] = 890; $HEType["blink"] = 900; $HEType["center"] = 910; $HEType["dir"] = 920; $HEType["head"] = 930; $HEType["html"] = 940; $HEType["menu"] = 950; $HEType["nobr"] = 960; $HEType["noembed"] = 970; $HEType["nolayer"] = 980; $HEType["plaintext"] = 990; $HEType["s"] = 1000; $HEType["server"] = 1010; $HEType["strike"] = 1020; $HEType["title"] = 1030; $HEType["u"] = 1040; $HEType["uc"] = 1045; $HEType["wbr"] = 1050; $HEType["xmp"] = 1060; foreach ($HEType as $key=>$value){ $EHType[$value] = $key; } class CHtmlParse{ /* * Parser functions: * * html_parse() - top level entry point. * parse_start() - deals with start tags <A...> * parse_script - deals with <script tag * parse_end() - deals with end tags </...> * plaintext() * plaintext_english() * plaintext_hebrew() * parse_decl() - deals with declarations <!...> * parse_comment() - deals with <!-- ... --> * parse_marked_section - deals with <![ ... [ ... ]]> * parse_process() - deals with process instructions <?...> * parse_null() - deals with anything else <....> */ var $len; var $data; var $pos; var $encoding = "hebrew"; var $ATE; //AllTheElements ordering by location into the document. var $EBT; //ElementsByType. var $ECE; //Elements close by $ATE id. var $NOE; //NumberOfElements. function CHtmlParse($data){ $this->len = strlen($data); $this->data = $data; $this->pos = 0; $this->ATE[] = false; $this->NOE = 0; $this->map_all(); $this->end_map(); // var_dump($this); } function map_all(){ $this->move_to_next_notrim(); while($this->pos < $this->len){ //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); if($this->data{$this->pos} != "<"){ if ($x = $this->plaintext()) $this->map(&$x); }else if(++$this->pos < $this->len){ $p = $this->data{$this->pos}; if($p == "!"){ $this->parse_decl(); //$this->map($this->parse_decl()); }else if($p == "/"){ $this->map($this->parse_end()); $this->pos++; }else if($p == "?"){ $this->map($this->parse_process()); }else{ //debuginfo("opening"); $this->map($this->parse_start(&$endfl),$endfl); $this->pos++; } } $this->move_to_next_notrim(); } } function parse_start($endfl){ global $HEType; //fining the <html> tag type: $last = $this->pos; $this->move_to_prop_end(); $return["w4htype"] = $HEType[strtolower(substr($this->data,$last,$this->pos-$last))]; // looping till getting all the elements: while(1){ //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); //debuginfo($return); $offset = $this->move_to_prop_start(); if($offset < 0) { $endfl = $offset+1; //debuginfo("endfl: $endfl"); return $return; } $last = $this->pos; $offset = $this->move_to_propname_end(); if($offset < 0){ $return[strtolower(substr($this->data,$last,$this->pos-$last-$offset))]="nop"; $endfl = $offset+1; //debuginfo("endfl: $endfl"); return $return; } $prop = strtolower(substr($this->data,$last,$this->pos-$last)); $this->move_to_next_notrim(); if($this->data{$this->pos} == '='){ // if property have value: $this->move_to_next_notrim(); $last=++$this->pos; $pq = $this->data{$this->pos}; if($pq =="\'"||$pq =="\""){//on quotation $last = ++$this->pos; if(!$ret = $this->move_to_character($pq)){ $this->add_error("tag property have no close -$pq- quetation"); } $return[$prop] = substr($this->data,$last,$this->pos-$last); $this->pos++; }else{ //on no quotation $offset = $this->move_to_prop_end(); if($offset < 0){ // '>' found: $return[$prop] = substr($this->data,$last,$this->pos-$last+$offset+1); $endfl = $offset+1; //tag have '/' close. return $return; } $return[$prop] = substr($this->data,$last,$this->pos-$last); } } } } function parse_end(){ global $HEType; $last = ++$this->pos; $this->move_to_character(">"); $tmp = strtolower(substr($this->data,$last,$this->pos-$last)); if(isset($HEType[$tmp])){ $return["w4htype"] = -$HEType[$tmp]; }else{ $return["w4htype"] = -__HTML_UNKNOWN__; } return $return; } function parse_decl(){ $this->pos++; //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); //debuginfo(substr($this->data,$this->pos-10,20)); if($this->pos <$this->len){ if($this->data{$this->pos} == "-" && $this->data{$this->pos+1} == "-"){ return $this->parse_comment(); }else if($this->data{$this->pos} == "["){ return $this->map($this->parse_marked_section()); }else{ $this->parse_null(); } }else{ $this->add_error("broken remark",0); return false; } } function parse_comment(){ $last = $this->pos; while ($this->pos+1 < $this->len){ //debuginfo("yep: ".$this->data{$this->pos}.$this->data{$this->pos+1}.$this->data{$this->pos+2}); if($this->data{$this->pos} == "-" && $this->data{$this->pos+1} == "-" && $this->data{$this->pos+2} == ">"){ $oldlen = $this->len; $this->len = $this->pos; $this->pos = $last; $tmp = array("w4htype"=>__HTML_COMMENT__); $this->map($tmp); $this->map_all(); $this->len = $oldlen; $tmp = array("w4htype"=>__HTML_COMMENT__*(-1)); $this->map($tmp); $this->pos+=3; return 1; } $this->pos++; } $this->add_error("comment on $last char haven't closed"); return array("w4htype"=>__HTML_COMMENT__,"data"=>substr($this->data,$last,$this->pos-$last)); } function parse_marked_section(){ $last = $this->pos; while ($this->pos <= $this->len){ if($this->data{$this->pos} == ">"){ $return = array("w4htype"=>__HTML_MARKED__,"data"=>substr($this->data,$last,$this->pos-$last)); $this->pos++; return $return; } $this->pos++; } $this->add_error("marked on $last char haven't closed"); return array("w4htype"=>__HTML_MARKED__,"data"=>substr($this->data,$last,$this->pos-$last)); } function parse_process(){ $last = $this->pos; while ($this->pos < $this->len){ if($this->data{$this->pos} == "?" && $this->data{$this->pos+1} == ">"){ $return = array("w4htype"=>__HTML_PROCESS__,"data"=>substr($this->data,$last,$this->pos-$last)); $this->pos+=3; return $return; } $this->pos++; } $this->add_error("process on $last char haven't closed"); return array("w4htype"=>__HTML_PROCESS__,"data"=>substr($this->data,$last,$this->pos-$last)); } function parse_null(){ $this->add_error("NULL found",-1); $this->move_to_character(">"); $this->pos++; //debuginfo("pos is: ". $this->pos. ", character is: " .$this->data{$this->pos}); } function plaintext(){ $this->move_to_next_notrim(); return @call_user_method("plaintext_".$this->encoding,$this); } function plaintext_english(){ $last = $this->pos; $encoding = __HTML_FREE_ENGLISH__; while($this->pos < $this->len && $this->data{$this->pos} != '<'){ $this->pos++; } if($last == $this->pos) return false; return array("w4htype"=>$encoding,"data"=>substr($this->data,$last,$this->pos-$last)); } function plaintext_hebrew(){ $last = $this->pos; $encoding= __HTML_FREE_ENGLISH__; while($this->pos < $this->len && $this->data{$this->pos} != '<'){ if(ord($this->data{$this->pos}) >= 224 && ord($this->data{$this->pos}) <= 250) $encoding = __HTML_FREE_HEBREW__; $this->pos++; } if($last == $this->pos) return false; return array("w4htype"=>$encoding,"data"=>substr($this->data,$last,$this->pos-$last)); } function map(&$element,$endfl=0){ $type = $element["w4htype"]; //debuginfo("<font color=blue>type: $type</font>"); //debuginfo($element); $this->NOE++; $this->ATE[$this->NOE] = $element; if($type>0) $this->EBT[$type][] = $this->NOE; if($endfl){ //echo "<font color=blue>type: $type</font>"; $this->ECE[$this->NOE] = $this->NOE; }else if($type >= 10){ //echo "<font color=blue>type: $type</font>"; $this->maptmp[$type][] = $this->NOE; //debuginfo($this->maptmp[$type]); }else if($type <= -10){ //debuginfo($type); //debuginfo($this->maptmp[$type]); if($id = array_pop($this->maptmp[-$type])){ $this->ECE[$id] = $this->NOE; }else{ //if more close $this->ECE[$this->NOE] = $this->NOE; $this->add_error("more close tag (code $type) then open"); } } //echo "<font color=green>";var_dump($this->maptmp);echo "</font>"; } function end_map(){ for($a=1;$a<=$this->NOE;$a++){ if(!isset($this->ECE[$a])) $this->ECE[$a] = $a; } unset($this->maptmp); } function add_error($msg,$level=0){ $this->errors[] = array("msg"=>$msg,"level"=>$level); if($level > 1000){ $this->show_errors(); //var_dump($this); } //if($level >= -1) exit; } function show_errors(){ $count = count($this->errors); for($a=0;$a<$count;$a++){ echo $this->errors[$a]["msg"]."\r\n<br>"; } } // ---------------------------------------------------------- // pointer checking & moving function to convert into macroz: // ---------------------------------------------------------- function move_to_character($char){ while($this->pos++ < $this->len){ if($this->data{$this->pos} == $char){ return $this->pos; } } $this->add_error("no $char ascii:".ord($char).": found"); return 0; } function move_to_prop_start(){ while($this->pos < $this->len){ //debuginfo($this->data{$this->pos}); if (!is_html_trim($this->data{$this->pos})){ //debuginfo($this->data{$this->pos}); if($this->data{$this->pos}==">"){ //debuginfo("ahe be chigale me"); return -1; // standard html tag. }else if($this->data{$this->pos} == "/"){ if (($this->pos+1 < $this->len) && ($this->data{$this->pos+1} == ">")){ $this->pos++; return -2; // close xhtml\xml tag. }else{ $this->add_error("tag property cant start with '/'"); } } return $this->data{$this->pos}; } $this->pos++; } $this->add_error("string end before prop start"); return 0; } // this function is used to find out if the property name\value have closed // return -2 on simple close // return -1 on '/' style (xhtml\xml) close // return 0 on unexpected string end & generating an error. // return character ascii code else. function move_to_prop_end(){ while($this->pos < $this->len){ //debuginfo($this->data{$this->pos}); if (is_html_trim($this->data{$this->pos})){ return $this->data{$this->pos}; }else if($this->data{$this->pos}==">"){ if($this->data{$this->pos-2} == "/") return -1; // close xhtml\xml tag. else return -1; // standard html tag. } $this->pos++; } $this->add_error("string end before prop end"); return 0; } function move_to_propname_end(){ while($this->pos < $this->len){ //debuginfo($this->data{$this->pos}); if (is_html_trim($this->data{$this->pos})||$this->data{$this->pos}=="="){ return $this->data{$this->pos}; }else if($this->data{$this->pos}==">"){ if($this->data{$this->pos-1} == "/") return -2; // close xhtml\xml tag. else return -1; // standard html tag. } $this->pos++; } $this->add_error("string end before prop end"); return 0; } function move_to_next_notrim(){ while($this->pos < $this->len){ if (!is_html_trim($this->data{$this->pos})){ return $this->data{$this->pos}; } $this->pos++; } return ($this->pos < $this->len)?$this->data{$this->pos}:false; } function move_to_next_trim(){ while($this->pos < $this->len){ if (is_html_trim($this->data{$this->pos})){ return $this->data{$this->pos}; } $this->pos++; } $this->add_error("string end before trim"); return 0; } } //convert to macro function is_html_trim($p){ if ($p==" "||$p=="\n"||$p=="\r"||$p=="\t"||$p=="\v"|| $p=="\0") return true; else return false; } ?> Index: phpdoc/scripts/rtlpatch/rtlpatch.php +++ phpdoc/scripts/rtlpatch/rtlpatch.php <? /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP licience, | | that is bundled with this package in the file LICENCE and is | | avalible through the world wide web at | | http://www.php.net/license/2_02.txt. | | If uou did not receive a copy of the PHP license and are unable to | | obtain it through the world wide web, please send a note to | | license@php.net so we can mail you a copy immediately | +----------------------------------------------------------------------+ | Authors: Moshe Doron <momo@php.net> | +----------------------------------------------------------------------+ $Id: rtlpatch.php,v 1.1 2003/05/22 16:13:44 momo Exp $ */ /* REQUIRES: PHP 4.3.2 or higher This file is temporary patch allow the hebrew and (in the future arabic) html generation. this script have to be run after the build proccess. giving the docs path it's edit the files and add dir=rtl,ltr where needed CAUTION! be careful here! that's 'll replace all the file with "rtl" version, so u may want first try it on partial directory. Usage: php rtlpatch path/to/html/directory script runing time on my box is about 15% then the build time, not so big deal, that'll force me rewrite the parser in C ;) */ error_reporting(2047); $mypath = $_SERVER["PWD"]; $docroot = $_SERVER["argv"][1]; require "$mypath/HtmlParser.class.php"; require "$mypath/HtmlExtParser.class.php"; // clean the new files, use for debuging: //cleanmyfiles($docroot); exit; loopfiles($docroot); //u may choose to comment the following line for not lose the orginal files. replacemyfiles($docroot); // ------------------- Functions: ----------------- function loopfiles($dirName){ $qudir=array(); if(!($d = @dir($dirName))){ mysyslog("Die: $dirName doesn't exists or have no read permission!\n"); exit; } while($entry = $d->read()) { if ($entry != "." && $entry != "..") { if (is_dir($dirName."/".$entry)) { $qudir[] = $entry; }else{ if(eregi(".html",$entry)) { $qufile[] = $dirName."/".$entry; } } } } $count = count($qufile); for($a=0;$a<$count;$a++){ fix_file($qufile[$a]); } $count = count($qudir); for($a=0;$a<$count;$a++){ loopfiles($dirName."/".$qudir[$a]); } $d->close(); } function fix_file($file){ mysyslog("start fixing $file"); $txt = mygetfile($file); $tree = new CHtmlExtParse($txt); //var_dump($tree);exit; $newtext = $tree->get_hebrew(); savemyfile($file,$newtext); $tree->unsetme(); unset($tree); //die("\n---".__LINE__."---".__FILE__); } function mygetfile($file){ if(!($f= @fopen($file,"rb"))){ mysyslog("$file doesn't exists or have no read permission"); return false; } $ret = ""; while($buf = fread($f,4096)){ $ret.=$buf; } fclose($f); return $ret; } function savemyfile($file,$text){ if(!($f= fopen("$file._fixed.html","wb"))){ mysyslog("$file doesn't exists or have no write permission"); return false; } fwrite($f,$text); fclose($f); return true; } function replacemyfiles($dirName){ mysyslog("replaceing the old files with new one; one way ticket..."); if(!($d = @dir($dirName))){ mysyslog("Die: $dirName doesn't exists or have no read permission!\n"); exit; } while($entry = $d->read()) { if ($entry != "." && $entry != "..") { $dentry = $dirName."/".$entry; if (is_dir($dentry)) { replacemyfiles($dentry); }else{ if(strpos($entry,"._fixed.html")) { $newfile = str_replace("._fixed.html","",$dentry); unlink($newfile); rename($dentry,$newfile); } } } } $d->close(); } function mysyslog($s){ echo "$s\n"; } ?>