debug = $debug; $this->filename = $filename; $this->subs["keys"] = array(); //Variables to Substiture with. $this->subs["vals"] = array(); //Variables to Substiture with. $this->cmnAssign(&$this->subs["keys"],&$this->subs["vals"],"DEFAULTS.VERSION","PDFTemplate v1.1.1"); $this->cmnAssign(&$this->subs["keys"],&$this->subs["vals"],"DEFAULTS.DATE",date("F j, Y")); $this->cmnAssign(&$this->subs["keys"],&$this->subs["vals"],"DEFAULTS.TIME",date("g:m A")); $this->fontstack["Font"] = array(); $this->fontstack["Size"] = array(); $this->addfont("Helvetica",12); //Setup PDF Docuemnt $this->pdf = &PDFDriver::factory($driver); $this->pdf->open(); //Send Headers if ($this->debug) { // Header("Content-type: text/html"); } else { Header("Content-type: application/pdf"); // Header("Content-disposition: attachment; filename=\"report.pdf\""); } } //DebugPrint wrapper..Only prints when debug==1 function DebugPrint($message) { if (!$this->debug) return; print "".htmlentities($message)."
\n"; } //Debug Assignments function DebugAssign() { if (!$this->debug) return; print "
\nGlobal\n";
		print_r($this->subs);
		foreach ($this->blocksubs as $key=>$val) {
			print "Block: $key\n";
			print_r($val);
		}
		print "
