fix php 5.6 in docker dev env (#1740)
[openemr.git] / vendor / mpdf / mpdf / qrcode / qrcode.class.php
blobe265f376d459c0c1f5a4dbfdd2c8f2a12b5221c8
1 <?php
2 /*
3 * Generateur de QRCode
4 * (QR Code is registered trademark of DENSO WAVE INCORPORATED | http://www.denso-wave.com/qrcode/)
5 * Fortement inspiré de "QRcode image PHP scripts version 0.50g (C)2000-2005,Y.Swetake"
6 *
7 * Distribué sous la licence LGPL.
9 * @author Laurent MINGUET <webmaster@spipu.net>
10 * @version 0.99
13 if (!defined('__CLASS_QRCODE__'))
15 define('__CLASS_QRCODE__', '0.99');
17 class QRcode
19 private $version_mx = 40; // numero de version maximal autorisé
20 private $type = 'bin'; // type de donnée
21 private $level = 'L'; // ECC
22 private $value = ''; // valeur a encoder
23 private $length = 0; // taille de la valeur
24 private $version = 0; // version
25 private $size = 0; // dimension de la zone data
26 private $qr_size = 0; // dimension du QRcode
28 private $data_bit = array(); // nb de bit de chacune des valeurs
29 private $data_val = array(); // liste des valeurs de bit différents
30 private $data_word = array(); // liste des valeurs tout ramené à 8bit
31 private $data_cur = 0; // position courante
32 private $data_num = 0; // position de la dimension
33 private $data_bits = 0; // nom de bit au total
34 private $max_data_bit = 0; // lilmite de nombre de bit maximal pour les datas
35 private $max_data_word = 0; // lilmite de nombre de mot maximal pour les datas
36 private $max_word = 0; // lilmite de nombre de mot maximal en global
38 private $ec = 0;
39 private $matrix = array();
40 private $matrix_remain = 0;
41 private $matrix_x_array = array();
42 private $matrix_y_array = array();
43 private $mask_array = array();
44 private $format_information_x1 = array();
45 private $format_information_y1 = array();
46 private $format_information_x2 = array();
47 private $format_information_y2 = array();
48 private $rs_block_order = array();
49 private $rs_ecc_codewords = 0;
50 private $byte_num = 0;
52 private $final = array();
53 private $disable_border = false;
56 /**
57 * Constructeur
59 * @param string message a encoder
60 * @param string niveau de correction d'erreur (ECC) : L, M, Q, H
61 * @return null
63 public function __construct($value, $level='L')
65 if (!in_array($level, array('L', 'M', 'Q', 'H')))
66 $this->ERROR('ECC non reconnu : L, M, Q, H');
68 $this->length = strlen($value);
69 if (!$this->length)
70 $this->ERROR('pas de data...');
72 $this->level = $level;
73 $this->value = &$value;
75 $this->data_bit = array();
76 $this->data_val = array();
77 $this->data_cur = 0;
78 $this->data_bits= 0;
80 $this->encode();
81 $this->loadECC();
82 $this->makeECC();
83 $this->makeMatrix();
86 /**
87 * permet de recuperer la taille du QRcode (le nombre de case de côté)
89 * @return int size of qrcode
91 public function getQrSize()
93 if ($this->disable_border)
94 return $this->qr_size-8;
95 else
96 return $this->qr_size;
99 public function disableBorder()
101 $this->disable_border = true;
105 * permet d'afficher le QRcode dans un pdf via FPDF
107 * @param FPDF objet fpdf
108 * @param float position X
109 * @param float position Y
110 * @param float taille du qrcode
111 * @param array couleur du background (R,V,B)
112 * @param array couleur des cases et du border (R,V,B)
113 * @return boolean true;
115 public function displayFPDF(&$fpdf, $x, $y, $w, $background=array(255,255,255), $color=array(0,0,0))
117 $size = $w;
118 $s = $size/$this->getQrSize();
120 $fpdf->SetDrawColor($color[0], $color[1], $color[2]);
121 $fpdf->SetFillColor($background[0], $background[1], $background[2]);
123 // rectangle de fond
124 if ($this->disable_border)
126 $s_min = 4;
127 $s_max = $this->qr_size-4;
128 $fpdf->Rect($x, $y, $size, $size, 'F');
130 else
132 $s_min = 0;
133 $s_max = $this->qr_size;
134 $fpdf->Rect($x, $y, $size, $size, 'FD');
137 $fpdf->SetFillColor($color[0], $color[1], $color[2]);
138 for($j=$s_min; $j<$s_max; $j++)
139 for($i=$s_min; $i<$s_max; $i++)
140 if ($this->final[$i + $j*$this->qr_size+1])
141 $fpdf->Rect($x+($i-$s_min)*$s, $y+($j-$s_min)*$s, $s, $s, 'F');
143 return true;
147 * permet d'afficher le QRcode au format HTML, à utiliser avec un style CSS
149 * @return boolean true;
151 public function displayHTML()
153 if ($this->disable_border)
155 $s_min = 4;
156 $s_max = $this->qr_size-4;
158 else
160 $s_min = 0;
161 $s_max = $this->qr_size;
163 echo '<table class="qr" cellpadding="0" cellspacing="0">'."\n";
164 for($y=$s_min; $y<$s_max; $y++)
166 echo '<tr>';
167 for($x=$s_min; $x<$s_max; $x++)
169 echo '<td class="'.($this->final[$x + $y*$this->qr_size+1] ? 'on' : 'off').'"></td>';
171 echo '</tr>'."\n";
173 echo '</table>';
175 return true;
179 * permet d'obtenir une image PNG
181 * @param float taille du qrcode
182 * @param array couleur du background (R,V,B)
183 * @param array couleur des cases et du border (R,V,B)
184 * @param string nom du fichier de sortie. si null : sortie directe
185 * @param integer qualité de 0 (aucune compression) a 9
186 * @return boolean true;
188 public function displayPNG($w=100, $background=array(255,255,255), $color=array(0,0,0), $filename = null, $quality = 0)
190 if ($this->disable_border)
192 $s_min = 4;
193 $s_max = $this->qr_size-4;
195 else
197 $s_min = 0;
198 $s_max = $this->qr_size;
200 $size = $w;
201 $s = $size/($s_max-$s_min);
203 // rectangle de fond
204 $im = imagecreatetruecolor($size,$size);
205 $c_case = imagecolorallocate($im,$color[0],$color[1],$color[2]);
206 $c_back = imagecolorallocate($im,$background[0],$background[1],$background[2]);
207 imagefilledrectangle($im,0,0,$size,$size,$c_back);
209 for($j=$s_min; $j<$s_max; $j++)
210 for($i=$s_min; $i<$s_max; $i++)
211 if ($this->final[$i + $j*$this->qr_size+1])
212 imagefilledrectangle($im,($i-$s_min)*$s,($j-$s_min)*$s,($i-$s_min+1)*$s-1,($j-$s_min+1)*$s-1,$c_case);
214 if ($filename)
216 imagepng($im, $filename, $quality);
218 else
220 header("Content-type: image/png");
221 imagepng($im);
223 imagedestroy($im);
224 return true;
227 private function ERROR($msg)
229 echo 'ERROR : '.$msg;
230 exit;
233 private function addData($val, $bit, $next = true)
235 $this->data_val[$this->data_cur] = $val;
236 $this->data_bit[$this->data_cur] = $bit;
237 if ($next)
239 $this->data_cur++;
240 return $this->data_cur-1;
242 else
244 return $this->data_cur;
248 private function encode()
250 // conversion des datas
251 if (preg_match('/[^0-9]/',$this->value))
253 if (preg_match('/[^0-9A-Z \$\*\%\+\-\.\/\:]/',$this->value))
255 // type : bin
256 $this->type = 'bin';
257 $this->addData(4, 4);
259 // taille. il faut garder l'indice, car besoin de correction
260 $this->data_num = $this->addData($this->length, 8); /* #version 1-9 */
261 $data_num_correction=array(0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8);
263 // datas
264 for ($i=0; $i<$this->length; $i++)
265 $this->addData(ord(substr($this->value, $i, 1)), 8);
267 else
269 // type : alphanum
270 $this->type = 'alphanum';
271 $this->addData(2, 4);
273 // taille. il faut garder l'indice, car besoin de correction
274 $this->data_num = $this->addData($this->length, 9); /* #version 1-9 */
275 $data_num_correction=array(0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4);
277 // datas
278 $an_hash=array(
279 '0'=>0,'1'=>1,'2'=>2,'3'=>3,'4'=>4,'5'=>5,'6'=>6,'7'=>7,'8'=>8,'9'=>9,
280 'A'=>10,'B'=>11,'C'=>12,'D'=>13,'E'=>14,'F'=>15,'G'=>16,'H'=>17,'I'=>18,'J'=>19,'K'=>20,'L'=>21,'M'=>22,
281 'N'=>23,'O'=>24,'P'=>25,'Q'=>26,'R'=>27,'S'=>28,'T'=>29,'U'=>30,'V'=>31,'W'=>32,'X'=>33,'Y'=>34,'Z'=>35,
282 ' '=>36,'$'=>37,'%'=>38,'*'=>39,'+'=>40,'-'=>41,'.'=>42,'/'=>43,':'=>44);
284 for ($i=0; $i<$this->length; $i++)
286 if (($i %2)==0)
287 $this->addData($an_hash[substr($this->value,$i,1)], 6, false);
288 else
289 $this->addData($this->data_val[$this->data_cur]*45+$an_hash[substr($this->value,$i,1)], 11, true);
291 unset($an_hash);
293 if (isset($this->data_bit[$this->data_cur]))
294 $this->data_cur++;
297 else
299 // type : num
300 $this->type = 'num';
301 $this->addData(1, 4);
303 //taille. il faut garder l'indice, car besoin de correction
304 $this->data_num = $this->addData($this->length, 10); /* #version 1-9 */
305 $data_num_correction=array(0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4);
307 // datas
308 for ($i=0; $i<$this->length; $i++)
310 if (($i % 3)==0)
311 $this->addData(substr($this->value,$i,1), 4, false);
312 else if (($i % 3)==1)
313 $this->addData($this->data_val[$this->data_cur]*10+substr($this->value,$i,1), 7, false);
314 else
315 $this->addData($this->data_val[$this->data_cur]*10+substr($this->value,$i,1), 10);
318 if (isset($this->data_bit[$this->data_cur]))
319 $this->data_cur++;
324 // calcul du nombre de bits
325 $this->data_bits=0;
326 foreach($this->data_bit as $bit)
327 $this->data_bits+= $bit;
329 // code ECC
330 $ec_hash = array('L'=>1, 'M'=>0, 'Q'=>3, 'H'=>2);
331 $this->ec = $ec_hash[$this->level];
333 // tableau de taille limite de bits
334 $max_bits = array(
335 0,128,224,352,512,688,864,992,1232,1456,1728,2032,2320,2672,2920,3320,3624,4056,4504,5016,5352,
336 5712,6256,6880,7312,8000,8496,9024,9544,10136,10984,11640,12328,13048,13800,14496,15312,15936,16816,17728,18672,
338 152,272,440,640,864,1088,1248,1552,1856,2192,2592,2960,3424,3688,4184,4712,5176,5768,6360,6888,
339 7456,8048,8752,9392,10208,10960,11744,12248,13048,13880,14744,15640,16568,17528,18448,19472,20528,21616,22496,23648,
341 72,128,208,288,368,480,528,688,800,976,1120,1264,1440,1576,1784,2024,2264,2504,2728,3080,
342 3248,3536,3712,4112,4304,4768,5024,5288,5608,5960,6344,6760,7208,7688,7888,8432,8768,9136,9776,10208,
344 104,176,272,384,496,608,704,880,1056,1232,1440,1648,1952,2088,2360,2600,2936,3176,3560,3880,
345 4096,4544,4912,5312,5744,6032,6464,6968,7288,7880,8264,8920,9368,9848,10288,10832,11408,12016,12656,13328
348 // determination automatique de la version necessaire
349 $this->version=1;
350 $i=1+40*$this->ec;
351 $j=$i+39;
352 while ($i<=$j)
354 if ($max_bits[$i]>=$this->data_bits+$data_num_correction[$this->version])
356 $this->max_data_bit=$max_bits[$i];
357 break;
359 $i++;
360 $this->version++;
363 // verification max version
364 if ($this->version>$this->version_mx)
365 $this->ERROR('too large version.');
367 // correctif sur le nombre de bits du strlen de la valeur
368 $this->data_bits+=$data_num_correction[$this->version];
369 $this->data_bit[$this->data_num]+=$data_num_correction[$this->version];
370 $this->max_data_word = ($this->max_data_bit/8);
372 // nombre de mots maximal
373 $max_words_array=array(0,26,44,70,100,134,172,196,242,292,346,404,466,532,581,655,733,815,901,991,1085,1156,
374 1258,1364,1474,1588,1706,1828,1921,2051,2185,2323,2465,2611,2761,2876,3034,3196,3362,3532,3706);
375 $this->max_word = $max_words_array[$this->version];
376 $this->size = 17 + 4*$this->version;
378 // nettoyages divers
379 unset($max_bits);
380 unset($data_num_correction);
381 unset($max_words_array);
382 unset($ec_hash);
384 // terminator
385 if ($this->data_bits<=$this->max_data_bit-4)
386 $this->addData(0, 4);
387 elseif ($this->data_bits<$this->max_data_bit)
388 $this->addData(0, $this->max_data_bit-$this->data_bits);
389 elseif ($this->data_bits>$this->max_data_bit)
390 $this->ERROR('Overflow error');
392 // construction des mots de 8 bit
393 $this->data_word = array();
394 $this->data_word[0] = 0;
395 $nb_word = 0;
397 $remaining_bit=8;
398 for($i=0; $i<$this->data_cur; $i++)
400 $buffer_val=$this->data_val[$i];
401 $buffer_bit=$this->data_bit[$i];
403 $flag = true;
404 while ($flag)
406 if ($remaining_bit>$buffer_bit)
408 $this->data_word[$nb_word]=((@$this->data_word[$nb_word]<<$buffer_bit) | $buffer_val);
409 $remaining_bit-=$buffer_bit;
410 $flag=false;
412 else
414 $buffer_bit-=$remaining_bit;
415 $this->data_word[$nb_word]=((@$this->data_word[$nb_word] << $remaining_bit) | ($buffer_val >> $buffer_bit));
416 $nb_word++;
418 if ($buffer_bit==0)
419 $flag=false;
420 else
421 $buffer_val= ($buffer_val & ((1 << $buffer_bit)-1) );
423 if ($nb_word<$this->max_data_word-1)
424 $this->data_word[$nb_word]=0;
425 $remaining_bit=8;
430 // completion du dernier mot si incomplet
431 if ($remaining_bit<8)
432 $this->data_word[$nb_word]=$this->data_word[$nb_word] << $remaining_bit;
433 else
434 $nb_word--;
436 // remplissage du reste
437 if ($nb_word<$this->max_data_word-1)
439 $flag=true;
440 while ($nb_word<$this->max_data_word-1)
442 $nb_word++;
443 if ($flag)
444 $this->data_word[$nb_word]=236;
445 else
446 $this->data_word[$nb_word]=17;
447 $flag=!$flag;
452 private function loadECC()
454 $matrix_remain_bit=array(0,0,7,7,7,7,7,0,0,0,0,0,0,0,3,3,3,3,3,3,3,4,4,4,4,4,4,4,3,3,3,3,3,3,3,0,0,0,0,0,0);
455 $this->matrix_remain = $matrix_remain_bit[$this->version];
456 unset($matrix_remain_bit);
458 // lecture du fichier : data file of geometry & mask for version V ,ecc level N
459 $this->byte_num = $this->matrix_remain+ 8*$this->max_word;
460 $filename = dirname(__FILE__)."/data/qrv".$this->version."_".$this->ec.".dat";
461 $fp1 = fopen ($filename, "rb");
462 $this->matrix_x_array = unpack("C*", fread($fp1,$this->byte_num));
463 $this->matrix_y_array = unpack("C*", fread($fp1,$this->byte_num));
464 $this->mask_array = unpack("C*", fread($fp1,$this->byte_num));
465 $this->format_information_x2 = unpack("C*", fread($fp1,15));
466 $this->format_information_y2 = unpack("C*", fread($fp1,15));
467 $this->rs_ecc_codewords = ord(fread($fp1,1));
468 $this->rs_block_order = unpack("C*", fread($fp1,128));
469 fclose($fp1);
470 $this->format_information_x1 = array(0,1,2,3,4,5,7,8,8,8,8,8,8,8,8);
471 $this->format_information_y1 = array(8,8,8,8,8,8,8,8,7,5,4,3,2,1,0);
475 private function makeECC()
477 // lecture du fichier : data file of caluclatin tables for RS encoding
478 $rs_cal_table_array = array();
479 $filename = dirname(__FILE__)."/data/rsc".$this->rs_ecc_codewords.".dat";
480 $fp0 = fopen ($filename, "rb");
481 for($i=0; $i<256; $i++)
482 $rs_cal_table_array[$i]=fread ($fp0,$this->rs_ecc_codewords);
483 fclose ($fp0);
485 $max_data_codewords = count($this->data_word);
487 // preparation
488 $j=0;
489 $rs_block_number=0;
490 $rs_temp[0]="";
491 for($i=0; $i<$max_data_codewords; $i++)
493 $rs_temp[$rs_block_number].=chr($this->data_word[$i]);
494 $j++;
495 if ($j>=$this->rs_block_order[$rs_block_number+1]-$this->rs_ecc_codewords)
497 $j=0;
498 $rs_block_number++;
499 $rs_temp[$rs_block_number]="";
503 // make
504 $rs_block_order_num=count($this->rs_block_order);
506 for($rs_block_number=0; $rs_block_number<$rs_block_order_num; $rs_block_number++)
508 $rs_codewords=$this->rs_block_order[$rs_block_number+1];
509 $rs_data_codewords=$rs_codewords-$this->rs_ecc_codewords;
511 $rstemp=$rs_temp[$rs_block_number].str_repeat(chr(0),$this->rs_ecc_codewords);
512 $padding_data=str_repeat(chr(0),$rs_data_codewords);
514 $j=$rs_data_codewords;
515 while($j>0)
517 $first=ord(substr($rstemp,0,1));
519 if ($first)
521 $left_chr=substr($rstemp,1);
522 $cal=$rs_cal_table_array[$first].$padding_data;
523 $rstemp=$left_chr ^ $cal;
525 else
526 $rstemp=substr($rstemp,1);
527 $j--;
530 $this->data_word=array_merge($this->data_word,unpack("C*",$rstemp));
534 private function makeMatrix()
536 // preparation
537 $this->matrix = array_fill(0, $this->size, array_fill(0, $this->size, 0));
539 // mettre les words
540 for($i=0; $i<$this->max_word; $i++)
542 $word = $this->data_word[$i];
543 for($j=8; $j>0; $j--)
545 $bit_pos = ($i<<3) + $j;
546 $this->matrix[ $this->matrix_x_array[$bit_pos] ][ $this->matrix_y_array[$bit_pos] ] = ((255*($word & 1)) ^ $this->mask_array[$bit_pos] );
547 $word = $word >> 1;
551 for($k=$this->matrix_remain; $k>0; $k--)
553 $bit_pos = $k + ( $this->max_word <<3);
554 $this->matrix[ $this->matrix_x_array[$bit_pos] ][ $this->matrix_y_array[$bit_pos] ] = ( 255 ^ $this->mask_array[$bit_pos] );
557 // mask select
558 $min_demerit_score=0;
559 $hor_master="";
560 $ver_master="";
561 $k=0;
562 while($k<$this->size)
564 $l=0;
565 while($l<$this->size)
567 $hor_master=$hor_master.chr($this->matrix[$l][$k]);
568 $ver_master=$ver_master.chr($this->matrix[$k][$l]);
569 $l++;
571 $k++;
574 $i=0;
575 $all_matrix=$this->size * $this->size;
577 while ($i<8)
579 $demerit_n1=0;
580 $ptn_temp=array();
581 $bit= 1<< $i;
582 $bit_r=(~$bit)&255;
583 $bit_mask=str_repeat(chr($bit),$all_matrix);
584 $hor = $hor_master & $bit_mask;
585 $ver = $ver_master & $bit_mask;
587 $ver_shift1=$ver.str_repeat(chr(170),$this->size);
588 $ver_shift2=str_repeat(chr(170),$this->size).$ver;
589 $ver_shift1_0=$ver.str_repeat(chr(0),$this->size);
590 $ver_shift2_0=str_repeat(chr(0),$this->size).$ver;
591 $ver_or=chunk_split(~($ver_shift1 | $ver_shift2),$this->size,chr(170));
592 $ver_and=chunk_split(~($ver_shift1_0 & $ver_shift2_0),$this->size,chr(170));
594 $hor=chunk_split(~$hor,$this->size,chr(170));
595 $ver=chunk_split(~$ver,$this->size,chr(170));
596 $hor=$hor.chr(170).$ver;
598 $n1_search="/".str_repeat(chr(255),5)."+|".str_repeat(chr($bit_r),5)."+/";
599 $n3_search=chr($bit_r).chr(255).chr($bit_r).chr($bit_r).chr($bit_r).chr(255).chr($bit_r);
601 $demerit_n3=substr_count($hor,$n3_search)*40;
602 $demerit_n4=floor(abs(( (100* (substr_count($ver,chr($bit_r))/($this->byte_num)) )-50)/5))*10;
604 $n2_search1="/".chr($bit_r).chr($bit_r)."+/";
605 $n2_search2="/".chr(255).chr(255)."+/";
606 $demerit_n2=0;
607 preg_match_all($n2_search1,$ver_and,$ptn_temp);
608 foreach($ptn_temp[0] as $str_temp)
610 $demerit_n2+=(strlen($str_temp)-1);
612 $ptn_temp=array();
613 preg_match_all($n2_search2,$ver_or,$ptn_temp);
614 foreach($ptn_temp[0] as $str_temp)
616 $demerit_n2+=(strlen($str_temp)-1);
618 $demerit_n2*=3;
620 $ptn_temp=array();
622 preg_match_all($n1_search,$hor,$ptn_temp);
623 foreach($ptn_temp[0] as $str_temp)
625 $demerit_n1+=(strlen($str_temp)-2);
627 $demerit_score=$demerit_n1+$demerit_n2+$demerit_n3+$demerit_n4;
629 if ($demerit_score<=$min_demerit_score || $i==0)
631 $mask_number=$i;
632 $min_demerit_score=$demerit_score;
635 $i++;
638 $mask_content=1 << $mask_number;
640 $format_information_value=(($this->ec << 3) | $mask_number);
641 $format_information_array=array("101010000010010","101000100100101",
642 "101111001111100","101101101001011","100010111111001","100000011001110",
643 "100111110010111","100101010100000","111011111000100","111001011110011",
644 "111110110101010","111100010011101","110011000101111","110001100011000",
645 "110110001000001","110100101110110","001011010001001","001001110111110",
646 "001110011100111","001100111010000","000011101100010","000001001010101",
647 "000110100001100","000100000111011","011010101011111","011000001101000",
648 "011111100110001","011101000000110","010010010110100","010000110000011",
649 "010111011011010","010101111101101");
651 for($i=0; $i<15; $i++)
653 $content=substr($format_information_array[$format_information_value],$i,1);
655 $this->matrix[$this->format_information_x1[$i]][$this->format_information_y1[$i]]=$content * 255;
656 $this->matrix[$this->format_information_x2[$i+1]][$this->format_information_y2[$i+1]]=$content * 255;
659 $this->final = unpack("C*", file_get_contents(dirname(__FILE__).'/data/modele'.$this->version.'.dat'));
660 $this->qr_size = $this->size+8;
662 for($x=0; $x<$this->size; $x++)
664 for($y=0; $y<$this->size; $y++)
666 if ($this->matrix[$x][$y] & $mask_content)
667 $this->final[($x+4) + ($y+4)*$this->qr_size+1] = true;