| 250 | kaklik | 1 | <?php | 
      
        |  |  | 2 | /******************************************************************************* | 
      
        |  |  | 3 | * Software: UFPDF, Unicode Free PDF generator                                  * | 
      
        |  |  | 4 | * Version:  0.1                                                                * | 
      
        |  |  | 5 | *           based on FPDF 1.52 by Olivier PLATHEY                              * | 
      
        |  |  | 6 | * Date:     2004-09-01                                                         * | 
      
        |  |  | 7 | * Author:   Steven Wittens <steven@acko.net>                                   * | 
      
        |  |  | 8 | * License:  GPL                                                                * | 
      
        |  |  | 9 | *                                                                              * | 
      
        |  |  | 10 | * UFPDF is a modification of FPDF to support Unicode through UTF-8.            * | 
      
        |  |  | 11 | *                                                                              * | 
      
        |  |  | 12 | *******************************************************************************/ | 
      
        |  |  | 13 |  | 
      
        |  |  | 14 | if(!class_exists('UFPDF')) | 
      
        |  |  | 15 | { | 
      
        |  |  | 16 | define('UFPDF_VERSION','0.1'); | 
      
        |  |  | 17 |  | 
      
        |  |  | 18 | include_once './libraries/fpdf/fpdf.php'; | 
      
        |  |  | 19 |  | 
      
        |  |  | 20 | class UFPDF extends FPDF | 
      
        |  |  | 21 | { | 
      
        |  |  | 22 |  | 
      
        |  |  | 23 | /******************************************************************************* | 
      
        |  |  | 24 | *                                                                              * | 
      
        |  |  | 25 | *                               Public methods                                 * | 
      
        |  |  | 26 | *                                                                              * | 
      
        |  |  | 27 | *******************************************************************************/ | 
      
        |  |  | 28 | function UFPDF($orientation='P',$unit='mm',$format='A4') | 
      
        |  |  | 29 | { | 
      
        |  |  | 30 |   FPDF::FPDF($orientation, $unit, $format); | 
      
        |  |  | 31 | } | 
      
        |  |  | 32 |  | 
      
        |  |  | 33 | function GetStringWidth($s) | 
      
        |  |  | 34 | { | 
      
        |  |  | 35 |   //Get width of a string in the current font | 
      
        |  |  | 36 |   $s = (string)$s; | 
      
        |  |  | 37 |   $codepoints=$this->utf8_to_codepoints($s); | 
      
        |  |  | 38 |   $cw=&$this->CurrentFont['cw']; | 
      
        |  |  | 39 |   $w=0; | 
      
        |  |  | 40 |   foreach($codepoints as $cp) | 
      
        |  |  | 41 |     $w+=isset($cw[$cp])?$cw[$cp]:0; | 
      
        |  |  | 42 |   return $w*$this->FontSize/1000; | 
      
        |  |  | 43 | } | 
      
        |  |  | 44 |  | 
      
        |  |  | 45 | function AddFont($family,$style='',$file='') | 
      
        |  |  | 46 | { | 
      
        |  |  | 47 |   //Add a TrueType or Type1 font | 
      
        |  |  | 48 |   $family=strtolower($family); | 
      
        |  |  | 49 |   if($family=='arial') | 
      
        |  |  | 50 |     $family='helvetica'; | 
      
        |  |  | 51 |   $style=strtoupper($style); | 
      
        |  |  | 52 |   if($style=='IB') | 
      
        |  |  | 53 |     $style='BI'; | 
      
        |  |  | 54 |   if(isset($this->fonts[$family.$style])) | 
      
        |  |  | 55 |     $this->Error('Font already added: '.$family.' '.$style); | 
      
        |  |  | 56 |   if($file=='') | 
      
        |  |  | 57 |     $file=str_replace(' ','',$family).strtolower($style).'.php'; | 
      
        |  |  | 58 |   if(defined('FPDF_FONTPATH')) | 
      
        |  |  | 59 |     $file=FPDF_FONTPATH.$file; | 
      
        |  |  | 60 |   include($file); | 
      
        |  |  | 61 |   if(!isset($name)) | 
      
        |  |  | 62 |     $this->Error('Could not include font definition file'); | 
      
        |  |  | 63 |   $i=count($this->fonts)+1; | 
      
        |  |  | 64 |   $this->fonts[$family.$style]=array('i'=>$i,'type'=>$type,'name'=>$name,'desc'=>$desc,'up'=>$up,'ut'=>$ut,'cw'=>$cw,'file'=>$file,'ctg'=>$ctg); | 
      
        |  |  | 65 |   if($file) | 
      
        |  |  | 66 |   { | 
      
        |  |  | 67 |     if($type=='TrueTypeUnicode') | 
      
        |  |  | 68 |       $this->FontFiles[$file]=array('length1'=>$originalsize); | 
      
        |  |  | 69 |     else | 
      
        |  |  | 70 |       $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2); | 
      
        |  |  | 71 |   } | 
      
        |  |  | 72 | } | 
      
        |  |  | 73 |  | 
      
        |  |  | 74 | function Text($x,$y,$txt) | 
      
        |  |  | 75 | { | 
      
        |  |  | 76 |   //Output a string | 
      
        |  |  | 77 |   $s=sprintf('BT %.2f %.2f Td %s Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escapetext($txt)); | 
      
        |  |  | 78 |   if($this->underline and $txt!='') | 
      
        |  |  | 79 |     $s.=' '.$this->_dounderline($x,$y,$this->GetStringWidth($txt),$txt); | 
      
        |  |  | 80 |   if($this->ColorFlag) | 
      
        |  |  | 81 |     $s='q '.$this->TextColor.' '.$s.' Q'; | 
      
        |  |  | 82 |   $this->_out($s); | 
      
        |  |  | 83 | } | 
      
        |  |  | 84 |  | 
      
        |  |  | 85 | function AcceptPageBreak() | 
      
        |  |  | 86 | { | 
      
        |  |  | 87 |   //Accept automatic page break or not | 
      
        |  |  | 88 |   return $this->AutoPageBreak; | 
      
        |  |  | 89 | } | 
      
        |  |  | 90 |  | 
      
        |  |  | 91 | function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='') | 
      
        |  |  | 92 | { | 
      
        |  |  | 93 |   //Output a cell | 
      
        |  |  | 94 |   $k=$this->k; | 
      
        |  |  | 95 |   if($this->y+$h>$this->PageBreakTrigger and !$this->InFooter and $this->AcceptPageBreak()) | 
      
        |  |  | 96 |   { | 
      
        |  |  | 97 |     //Automatic page break | 
      
        |  |  | 98 |     $x=$this->x; | 
      
        |  |  | 99 |     $ws=$this->ws; | 
      
        |  |  | 100 |     if($ws>0) | 
      
        |  |  | 101 |     { | 
      
        |  |  | 102 |       $this->ws=0; | 
      
        |  |  | 103 |       $this->_out('0 Tw'); | 
      
        |  |  | 104 |     } | 
      
        |  |  | 105 |     $this->AddPage($this->CurOrientation); | 
      
        |  |  | 106 |     $this->x=$x; | 
      
        |  |  | 107 |     if($ws>0) | 
      
        |  |  | 108 |     { | 
      
        |  |  | 109 |       $this->ws=$ws; | 
      
        |  |  | 110 |       $this->_out(sprintf('%.3f Tw',$ws*$k)); | 
      
        |  |  | 111 |     } | 
      
        |  |  | 112 |   } | 
      
        |  |  | 113 |   if($w==0) | 
      
        |  |  | 114 |     $w=$this->w-$this->rMargin-$this->x; | 
      
        |  |  | 115 |   $s=''; | 
      
        |  |  | 116 |   if($fill==1 or $border==1) | 
      
        |  |  | 117 |   { | 
      
        |  |  | 118 |     if($fill==1) | 
      
        |  |  | 119 |       $op=($border==1) ? 'B' : 'f'; | 
      
        |  |  | 120 |     else | 
      
        |  |  | 121 |       $op='S'; | 
      
        |  |  | 122 |     $s=sprintf('%.2f %.2f %.2f %.2f re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op); | 
      
        |  |  | 123 |   } | 
      
        |  |  | 124 |   if(is_string($border)) | 
      
        |  |  | 125 |   { | 
      
        |  |  | 126 |     $x=$this->x; | 
      
        |  |  | 127 |     $y=$this->y; | 
      
        |  |  | 128 |     if(is_int(strpos($border,'L'))) | 
      
        |  |  | 129 |       $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k); | 
      
        |  |  | 130 |     if(is_int(strpos($border,'T'))) | 
      
        |  |  | 131 |       $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k); | 
      
        |  |  | 132 |     if(is_int(strpos($border,'R'))) | 
      
        |  |  | 133 |       $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k); | 
      
        |  |  | 134 |     if(is_int(strpos($border,'B'))) | 
      
        |  |  | 135 |       $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k); | 
      
        |  |  | 136 |   } | 
      
        |  |  | 137 |   if($txt!='') | 
      
        |  |  | 138 |   { | 
      
        |  |  | 139 |     $width = $this->GetStringWidth($txt); | 
      
        |  |  | 140 |     if($align=='R') | 
      
        |  |  | 141 |       $dx=$w-$this->cMargin-$width; | 
      
        |  |  | 142 |     elseif($align=='C') | 
      
        |  |  | 143 |       $dx=($w-$width)/2; | 
      
        |  |  | 144 |     else | 
      
        |  |  | 145 |       $dx=$this->cMargin; | 
      
        |  |  | 146 |     if($this->ColorFlag) | 
      
        |  |  | 147 |       $s.='q '.$this->TextColor.' '; | 
      
        |  |  | 148 |     $txtstring=$this->_escapetext($txt); | 
      
        |  |  | 149 |     $s.=sprintf('BT %.2f %.2f Td %s Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txtstring); | 
      
        |  |  | 150 |     if($this->underline) | 
      
        |  |  | 151 |       $s.=' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$width,$txt); | 
      
        |  |  | 152 |     if($this->ColorFlag) | 
      
        |  |  | 153 |       $s.=' Q'; | 
      
        |  |  | 154 |     if($link) | 
      
        |  |  | 155 |       $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$width,$this->FontSize,$link); | 
      
        |  |  | 156 |   } | 
      
        |  |  | 157 |   if($s) | 
      
        |  |  | 158 |     $this->_out($s); | 
      
        |  |  | 159 |   $this->lasth=$h; | 
      
        |  |  | 160 |   if($ln>0) | 
      
        |  |  | 161 |   { | 
      
        |  |  | 162 |     //Go to next line | 
      
        |  |  | 163 |     $this->y+=$h; | 
      
        |  |  | 164 |     if($ln==1) | 
      
        |  |  | 165 |       $this->x=$this->lMargin; | 
      
        |  |  | 166 |   } | 
      
        |  |  | 167 |   else | 
      
        |  |  | 168 |     $this->x+=$w; | 
      
        |  |  | 169 | } | 
      
        |  |  | 170 |  | 
      
        |  |  | 171 | /******************************************************************************* | 
      
        |  |  | 172 | *                                                                              * | 
      
        |  |  | 173 | *                              Protected methods                               * | 
      
        |  |  | 174 | *                                                                              * | 
      
        |  |  | 175 | *******************************************************************************/ | 
      
        |  |  | 176 |  | 
      
        |  |  | 177 | function _puttruetypeunicode($font) { | 
      
        |  |  | 178 |   //Type0 Font | 
      
        |  |  | 179 |   $this->_newobj(); | 
      
        |  |  | 180 |   $this->_out('<</Type /Font'); | 
      
        |  |  | 181 |   $this->_out('/Subtype /Type0'); | 
      
        |  |  | 182 |   $this->_out('/BaseFont /'. $font['name'] .'-UCS'); | 
      
        |  |  | 183 |   $this->_out('/Encoding /Identity-H'); | 
      
        |  |  | 184 |   $this->_out('/DescendantFonts ['. ($this->n + 1) .' 0 R]'); | 
      
        |  |  | 185 |   $this->_out('>>'); | 
      
        |  |  | 186 |   $this->_out('endobj'); | 
      
        |  |  | 187 |  | 
      
        |  |  | 188 |   //CIDFont | 
      
        |  |  | 189 |   $this->_newobj(); | 
      
        |  |  | 190 |   $this->_out('<</Type /Font'); | 
      
        |  |  | 191 |   $this->_out('/Subtype /CIDFontType2'); | 
      
        |  |  | 192 |   $this->_out('/BaseFont /'. $font['name']); | 
      
        |  |  | 193 |   $this->_out('/CIDSystemInfo <</Registry (Adobe) /Ordering (UCS) /Supplement 0>>'); | 
      
        |  |  | 194 |   $this->_out('/FontDescriptor '. ($this->n + 1) .' 0 R'); | 
      
        |  |  | 195 |   $c = 0; | 
      
        |  |  | 196 |   $widths = ''; | 
      
        |  |  | 197 |   foreach ($font['cw'] as $i => $w) { | 
      
        |  |  | 198 |     $widths .= $i .' ['. $w.'] '; | 
      
        |  |  | 199 |   } | 
      
        |  |  | 200 |   $this->_out('/W ['. $widths .']'); | 
      
        |  |  | 201 |   $this->_out('/CIDToGIDMap '. ($this->n + 2) .' 0 R'); | 
      
        |  |  | 202 |   $this->_out('>>'); | 
      
        |  |  | 203 |   $this->_out('endobj'); | 
      
        |  |  | 204 |  | 
      
        |  |  | 205 |   //Font descriptor | 
      
        |  |  | 206 |   $this->_newobj(); | 
      
        |  |  | 207 |   $this->_out('<</Type /FontDescriptor'); | 
      
        |  |  | 208 |   $this->_out('/FontName /'.$font['name']); | 
      
        |  |  | 209 |   $s = ''; | 
      
        |  |  | 210 |   foreach ($font['desc'] as $k => $v) { | 
      
        |  |  | 211 |     $s .= ' /'. $k .' '. $v; | 
      
        |  |  | 212 |   } | 
      
        |  |  | 213 |   if ($font['file']) { | 
      
        |  |  | 214 | 		$s .= ' /FontFile2 '. $this->FontFiles[$font['file']]['n'] .' 0 R'; | 
      
        |  |  | 215 |   } | 
      
        |  |  | 216 |   $this->_out($s); | 
      
        |  |  | 217 |   $this->_out('>>'); | 
      
        |  |  | 218 |   $this->_out('endobj'); | 
      
        |  |  | 219 |  | 
      
        |  |  | 220 |   //Embed CIDToGIDMap | 
      
        |  |  | 221 |   $this->_newobj(); | 
      
        |  |  | 222 |   if(defined('FPDF_FONTPATH')) | 
      
        |  |  | 223 |     $file=FPDF_FONTPATH.$font['ctg']; | 
      
        |  |  | 224 |   else | 
      
        |  |  | 225 |     $file=$font['ctg']; | 
      
        |  |  | 226 |   $size=filesize($file); | 
      
        |  |  | 227 |   if(!$size) | 
      
        |  |  | 228 |     $this->Error('Font file not found'); | 
      
        |  |  | 229 |   $this->_out('<</Length '.$size); | 
      
        |  |  | 230 | 	if(substr($file,-2) == '.z') | 
      
        |  |  | 231 |     $this->_out('/Filter /FlateDecode'); | 
      
        |  |  | 232 |   $this->_out('>>'); | 
      
        |  |  | 233 |   $f = fopen($file,'rb'); | 
      
        |  |  | 234 |   $this->_putstream(fread($f,$size)); | 
      
        |  |  | 235 |   fclose($f); | 
      
        |  |  | 236 |   $this->_out('endobj'); | 
      
        |  |  | 237 | } | 
      
        |  |  | 238 |  | 
      
        |  |  | 239 | function _dounderline($x,$y,$width,$txt) | 
      
        |  |  | 240 | { | 
      
        |  |  | 241 |   //Underline text | 
      
        |  |  | 242 |   $up=$this->CurrentFont['up']; | 
      
        |  |  | 243 |   $ut=$this->CurrentFont['ut']; | 
      
        |  |  | 244 |   $w=$width+$this->ws*substr_count($txt,' '); | 
      
        |  |  | 245 |   return sprintf('%.2f %.2f %.2f %.2f re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt); | 
      
        |  |  | 246 | } | 
      
        |  |  | 247 |  | 
      
        |  |  | 248 | function _textstring($s) | 
      
        |  |  | 249 | { | 
      
        |  |  | 250 |   //Convert to UTF-16BE | 
      
        |  |  | 251 |   $s = $this->utf8_to_utf16be($s); | 
      
        |  |  | 252 |   //Escape necessary characters | 
      
        |  |  | 253 |   return '('. strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\')) .')'; | 
      
        |  |  | 254 | } | 
      
        |  |  | 255 |  | 
      
        |  |  | 256 | function _strreplace($what, $to, $where) { | 
      
        |  |  | 257 |     $to = '' . $to; | 
      
        |  |  | 258 |     return str_replace($this->utf8_to_utf16be($what, false), $this->utf8_to_utf16be($to, false), $where); | 
      
        |  |  | 259 | } | 
      
        |  |  | 260 |  | 
      
        |  |  | 261 | function _escapetext($s) | 
      
        |  |  | 262 | { | 
      
        |  |  | 263 |   //Convert to UTF-16BE | 
      
        |  |  | 264 |   $s = $this->utf8_to_utf16be($s, false); | 
      
        |  |  | 265 |   //Escape necessary characters | 
      
        |  |  | 266 |   return '('. strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\')) .')'; | 
      
        |  |  | 267 | } | 
      
        |  |  | 268 |  | 
      
        |  |  | 269 | function _putinfo() | 
      
        |  |  | 270 | { | 
      
        |  |  | 271 | 	$this->_out('/Producer '.$this->_textstring('UFPDF '. UFPDF_VERSION)); | 
      
        |  |  | 272 | 	if(!empty($this->title)) | 
      
        |  |  | 273 | 		$this->_out('/Title '.$this->_textstring($this->title)); | 
      
        |  |  | 274 | 	if(!empty($this->subject)) | 
      
        |  |  | 275 | 		$this->_out('/Subject '.$this->_textstring($this->subject)); | 
      
        |  |  | 276 | 	if(!empty($this->author)) | 
      
        |  |  | 277 | 		$this->_out('/Author '.$this->_textstring($this->author)); | 
      
        |  |  | 278 | 	if(!empty($this->keywords)) | 
      
        |  |  | 279 | 		$this->_out('/Keywords '.$this->_textstring($this->keywords)); | 
      
        |  |  | 280 | 	if(!empty($this->creator)) | 
      
        |  |  | 281 | 		$this->_out('/Creator '.$this->_textstring($this->creator)); | 
      
        |  |  | 282 | 	$this->_out('/CreationDate '.$this->_textstring('D:'.date('YmdHis'))); | 
      
        |  |  | 283 | } | 
      
        |  |  | 284 |  | 
      
        |  |  | 285 | // UTF-8 to UTF-16BE conversion. | 
      
        |  |  | 286 | // Correctly handles all illegal UTF-8 sequences. | 
      
        |  |  | 287 | function utf8_to_utf16be(&$txt, $bom = true) { | 
      
        |  |  | 288 |   $l = strlen($txt); | 
      
        |  |  | 289 |   $out = $bom ? "\xFE\xFF" : ''; | 
      
        |  |  | 290 |   for ($i = 0; $i < $l; ++$i) { | 
      
        |  |  | 291 |     $c = ord($txt{$i}); | 
      
        |  |  | 292 |     // ASCII | 
      
        |  |  | 293 |     if ($c < 0x80) { | 
      
        |  |  | 294 |       $out .= "\x00". $txt{$i}; | 
      
        |  |  | 295 |     } | 
      
        |  |  | 296 |     // Lost continuation byte | 
      
        |  |  | 297 |     else if ($c < 0xC0) { | 
      
        |  |  | 298 |       $out .= "\xFF\xFD"; | 
      
        |  |  | 299 |       continue; | 
      
        |  |  | 300 |     } | 
      
        |  |  | 301 |     // Multibyte sequence leading byte | 
      
        |  |  | 302 |     else { | 
      
        |  |  | 303 |       if ($c < 0xE0) { | 
      
        |  |  | 304 |         $s = 2; | 
      
        |  |  | 305 |       } | 
      
        |  |  | 306 |       else if ($c < 0xF0) { | 
      
        |  |  | 307 |         $s = 3; | 
      
        |  |  | 308 |       } | 
      
        |  |  | 309 |       else if ($c < 0xF8) { | 
      
        |  |  | 310 |         $s = 4; | 
      
        |  |  | 311 |       } | 
      
        |  |  | 312 |       // 5/6 byte sequences not possible for Unicode. | 
      
        |  |  | 313 |       else { | 
      
        |  |  | 314 |         $out .= "\xFF\xFD"; | 
      
        |  |  | 315 |         while (ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; } | 
      
        |  |  | 316 |         continue; | 
      
        |  |  | 317 |       } | 
      
        |  |  | 318 |  | 
      
        |  |  | 319 |       $q = array($c); | 
      
        |  |  | 320 |       // Fetch rest of sequence | 
      
        |  |  | 321 |       $l = strlen($txt); | 
      
        |  |  | 322 |       while ($i + 1 < $l && ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; $q[] = ord($txt{$i}); } | 
      
        |  |  | 323 |  | 
      
        |  |  | 324 |       // Check length | 
      
        |  |  | 325 |       if (count($q) != $s) { | 
      
        |  |  | 326 |         $out .= "\xFF\xFD"; | 
      
        |  |  | 327 |         continue; | 
      
        |  |  | 328 |       } | 
      
        |  |  | 329 |  | 
      
        |  |  | 330 |       switch ($s) { | 
      
        |  |  | 331 |         case 2: | 
      
        |  |  | 332 |           $cp = (($q[0] ^ 0xC0) << 6) | ($q[1] ^ 0x80); | 
      
        |  |  | 333 |           // Overlong sequence | 
      
        |  |  | 334 |           if ($cp < 0x80) { | 
      
        |  |  | 335 |             $out .= "\xFF\xFD"; | 
      
        |  |  | 336 |           } | 
      
        |  |  | 337 |           else { | 
      
        |  |  | 338 |             $out .= chr($cp >> 8); | 
      
        |  |  | 339 |             $out .= chr($cp & 0xFF); | 
      
        |  |  | 340 |           } | 
      
        |  |  | 341 |           continue; | 
      
        |  |  | 342 |  | 
      
        |  |  | 343 |         case 3: | 
      
        |  |  | 344 |           $cp = (($q[0] ^ 0xE0) << 12) | (($q[1] ^ 0x80) << 6) | ($q[2] ^ 0x80); | 
      
        |  |  | 345 |           // Overlong sequence | 
      
        |  |  | 346 |           if ($cp < 0x800) { | 
      
        |  |  | 347 |             $out .= "\xFF\xFD"; | 
      
        |  |  | 348 |           } | 
      
        |  |  | 349 |           // Check for UTF-8 encoded surrogates (caused by a bad UTF-8 encoder) | 
      
        |  |  | 350 |           else if ($c > 0xD800 && $c < 0xDFFF) { | 
      
        |  |  | 351 |             $out .= "\xFF\xFD"; | 
      
        |  |  | 352 |           } | 
      
        |  |  | 353 |           else { | 
      
        |  |  | 354 |             $out .= chr($cp >> 8); | 
      
        |  |  | 355 |             $out .= chr($cp & 0xFF); | 
      
        |  |  | 356 |           } | 
      
        |  |  | 357 |           continue; | 
      
        |  |  | 358 |  | 
      
        |  |  | 359 |         case 4: | 
      
        |  |  | 360 |           $cp = (($q[0] ^ 0xF0) << 18) | (($q[1] ^ 0x80) << 12) | (($q[2] ^ 0x80) << 6) | ($q[3] ^ 0x80); | 
      
        |  |  | 361 |           // Overlong sequence | 
      
        |  |  | 362 |           if ($cp < 0x10000) { | 
      
        |  |  | 363 |             $out .= "\xFF\xFD"; | 
      
        |  |  | 364 |           } | 
      
        |  |  | 365 |           // Outside of the Unicode range | 
      
        |  |  | 366 |           else if ($cp >= 0x10FFFF) { | 
      
        |  |  | 367 |             $out .= "\xFF\xFD"; | 
      
        |  |  | 368 |           } | 
      
        |  |  | 369 |           else { | 
      
        |  |  | 370 |             // Use surrogates | 
      
        |  |  | 371 |             $cp -= 0x10000; | 
      
        |  |  | 372 |             $s1 = 0xD800 | ($cp >> 10); | 
      
        |  |  | 373 |             $s2 = 0xDC00 | ($cp & 0x3FF); | 
      
        |  |  | 374 |  | 
      
        |  |  | 375 |             $out .= chr($s1 >> 8); | 
      
        |  |  | 376 |             $out .= chr($s1 & 0xFF); | 
      
        |  |  | 377 |             $out .= chr($s2 >> 8); | 
      
        |  |  | 378 |             $out .= chr($s2 & 0xFF); | 
      
        |  |  | 379 |           } | 
      
        |  |  | 380 |           continue; | 
      
        |  |  | 381 |       } | 
      
        |  |  | 382 |     } | 
      
        |  |  | 383 |   } | 
      
        |  |  | 384 |   return $out; | 
      
        |  |  | 385 | } | 
      
        |  |  | 386 |  | 
      
        |  |  | 387 | // UTF-8 to codepoint array conversion. | 
      
        |  |  | 388 | // Correctly handles all illegal UTF-8 sequences. | 
      
        |  |  | 389 | function utf8_to_codepoints(&$txt) { | 
      
        |  |  | 390 |   $l = strlen($txt); | 
      
        |  |  | 391 |   $out = array(); | 
      
        |  |  | 392 |   for ($i = 0; $i < $l; ++$i) { | 
      
        |  |  | 393 |     $c = ord($txt{$i}); | 
      
        |  |  | 394 |     // ASCII | 
      
        |  |  | 395 |     if ($c < 0x80) { | 
      
        |  |  | 396 |       $out[] = ord($txt{$i}); | 
      
        |  |  | 397 |     } | 
      
        |  |  | 398 |     // Lost continuation byte | 
      
        |  |  | 399 |     else if ($c < 0xC0) { | 
      
        |  |  | 400 |       $out[] = 0xFFFD; | 
      
        |  |  | 401 |       continue; | 
      
        |  |  | 402 |     } | 
      
        |  |  | 403 |     // Multibyte sequence leading byte | 
      
        |  |  | 404 |     else { | 
      
        |  |  | 405 |       if ($c < 0xE0) { | 
      
        |  |  | 406 |         $s = 2; | 
      
        |  |  | 407 |       } | 
      
        |  |  | 408 |       else if ($c < 0xF0) { | 
      
        |  |  | 409 |         $s = 3; | 
      
        |  |  | 410 |       } | 
      
        |  |  | 411 |       else if ($c < 0xF8) { | 
      
        |  |  | 412 |         $s = 4; | 
      
        |  |  | 413 |       } | 
      
        |  |  | 414 |       // 5/6 byte sequences not possible for Unicode. | 
      
        |  |  | 415 |       else { | 
      
        |  |  | 416 |         $out[] = 0xFFFD; | 
      
        |  |  | 417 |         while (ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; } | 
      
        |  |  | 418 |         continue; | 
      
        |  |  | 419 |       } | 
      
        |  |  | 420 |  | 
      
        |  |  | 421 |       $q = array($c); | 
      
        |  |  | 422 |       // Fetch rest of sequence | 
      
        |  |  | 423 |       $l = strlen($txt); | 
      
        |  |  | 424 |       while ($i + 1 < $l && ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; $q[] = ord($txt{$i}); } | 
      
        |  |  | 425 |  | 
      
        |  |  | 426 |       // Check length | 
      
        |  |  | 427 |       if (count($q) != $s) { | 
      
        |  |  | 428 |         $out[] = 0xFFFD; | 
      
        |  |  | 429 |         continue; | 
      
        |  |  | 430 |       } | 
      
        |  |  | 431 |  | 
      
        |  |  | 432 |       switch ($s) { | 
      
        |  |  | 433 |         case 2: | 
      
        |  |  | 434 |           $cp = (($q[0] ^ 0xC0) << 6) | ($q[1] ^ 0x80); | 
      
        |  |  | 435 |           // Overlong sequence | 
      
        |  |  | 436 |           if ($cp < 0x80) { | 
      
        |  |  | 437 |             $out[] = 0xFFFD; | 
      
        |  |  | 438 |           } | 
      
        |  |  | 439 |           else { | 
      
        |  |  | 440 |             $out[] = $cp; | 
      
        |  |  | 441 |           } | 
      
        |  |  | 442 |           continue; | 
      
        |  |  | 443 |  | 
      
        |  |  | 444 |         case 3: | 
      
        |  |  | 445 |           $cp = (($q[0] ^ 0xE0) << 12) | (($q[1] ^ 0x80) << 6) | ($q[2] ^ 0x80); | 
      
        |  |  | 446 |           // Overlong sequence | 
      
        |  |  | 447 |           if ($cp < 0x800) { | 
      
        |  |  | 448 |             $out[] = 0xFFFD; | 
      
        |  |  | 449 |           } | 
      
        |  |  | 450 |           // Check for UTF-8 encoded surrogates (caused by a bad UTF-8 encoder) | 
      
        |  |  | 451 |           else if ($c > 0xD800 && $c < 0xDFFF) { | 
      
        |  |  | 452 |             $out[] = 0xFFFD; | 
      
        |  |  | 453 |           } | 
      
        |  |  | 454 |           else { | 
      
        |  |  | 455 |             $out[] = $cp; | 
      
        |  |  | 456 |           } | 
      
        |  |  | 457 |           continue; | 
      
        |  |  | 458 |  | 
      
        |  |  | 459 |         case 4: | 
      
        |  |  | 460 |           $cp = (($q[0] ^ 0xF0) << 18) | (($q[1] ^ 0x80) << 12) | (($q[2] ^ 0x80) << 6) | ($q[3] ^ 0x80); | 
      
        |  |  | 461 |           // Overlong sequence | 
      
        |  |  | 462 |           if ($cp < 0x10000) { | 
      
        |  |  | 463 |             $out[] = 0xFFFD; | 
      
        |  |  | 464 |           } | 
      
        |  |  | 465 |           // Outside of the Unicode range | 
      
        |  |  | 466 |           else if ($cp >= 0x10FFFF) { | 
      
        |  |  | 467 |             $out[] = 0xFFFD; | 
      
        |  |  | 468 |           } | 
      
        |  |  | 469 |           else { | 
      
        |  |  | 470 |             $out[] = $cp; | 
      
        |  |  | 471 |           } | 
      
        |  |  | 472 |           continue; | 
      
        |  |  | 473 |       } | 
      
        |  |  | 474 |     } | 
      
        |  |  | 475 |   } | 
      
        |  |  | 476 |   return $out; | 
      
        |  |  | 477 | } | 
      
        |  |  | 478 |  | 
      
        |  |  | 479 | //End of class | 
      
        |  |  | 480 | } | 
      
        |  |  | 481 |  | 
      
        |  |  | 482 | } | 
      
        |  |  | 483 | ?> |