"; } //Assign variables // ie. Assign(array('key'=>'val')); // or Assign("key","val"); function Assign($key,$val=NULL) { if (is_array($key)) { foreach($key as $t_key=>$t_val) { $this->DebugPrint("Assigning $t_key=>$t_val"); $this->cmnAssign(&$this->subs["keys"],&$this->subs["vals"],$t_key,$t_val); } } else { $this->DebugPrint("Assigning $key=>$val"); $this->cmnAssign(&$this->subs["keys"],&$this->subs["vals"],$key,$val); } } // $block is the name associated with the block. // ie. AssignBlock("block",array('key'=>'val')); // or AssignBlock("block","key","val"); function AssignBlock($block,$key,$val=NULL) { if (!isset($this->blocksubs[$block])) { $this->blocksubs[$block]["keys"][0]=array(); $this->blocksubs[$block]["vals"][0]=array(); $this->blocksubs[$block]["current"]=0; $this->cmnAssign( &$this->blocksubs[$block]["keys"][$this->blocksubs[$block]["current"]], &$this->blocksubs[$block]["vals"][$this->blocksubs[$block]["current"]], "DEFAULTS.BLOCK",$block); $this->cmnAssign( &$this->blocksubs[$block]["keys"][$this->blocksubs[$block]["current"]], &$this->blocksubs[$block]["vals"][$this->blocksubs[$block]["current"]], "DEFAULTS.ITERATION",1); } if (is_array($key)) { //assiciative array passed foreach($key as $t_key=>$t_val) { $this->DebugPrint("Assigning \"$block\" $t_key=>$t_val"); $this->cmnAssign( &$this->blocksubs[$block]["keys"][$this->blocksubs[$block]["current"]], &$this->blocksubs[$block]["vals"][$this->blocksubs[$block]["current"]], ".$t_key",$t_val); } } else { //plain old strings were passed $this->DebugPrint("Assigning \"$block\" $key=>$val"); $this->cmnAssign( &$this->blocksubs[$block]["keys"][$this->blocksubs[$block]["current"]], &$this->blocksubs[$block]["vals"][$this->blocksubs[$block]["current"]], ".$key",$val); } } //incremenmnt to next block iteration function NextBlock($block) { if (isset($this->blocksubs[$block])) { $this->blocksubs[$block]["keys"][++$this->blocksubs[$block]["current"]]=array(); $this->blocksubs[$block]["vals"][$this->blocksubs[$block]["current"]]=array(); $this->cmnAssign( &$this->blocksubs[$block]["keys"][$this->blocksubs[$block]["current"]], &$this->blocksubs[$block]["vals"][$this->blocksubs[$block]["current"]], "DEFAULTS.BLOCK",$block); $this->cmnAssign( &$this->blocksubs[$block]["keys"][$this->blocksubs[$block]["current"]], &$this->blocksubs[$block]["vals"][$this->blocksubs[$block]["current"]], "DEFAULTS.ITERATION",$this->blocksubs[$block]["current"]+1); } } //Close PDF and finish flushing output to client function Close() { if ($this->debug) ob_start(); $this->pdf->close(); if ($this->debug) ob_end_clean(); } //Parse through the XML once document and generate PDF (can be called multiple times) //Returns XML Error messages if bad XML. otherwise false. function Parse() { $this->DebugAssign(); $error = XMLParseFile (&$this->parser, "pdf.xml", 0, "", 1,"UTF-8"); if (strcmp ($error, "")) { return $error; } else { $this->WalkXML ("0"); //&$this->parser->structure, &$this->parser->positions); return false; } } //END OF use Public functions.. The rest are "internal use only" functions //Recursive funtion to "WALK" the XML tree and call rendering routines function WalkXML ($path) { if (is_array($this->parser->structure[$path])) { //Beginning Tag $this->startElement($path); //Check for looping $count=1; if (strcmp ($this->parser->structure[$path]["Tag"], "BLOCK") == 0) { $this->page["block"]["name"]=$this->parser->structure[$path]["Attributes"]["NAME"]; for ($this->page["block"]["current"]=0; $this->page["block"]["current"]<=$this->blocksubs[$this->page["block"]["name"]]["current"]; $this->page["block"]["current"]++) { for ($element = 0; $element < $this->parser->structure[$path]["Elements"];$element++) $this->WalkXML($path.",$element"); } $this->page["block"]["name"]=""; //Clear out block; } else { //not in a block. Normal parsing //walk sub tags/content for ($element = 0; $element < $this->parser->structure[$path]["Elements"];$element++) $this->WalkXML($path.",$element"); } //End Tag $this->endElement($path); } else { //Content //Find parent path $parentpath = substr($path,0,strrpos($path,",")); $this->DebugPrint("PATH=".$path."-".strrpos($path,",")."-".$parentpath); $data = trim(preg_replace("/(\ +|\n)/"," ",$this->parser->structure[$path])); if (strlen($data)>0) { $this->characterData($this->parser->structure[$parentpath]["Tag"] ,$this->parser->structure[$parentpath]["Attributes"] ,$data); } } } //set a font. $index of the font in the font stack (-1 for latest). if $force is true then // this font's height will be forced for line height otherwise the max of the current // lineheight and the fonts will be used. function setfont($index=-1,$force=false) { if ($index<0) { $index = count($this->fontstack["Font"])-1; } //line height if ($force) { $this->page["fontlead"] = $this->fontstack["Size"][$index]*1.5; } else { $this->page["fontlead"] = max($this->page["fontlead"],$this->fontstack["Size"][$index]*1.5); } $this->DebugPrint ("fontlead=".$this->page["fontlead"]); $this->pdf->setfont($this->fontstack["Font"][$index], $this->fontstack["Size"][$index]); } //Add font to the font stack $fontname is the name of the registered font. $size is the point size //if either parameter is null("" or 0) the previous fonts value will be used. function addfont($fontname="",$size=0) { array_push($this->fontstack["Font"],( ($fontname=="") ? $this->fontstack["Font"][count($this->fontstack["Size"])-1] : $fontname) ); array_push($this->fontstack["Size"],( ($size<1) ? $this->fontstack["Size"][count($this->fontstack["Size"])-1] : $size) ); return count($this->fontstack["Font"])-1; } //returns an indexed array of the Font name and Font size of the specified font in the index function getfont($index=-1) { if ($index<0) { $index = count($this->fontstack["Font"])-1; } return array($this->fontstack["Font"][$index],$this->fontstack["Size"][$index]); } //removes the last font from the font stack function removefont() { array_pop($this->fontstack["Font"]); array_pop($this->fontstack["Size"]); } //finds the bold version of the current (or specified) font and returns the name of the font function getbold($index=-1) { $font = $this->getfont($index); $bolds = array( 'Helvetica'=>"Helvetica-Bold", 'Courier'=>"Courier-Bold", 'Times-Roman'=>"Times-Roman-Bold", 'Helvetica-Oblique'=>"Helvetica-BoldOblique", 'Courier-Oblique'=>"Courier-BoldOblique", 'Time-Roman-Italic'=>"Times-Roman-BoldItalic", ); return (empty($bolds[$font[0]])?"":$bolds[$font[0]]); } //finds the italic version of the current (or specified) font and returns the name of the font function getitalic($index=-1) { $font = $this->getfont($index); $italics = array( 'Helvetica'=>"Helvetica-Oblique", 'Courier'=>"Courier-Oblique", 'Times-Roman'=>"Times-Roman-Italic", 'Helvetica-Bold'=>"Helvetica-BoldOblique", 'Courier-Bold'=>"Courier-BoldOblique", 'Time-Roman-Bold'=>"Times-Roman-BoldItalic", ); return (empty($italics[$font[0]])?"":$italics[$font[0]]); } //add (and replace) key/value pair in substitution arrays function cmnAssign(&$keys,&$vals,$key,$val) { $i=array_search("{".$key."}",$keys); if ( ($i!==FALSE) && ($i!==NUll)) { $this->DebugPrint("previous=".$i); $keys[$i] = "{".$key."}"; $vals[$i] = $val; } else { array_push($keys,"{".$key."}"); array_push($vals,$val); } } //Ends the last page function cmnEndPage() { if (!$this->page["closed"]) { if($this->debug) ob_start(); $this->pdf->endpage(); if ($this->debug) ob_end_clean(); $this->page["closed"]=true; } } //Ends last page (if one exists) and Begins a new page and resets curX and curY. sets font info. function cmnNewPage() { $this->cmnEndPage(); //End previous page if exists //Current Position of cursor $this->page["curY"] = $this->page["height"]-$this->page["margin"]; //Current Y position $this->page["curX"] = $this->page["margin"]; //Current X position //Begin the page $this->DebugPrint("newpage=".$this->page["width"].",".$this->page["height"]); $this->pdf->beginpage($this->page["width"],$this->page["height"]); $this->page["closed"]=false; if ($this->page["ticks"]) { $this->cmnDrawTicks(); } else { $this->setfont(); } } //Draw ticks on sides of page function cmnDrawTicks() { //Generate debuging ticks; $c=0; $this->pdf->setfont("Helvetica",8); //left side /* for ($i=$this->page["height"]; $i>0;$i-=10) { if (!($c % 5)) { $this->pdf->drawtext_xy($this->page["height"]-$i,10,$i-3); } if (!($c % 5)) { $this->pdf->line(0,$i,10,$i); } else { $this->pdf->line(0,$i,7,$i); } $c++; }*/ for ($i=0; $i<$this->page["height"]; $i+=10) { if (!($c % 5)) { $this->pdf->drawtext_xy($i,10,$i-3); } if (!($c % 5)) { $this->pdf->line(0,$i,10,$i); } else { $this->pdf->line(0,$i,7,$i); } $c++; } //top edge $c=0; for ($i=0; $i<$this->page["width"]; $i+=10) { if (!($c % 5)) { $this->pdf->drawtext_xy($i,$i-6,$this->page["height"]-18); } if (!($c % 5)) { $this->pdf->line($i,$this->page["height"],$i,$this->page["height"]-10); } else { $this->pdf->line($i,$this->page["height"],$i,$this->page["height"]-7); } $c++; } $this->setfont(); } //handles the "beginning" of a tag and sets parameters appropriatly function startElement($path) { $attribs = &$this->parser->structure[$path]["Attributes"]; switch ($this->parser->structure[$path]["Tag"]) { case 'PDF': $this->DebugPrint("Begining of file"); $this->page["closed"]=true; break; case 'PAGE': $this->page["fontlead"]=0; $this->page["tab"] = 36; //A single 'tab'. For paragraph formatting. //Margin spacing Default = 36 (half inch); $this->page["margin"]=(empty($attribs["MARGIN"])?36:$attribs["MARGIN"]*72); //Default Width $this->page["width"]=(empty($attribs["WIDTH"])?612:$attribs["WIDTH"]*72); //Default Height $this->page["height"]=(empty($attribs["HEIGHT"])?792:$attribs["HEIGHT"]*72); $this->page["ticks"]=($attribs["TICKS"]==1?true:false); //Make new page $this->cmnNewPage(); if (!empty($attribs["TOPMARGIN"])) { $this->page["curY"]=$this->page["height"]-$attribs["TOPMARGIN"]*72; } break; case 'PARA': if (!empty($attribs["INDENT"])) { $this->page["curX"]+=$this->page["tab"]; } if (!empty($attribs["TAB"])) { $this->page["left"]=$this->page["margin"]+$attribs["TAB"]*72; $this->page["curX"]+=$attribs["TAB"]*72; } else { $this->page["left"]=$this->page["margin"]; } $this->page["align"]=(empty($attribs["ALIGN"])?"left":strtolower($attribs["ALIGN"])); break; case 'BOLD': $this->setfont($this->addfont($this->getbold())); break; case 'ITALIC': $this->setfont($this->addfont($this->getitalic())); break; /*case 'FONTDEF': $this->DebugPrint( "face=".$attribs["FACE"] ." afm=".$attribs["AFM"] ." pfm=".$attribs["PFM"] ." outline=".$attribs["OUTLINE"]); PDF_set_parameter($this->pdf,"FontAFM",$attribs["FACE"]."=".$attribs["AFM"]); PDF_set_parameter($this->pdf,"FontOutline",$attribs["FACE"]."=".$attribs["OUTLINE"]); break;*/ case 'FONT': $this->DebugPrint("face=".$attribs["FACE"]." size=".$attribs["SIZE"]); $this->setfont($this->addfont($attribs["FACE"],$attribs["SIZE"])); break; case 'TAB': $this->page["curX"]=72 * $attribs["TAB"]; break; case 'BR': $this->page["curX"]=$this->page["left"]; $this->page["curY"]-=$this->page["fontlead"]; break; case 'BREAK': $this->cmnNewPage(); break; default: break; } } //handles the "end" of a tag and (un)sets parameters appropriatly function endElement($path) { $attribs = &$this->parser->structure[$path]["Attributes"]; switch ($this->parser->structure[$path]["Tag"]) { case 'PDF': $this->DebugPrint("End of document"); $this->cmnEndPage(); break; case 'PAGE': //End the page and push to client. // PDF_end_page($this->pdf); break; case 'PARA': $this->page["curY"]-=$this->page["fontlead"]; //goto next line. $this->setfont(-1,true); //reset font and fontlead (line height) $this->page["curX"]=$this->page["margin"]; //Back to the left of the line if (empty($attribs["NOBREAK"])) { $this->page["curY"]-=$this->page["fontlead"]; //insert space between paragraphs } //$this->setfont(); //Set Default Font break; case 'FONT': case 'ITALIC': case 'BOLD': $this->removefont(); //Remove Last Font $this->setfont(); //Set Previous Font break; default: break; } } //end Element //handles the data between tags (the actual text) function characterData($tag,$attribs,$data) { $this->DebugPrint("CharData tag=$tag"); switch ($tag) { case 'FONT': //Displays text in different font. Formatting done in beginElement case 'BOLD': //Displays bold text. Formating done in beginElement case 'ITALIC': //Displays italic text. Formating done in beginElement //case 'LOOP': //incase loop tag around plain text in a para. (not preferred) case 'NOSUB': //Do not substitute variables case 'PARA': //Displays a paragraph //Substitute variables if (strcmp($tag,'NOSUB')<>0) { $data = str_replace($this->subs["keys"],$this->subs["vals"],$data); if (!empty($this->page["block"]["name"])) { $this->DebugPrint("blocksub=".$this->page["block"]["name"]."-".$this->page["block"]["current"]); $data = str_replace( $this->blocksubs[$this->page["block"]["name"]]["keys"][$this->page["block"]["current"]], $this->blocksubs[$this->page["block"]["name"]]["vals"][$this->page["block"]["current"]], $data); } } while (strlen($data)>0) { //Check to see if we are past the bottom of the page. // It's backward login due to PDF's 0 coord is at the bottom if ($this->page["curY"] < ($this->page["margin"]+$this->page["fontlead"]) ) { $this->DebugPrint("Ending Current Page"); $this->cmnNewPage(); $this->DebugPrint("Starting New Page"); } $extra = $this->pdf->drawtext($data, $this->page["curX"], //Left $this->page["curY"], //Top $this->page["width"]-$this->page["curX"]-$this->page["margin"], //Width $this->page["fontlead"], //Height (one line) $this->page["align"]); //Justification $printed_num = strlen($data) - $extra; $printed_text = substr($data, 0, $printed_num); $printed_size = $this->pdf->textwidth($printed_text); $this->DebugPrint( "X=".$this->page["curX"] ." Y=".$this->page["curY"] ." extra=".$extra ." end=".$printed_size ." W=".$printed_size); $this->DebugPrint("text=".$printed_text); $printed_size+=$this->page["curX"]; if (method_exists($this->pdf, "get_textX")) { $this->DebugPrint("textx=".$this->pdf->get_textX() ." PS=".$printed_size); $printed_size = $this->pdf->get_textX(); } if ($this->pdf->textwidth($data) > ($this->page["width"]-$this->page["margin"]-$this->page["curX"])) { //Text wider than page $this->page["curX"]=$this->page["left"]; //go back to beginning of line $this->page["curY"]-=$this->page["fontlead"]; //Go down one line $this->setfont(-1,true); //Reset line height (also resets fontlead) } else { //fits on one line get current "X" position $this->page["curX"]=$printed_size; } //update $data with unprinted text //if ($extra>0) { $data = substr($data,(strlen($data)-$extra)); //}; } //while break; default: break; } } } //End Class PDFTemplate