一个PHP的QRcode类与大家分享
前端之家收集整理的这篇文章主要介绍了
一个PHP的QRcode类与大家分享,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
类的代码:
<div class="codetitle"><a style="CURSOR: pointer" data="85023" class="copybut" id="copybut85023" onclick="doCopy('code85023')"> 代码如下:
<div class="codebody" id="code8
5023">
define('QR_MODE_NUL',-1);
define('QR_MODE_NUM',0);
define('QR_MODE_AN',1);
define('QR_MODE_8',2);
define('QR_MODE_KANJI',3);
define('QR_MODE_STRUCTURE',4);
// Levels of error correction.
define('QR_ECLEVEL_L',0);
define('QR_ECLEVEL_M',1);
define('QR_ECLEVEL_Q',2);
define('QR_ECLEVEL_H',3);
// Supported output formats
define('QR_FORMAT_TEXT',0);
define('QR_FORMAT_PNG',1);
class qrstr {
public static function set(&$srctab,$x,$y,$repl,$replLen = false) {
$srctab[$y] = substr_replace($srctab[$y],($replLen !== false)?substr($repl,$replLen):$repl,($replLen !== false)?$replLen:strlen($repl));
}
}
define('QR_CACHEABLE',false); // use cache - more disk reads but less
cpu power,masks and format templates are stored there
define('QR_CACHE_DIR',false); // used when QR_CACHEABLE === true
define('QR_LOG_DIR',false); // default error logs dir
define('QR_FIND_BEST_MASK',true); // if true,estimates best mask (spec. default,but extremally slow; set to false to significant performance boost but (propably) worst quality code
define('QR_FIND_FROM_RANDOM',2); // if false,checks all masks available,otherwise value tells count of masks need to be checked,mask id are got randomly
define('QR_DEFAULT_MASK',2); // when QR_FIND_BEST_MASK === false
define('QR_PNG_MAXIMUM_SIZE',1024); // maximum allowed png image width (in pixels),tune to make sure GD and
PHP can handle such big images
class QRtools {
public static function binarize($frame){
$len = count($frame);
foreach ($frame as &$frameLine) {
for($i=0; $i<$len; $i++) {
$frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
}
}
return $frame;
}
public static function tcpdfBarcodeArray($code,$mode = 'QR,L',$tcPdfVersion = '4.5.037'){
$barcode_array = array();
if (!is_array($mode))
$mode = explode(',',$mode);
$eccLevel = 'L';
if (count($mode) > 1) {
$eccLevel = $mode[1];
}
$qrTab = QRcode::text($code,false,$eccLevel);
$size = count($qrTab);
$barcode_array['num_rows'] = $size;
$barcode_array['num_cols'] = $size;
$barcode_array['bcode'] = array();
foreach ($qrTab as $line) {
$arrAdd = array();
foreach(str_split($line) as $char)
$arrAdd[] = ($char=='1')?1:0;
$barcode_array['bcode'][] = $arrAdd;
}
return $barcode_array;
}
public static function clearCache(){
self::$frames = array();
}
public static function buildCache(){
QRtools::markTime('before_build_cache');
$mask = new QRmask();
for ($a=1; $a <= QRSPEC_VERSION_MAX; $a++) {
$frame = QRspec::newFrame($a);
if (QR_IMAGE) {
$fileName = QR_CACHE
DIR.'frame'.$a.'.png';
QRimage::png(self::binarize($frame),$fileName,1,0);
}
$width = count($frame);
$bitMask = array_fill(0,$width,array_fill(0,0));
for ($maskNo=0; $maskNo<8; $maskNo++)
$mask->makeMaskNo($maskNo,$frame,$bitMask,true);
}
QRtools::markTime('after_build_cache');
}
public static function log($outfile,$err){
if (QR_LOG_DIR !== false) {
if ($err != '') {
if ($outfile !== false) {
file_put_contents(QR_LOG_DIR.basename($outfile).'-errors.txt',date('Y-m-d H:i:s').': '.$err,FILE_APPEND);
} else {
file_put_contents(QR_LOG_DIR.'errors.txt',FILE_APPEND);
}
}
}
}
public static function dumpMask($frame){
$width = count($frame);
for($y=0;$y<$width;$y++) {
for($x=0;$x<$width;$x++) {
echo ord($frame[$y][$x]).',';
}
}
}
public static function markTime($markerId){
list($usec,$sec) = explode(" ",microtime());
$time = ((float)$usec + (float)$sec);
if (!isset($GLOBALS['qr_time_bench']))
$GLOBALS['qr_time_bench'] = array();
$GLOBALS['qr_time_bench'][$markerId] = $time;
}
public static function timeBenchmark(){
self::markTime('finish');
$lastTime = 0;
$startTime = 0;
$p = 0;
echo '<table cellpadding="3" cellspacing="1">
<tr style="border-bottom:1px solid silver"><td colspan="2" style="text-align:center">BENCHMARK</td></tr> ';
foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) {
if ($p > 0) {
echo '<tr><th style="text-align:right">till '.$markerId.': <td>'.number_format($thisTime-$lastTime,6).'s</td></tr>';
} else {
$startTime = $thisTime;
}
$p++;
$lastTime = $thisTime;
}
echo '
<tr style="border-top:2px solid black"><th style="text-align:right">TOTAL: <td>'.number_format($lastTime-$startTime,6).'s</td></tr>
</table>';
}
}
//##########################################################################
QRtools::markTime('start');
define('QRSPEC_VERSION_MAX',40);
define('QRSPEC_WIDTH_MAX',177);
define('QRCAP_WIDTH',0);
define('QRCAP_WORDS',1);
define('QRCAP_REMINDER',2);
define('QRCAP_EC',3);
class QRspec {
public static $capacity = array(
array( 0,array( 0,0)),
array( 21,26,array( 7,10,13,17)),// 1
array( 25,44,7,array( 10,16,22,28)),
array( 29,70,array( 15,36,44)),
array( 33,100,array( 20,52,64)),
array( 37,134,array( 26,48,72,88)),// 5
array( 41,172,array( 36,64,96,112)),
array( 45,196,array( 40,108,130)),
array( 49,242,array( 48,88,132,156)),
array( 53,292,array( 60,110,160,192)),
array( 57,346,array( 72,130,192,224)),//10
array( 61,
404,array( 80,150,224,264)),
array( 65,466,array( 96,176,260,308)),
array( 69,532,array( 104,198,288,352)),
array( 73,581,3,array( 120,216,320,384)),
array( 77,655,array( 132,240,360,432)),//15
array( 81,733,array( 144,280,408,480)),
array( 85,815,array( 168,308,448,532)),
array( 89,901,array( 180,338,504,588)),
array( 93,991,array( 196,364,546,650)),
array( 97,1085,array( 224,416,600,700)),//20
array(101,1156,4,442,644,750)),
array(105,1258,array( 252,476,690,816)),
array(109,1364,array( 270,750,900)),
array(113,1474,array( 300,560,810,960)),
array(117,1588,array( 312,588,870,1050)),//25
array(121,1706,array( 336,952,1110)),
array(125,1828,array( 360,700,1020,1200)),
array(129,1921,array( 390,728,1050,1260)),
array(133,2051,array( 420,784,1140,1350)),
array(137,2185,array( 450,812,1200,1440)),//30
array(141,2323,array( 480,868,1290,1530)),
array(145,2465,array( 510,924,1350,1620)),
array(149,2611,array( 540,980,1440,1710)),
array(153,2761,array( 570,1036,1530,1800)),
array(157,2876,1064,1590,1890)),//35
array(161,3034,array( 600,1120,1680,1980)),
array(165,3196,array( 630,1204,1770,2100)),
array(169,3362,array( 660,1260,1860,2220)),
array(173,3532,array( 720,1316,1950,2310)),
array(177,3706,array( 750,1372,2040,2430)) //40
);
public static function getDataLength($version,$level){
return self::$capacity[$version][QRCAP_WORDS] - self::$capacity[$version][QRCAP_EC][$level];
}
public static function getECCLength($version,$level){
return self::$capacity[$version][QRCAP_EC][$level];
}
public static function getWidth($version){
return self::$capacity[$version][QRCAP_WIDTH];
}
public static function getRemainder($version){
return self::$capacity[$version][QRCAP_REMINDER];
}
public static function getMinimumVersion($size,$level){
for($i=1; $i<= QRSPEC_VERSION_MAX; $i++) {
$words = self::$capacity[$i][QRCAP_WORDS] - self::$capacity[$i][QRCAP_EC][$level];
if($words >= $size)
return $i;
}
return -1;
}
//######################################################################
public static $lengthTableBits = array(
array(10,12,14),
array( 9,11,13),
array( 8,16),12)
);
public static function lengthIndicator($mode,$version){
if ($mode == QR_MODE_STRUCTURE)
return 0;
if ($version <= 9) {
$l = 0;
} else if ($version <= 26) {
$l = 1;
} else {
$l = 2;
}
return self::$lengthTableBits[$mode][$l];
}
public static function maximumWords($mode,$version){
if($mode == QR_MODE_STRUCTURE)
return 3;
if($version <= 9) {
$l = 0;
} else if($version <= 26) {
$l = 1;
} else {
$l = 2;
}
$bits = self::$lengthTableBits[$mode][$l];
$words = (1 << $bits) - 1;
if($mode == QR_MODE_KANJI) {
$words
= 2; // the number of bytes is required
}
return $words;
}
public static $eccTable = array(
array(array( 0,0),
array(array( 1,array( 1,// 1
array(array( 1,array( 2,array( 4,2),2)),// 5
array(array( 2,
array(array( 2,4),1)),array( 3,4)),1),array( 6,//10
array(array( 4,8)),6),
array(array( 4,array( 8,array(12,
array(array( 3,5),array(11,5)),
array(array( 5,array( 5,7),7)),//15
array(array( 5,3),array(15,13)),array(10,15),array( 9,array(17,19)),11),16)),10)),//20
array(array( 4,array(19,6)),array(34,array(16,14)),
array(array( 6,array(30,
array(array( 8,22),array(22,//25
array(array(10,array(28,array(33,26),10),23),31),31)),
array(array( 7,array(21,37),26)),25),array(23,25)),//30
array(array(13,29),array(42,
array(array(17,35),35)),array(14,21),array(29,19),46)),
array(array(13,array(44,array(59,
array(array(12,array(39,41)),//35
array(array( 6,34),array(46,array(49,array(24,18),array(13,32),array(48,32)),
array(array(20,array(40,array(43,67)),
array(array(19,array(18,array(20,61)),//40
);
// CACHEABLE!!!
public static function getEccSpec($version,$level,array &$spec){
if (count($spec) < 5) {
$spec = array(0,0);
}
$b1 = self::$eccTable[$version][$level][0];
$b2 = self::$eccTable[$version][$level][1];
$data = self::getDataLength($version,$level);
$ecc = self::getECCLength($version,$level);
if($b2 == 0) {
$spec[0] = $b1;
$spec[1] = (int)($data / $b1);
$spec[2] = (int)($ecc / $b1);
$spec[3] = 0;
$spec[4] = 0;
} else {
$spec[0] = $b1;
$spec[1] = (int)($data / ($b1 + $b2));
$spec[2] = (int)($ecc / ($b1 + $b2));
$spec[3] = $b2;
$spec[4] = $spec[1] + 1;
}
}
public static $alignmentPattern = array(
array( 0,
array( 0,array(26,// 1- 5
array(34,38),42),46),50),// 6-10
array(30,54),array(32,58),62),48),//11-15
array(26,56),//16-20
array(28,//21-25
array(30,52),//26-30
array(30,60),//31-35
array(24,//35-40
);
public static function putAlignmentMarker(array &$frame,$ox,$oy){
$finder = array(
"\xa1\xa1\xa1\xa1\xa1",
"\xa1\xa0\xa0\xa0\xa1",
"\xa1\xa0\xa1\xa0\xa1",
"\xa1\xa1\xa1\xa1\xa1"
);
$yStart = $oy-2;
$xStart = $ox-2;
for($y=0; $y<5; $y++) {
QRstr::set($frame,$xStart,$yStart+$y,$finder[$y]);
}
}
public static function putAlignmentPattern($version,&$frame,$width){
if($version < 2)
return;
$d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0];
if($d < 0) {
$w = 2;
} else {
$w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2);
}
if($w $w - 3 == 1) {
$x = self::$alignmentPattern[$version][0];
$y = self::$alignmentPattern[$version][0];
self::putAlignmentMarker($frame,$y);
return;
}
$cx = self::$alignmentPattern[$version][0];
for($x=1; $x<$w - 1; $x++) {
self::putAlignmentMarker($frame,6,$cx);
self::putAlignmentMarker($frame,$cx,6);
$cx += $d;
}
$cy = self::$alignmentPattern[$version][0];
for($y=0; $y<$w-1; $y++) {
$cx = self::$alignmentPattern[$version][0];
for($x=0; $x<$w-1; $x++) {
self::putAlignmentMarker($frame,$cy);
$cx += $d;
}
$cy += $d;
}
}
public static $versionPattern = array(
0x07c94,0x085bc,0x09a99,0x0a4d3,0x0bbf6,0x0c762,0x0d847,0x0e60d,
0x0f928,0x10b78,0x1145d,0x12a17,0x13532,0x149a6,0x15683,0x168c9,
0x177ec,0x18ec4,0x191e1,0x1afab,0x1b08e,0x1cc1a,0x1d33f,0x1ed75,
0x1f250,0x209d5,0x216f0,0x228ba,0x2379f,0x24b0b,0x2542e,0x26a64,
0x27541,0x28c69
);
public static function getVersionPattern($version){
if($version < 7 || $version > QRSPEC_VERSION_MAX)
return 0;
return self::$versionPattern[$version -7];
}
// Format information --------------------------------------------------
// See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib)
public static $formatInfo = array(
array(0x77c4,0x72f3,0x7daa,0x789d,0x662f,0x6318,0x6c41,0x6976),
array(0x5412,0x5125,0x5e7c,0x5b4b,0x45f9,0x40ce,0x4f97,0x4aa0),
array(0x355f,0x3068,0x3f31,0x3a06,0x24b4,0x2183,0x2eda,0x2bed),
array(0x1689,0x13be,0x1ce7,0x19d0,0x0762,0x0255,0x0d0c,0x083b)
);
public static function getFormatInfo($mask,$level){
if($mask < 0 || $mask > 7)
return 0;
if($level < 0 || $level > 3)
return 0;
return self::$formatInfo[$level][$mask];
}
// Frame ---------------------------------------------------------------
// Cache of initial frames.
public static $frames = array();
public static function putFinderPattern(&$frame,$oy){
$finder = array(
"\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
"\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
"\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
"\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
);
for($y=0; $y<7; $y++) {
QRstr::set($frame,$oy+$y,$finder[$y]);
}
}
public static function createFrame($version){
$width = self::$capacity[$version][QRCAP_WIDTH];
$frameLine = str_repeat ("\0",$width);
$frame = array_fill(0,$frameLine);
// Finder pattern
self::putFinderPattern($frame,0);
self::putFinderPattern($frame,$width - 7,$width - 7);
// Separator
$yOffset = $width - 7;
for($y=0; $y<7; $y++) {
$frame[$y][7] = "\xc0";
$frame[$y][$width - 8] = "\xc0";
$frame[$yOffset][7] = "\xc0";
$yOffset++;
}
$setPattern = str_repeat("\xc0",8);
QRstr::set($frame,$setPattern);
QRstr::set($frame,$width-8,$width - 8,$setPattern);
// Format info
$setPattern = str_repeat("\x84",9);
QRstr::set($frame,8,$setPattern,8);
$yOffset = $width - 8;
for($y=0; $y<8; $y++,$yOffset++) {
$frame[$y][8] = "\x84";
$frame[$yOffset][8] = "\x84";
}
// Timing pattern
for($i=1; $i<$width-15; $i++) {
$frame[6][7+$i] = chr(0x90 | ($i & 1));
$frame[7+$i][6] = chr(0x90 | ($i & 1));
}
// Alignment pattern
self::putAlignmentPattern($version,$width);
// Version information
if($version >= 7) {
$vinf = self::getVersionPattern($version);
$v = $vinf;
for($x=0; $x<6; $x++) {
for($y=0; $y<3; $y++) {
$frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
$v = $v >> 1;
}
}
$v = $vinf;
for($y=0; $y<6; $y++) {
for($x=0; $x<3; $x++) {
$frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
$v = $v >> 1;
}
}
}
// and a little bit...
$frame[$width - 8][8] = "\x81";
return $frame;
}
public static function debug($frame,$binary_mode = false){
if ($binary_mode) {
foreach ($frame as &$frameLine) {
$frameLine = join('<span class="m">',explode('0',$frameLine));
$frameLine = join('██',explode('1',$frameLine));
}
echo '
<br/ ><br/ ><br/ >';
echo join("<br/ >",$frame);
echo '
<br/ ><br/ ><br/ ><br/ ><br/ ><br/ >';
} else {
foreach ($frame as &$frameLine) {
$frameLine = join('<span class="m">',explode("\xc0",$frameLine));
$frameLine = join('<span class="m">▒',explode("\xc1",$frameLine));
$frameLine = join('<span class="p">',explode("\xa0",$frameLine));
$frameLine = join('<span class="p">▒',explode("\xa1",$frameLine));
$frameLine = join('<span class="s">◇',explode("\x84",$frameLine)); //format 0
$frameLine = join('<span class="s">◆',explode("\x85",$frameLine)); //format 1
$frameLine = join('<span class="x">☢',explode("\x81",$frameLine)); //special bit
$frameLine = join('<span class="c">',explode("\x90",$frameLine)); //clock 0
$frameLine = join('<span class="c">◷',explode("\x91",$frameLine)); //clock 1
$frameLine = join('<span class="f">',explode("\x88",$frameLine)); //version
$frameLine = join('<span class="f">▒',explode("\x89",$frameLine)); //version
$frameLine = join('♦',explode("\x01",$frameLine));
$frameLine = join('⋅',explode("\0",$frameLine));
}
echo "
";
echo join("<br/ >",$frame);
echo "
";
}
}
public static function serial($frame){
return gzcompress(join("\n",$frame),9);
}
public static function unserial($code){
return explode("\n",gzuncompress($code));
}
public static function newFrame($version){
if($version < 1 || $version > QRSPEC_VERSION_MAX)
return null;
if(!isset(self::$frames[$version])) {
$fileName = QR_CACHE
DIR.'frame'.$version.'.dat';
if (QR_CACHEABLE) {
if (file_exists($fileName)) {
self::$frames[$version] = self::unserial(file_get_contents($fileName));
} else {
self::$frames[$version] = self::createFrame($version);
file_put_contents($fileName,self::serial(self::$frames[$version]));
}
} else {
self::$frames[$version] = self::createFrame($version);
}
}
if(is_null(self::$frames[$version]))
return null;
return self::$frames[$version];
}
public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; }
public static function rsBlockNum1($spec) { return $spec[0]; }
public static function rsDataCodes1($spec) { return $spec[1]; }
public static function rsEccCodes1($spec) { return $spec[2]; }
public static function rsBlockNum2($spec) { return $spec[3]; }
public static function rsDataCodes2($spec) { return $spec[4]; }
public static function rsEccCodes2($spec) { return $spec[2]; }
public static function rsDataLength($spec) { return ($spec[0]
$spec[1]) + ($spec[3] $spec[4]); }
public static function rsEccLength($spec) { return ($spec[0] + $spec[3])
$spec[2]; }
}
define('QR_IMAGE',true);
class QRimage {
public static function png($frame,$filename = false,$pixelPerPoint = 4,$outerFrame = 4,$saveandprint=FALSE){
$image = self::image($frame,$pixelPerPoint,$outerFrame);
if ($filename === false) {
Header("Content-type: image/png");
ImagePng($image);
} else {
if($saveandprint===TRUE){
ImagePng($image,$filename);
header("Content-type: image/png");
ImagePng($image);
}else{
ImagePng($image,$filename);
}
}
ImageDestroy($image);
}
public static function jpg($frame,$pixelPerPoint = 8,$q = 85){
$image = self::image($frame,$outerFrame);
if ($filename === false) {
Header("Content-type: image/jpeg");
ImageJpeg($image,null,$q);
} else {
ImageJpeg($image,$filename,$q);
}
ImageDestroy($image);
}
private static function image($frame,$outerFrame = 4){
$h = count($frame);
$w = strlen($frame[0]);
$imgW = $w + 2$outerFrame;
$imgH = $h + 2
$outerFrame;
$base_image =ImageCreate($imgW,$imgH);
$col[0] = ImageColorAllocate($base_image,255,255);
$col[1] = ImageColorAllocate($base_image,0);
imagefill($base_image,$col[0]);
for($y=0; $y<$h; $y++) {
for($x=0; $x<$w; $x++) {
if ($frame[$y][$x] == '1') {
ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]);
}
}
}
$target_image =ImageCreate($imgW $pixelPerPoint,$imgH
$pixelPerPoint);
ImageCopyResized($target_image,$base_image,$imgW $pixelPerPoint,$imgH
$pixelPerPoint,$imgW,$imgH);
ImageDestroy($base_image);
return $target_image;
}
}
define('STRUCTURE_HEADER_BITS',20);
define('MAX_STRUCTURED_SYMBOLS',16);
class QRinputItem {
public $mode;
public $size;
public $data;
public $bstream;
public function __construct($mode,$size,$data,$bstream = null){
$setData = array_slice($data,$size);
if (count($setData) < $size) {
$setData = array_merge($setData,$size-count($setData),0));
}
if(!QRinput::check($mode,$setData)) {
throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',$setData));
return null;
}
$this->mode = $mode;
$this->size = $size;
$this->data = $setData;
$this->bstream = $bstream;
}
public function encodeModeNum($version){
try {
$words = (int)($this->size / 3);
$bs = new QRbitstream();
$val = 0x1;
$bs->appendNum(4,$val);
$bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM,$version),$this->size);
for($i=0; $i<$words; $i++) {
$val = (ord($this->data[$i3 ]) - ord('0'))
100;
$val += (ord($this->data[$i3+1]) - ord('0'))
10;
$val += (ord($this->data[$i3+2]) - ord('0'));
$bs->appendNum(10,$val);
}
if($this->size - $words
3 == 1) {
$val = ord($this->data[$words3]) - ord('0');
$bs->appendNum(4,$val);
} else if($this->size - $words
3 == 2) {
$val = (ord($this->data[$words3 ]) - ord('0'))
10;
$val += (ord($this->data[$words3+1]) - ord('0'));
$bs->appendNum(7,$val);
}
$this->bstream = $bs;
return 0;
} catch (Exception $e) {
return -1;
}
}
public function encodeModeAn($version){
try {
$words = (int)($this->size / 2);
$bs = new QRbitstream();
$bs->appendNum(4,0x02);
$bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN,$this->size);
for($i=0; $i<$words; $i++) {
$val = (int)QRinput::lookAnTable(ord($this->data[$i
2 ])) 45;
$val += (int)QRinput::lookAnTable(ord($this->data[$i
2+1]));
$bs->appendNum(11,$val);
}
if($this->size & 1) {
$val = QRinput::lookAnTable(ord($this->data[$words 2]));
$bs->appendNum(6,$val);
}
$this->bstream = $bs;
return 0;
} catch (Exception $e) {
return -1;
}
}
public function encodeMode8($version){
try {
$bs = new QRbitstream();
$bs->appendNum(4,0x4);
$bs->appendNum(QRspec::lengthIndicator(QR_MODE_8,$this->size);
for($i=0; $i<$this->size; $i++) {
$bs->appendNum(8,ord($this->data[$i]));
}
$this->bstream = $bs;
return 0;
} catch (Exception $e) {
return -1;
}
}
public function encodeModeKanji($version){
try {
$bs = new QRbitrtream();
$bs->appendNum(4,0x8);
$bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI,(int)($this->size / 2));
for($i=0; $i<$this->size; $i+=2) {
$val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);
if($val <= 0x9ffc) {
$val -= 0x8140;
} else {
$val -= 0xc140;
}
$h = ($val >> 8)
0xc0;
$val = ($val & 0xff) + $h;
$bs->appendNum(13,$val);
}
$this->bstream = $bs;
return 0;
} catch (Exception $e) {
return -1;
}
}
public function encodeModeStructure(){
try {
$bs = new QRbitstream();
$bs->appendNum(4,0x03);
$bs->appendNum(4,ord($this->data[1]) - 1);
$bs->appendNum(4,ord($this->data[0]) - 1);
$bs->appendNum(8,ord($this->data[2]));
$this->bstream = $bs;
return 0;
} catch (Exception $e) {
return -1;
}
}
public function estimateBitStreamSizeOfEntry($version){
$bits = 0;
if($version == 0)
$version = 1;
switch($this->mode) {
case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break;
case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break;
case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break;
case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break;
case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS;
default:
return 0;
}
$l = QRspec::lengthIndicator($this->mode,$version);
$m = 1 << $l;
$num = (int)(($this->size + $m - 1) / $m);
$bits += $num (4 + $l);
return $bits;
}
public function encodeBitStream($version){
try {
unset($this->bstream);
$words = QRspec::maximumWords($this->mode,$version);
if($this->size > $words) {
$st1 = new QRinputItem($this->mode,$words,$this->data);
$st2 = new QRinputItem($this->mode,$this->size - $words,array_slice($this->data,$words));
$st1->encodeBitStream($version);
$st2->encodeBitStream($version);
$this->bstream = new QRbitstream();
$this->bstream->append($st1->bstream);
$this->bstream->append($st2->bstream);
unset($st1);
unset($st2);
} else {
$ret = 0;
switch($this->mode) {
case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break;
case QR_MODE_AN: $ret = $this->encodeModeAn($version); break;
case QR_MODE_8: $ret = $this->encodeMode8($version); break;
case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break;
case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break;
default:
break;
}
if($ret < 0)
return -1;
}
return $this->bstream->size();
} catch (Exception $e) {
return -1;
}
}
};
class QRinput {
public $items;
private $version;
private $level;
public function
construct($version = 0,$level = QR_ECLEVEL_L){
if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {
throw new Exception('Invalid version no');
return NULL;
}
$this->version = $version;
$this->level = $level;
}
public function getVersion(){
return $this->version;
}
public function setVersion($version){
if($version < 0 || $version > QRSPEC_VERSION_MAX) {
throw new Exception('Invalid version no');
return -1;
}
$this->version = $version;
return 0;
}
public function getErrorCorrectionLevel(){
return $this->level;
}
public function setErrorCorrectionLevel($level){
if($level > QR_ECLEVEL_H) {
throw new Exception('Invalid ECLEVEL');
return -1;
}
$this->level = $level;
return 0;
}
public function appendEntry(QRinputItem $entry){
$this->items[] = $entry;
}
public function append($mode,$data){
try {
$entry = new QRinputItem($mode,$data);
$this->items[] = $entry;
return 0;
} catch (Exception $e) {
return -1;
}
}
public function insertStructuredAppendHeader($size,$index,$parity){
if( $size > MAX_STRUCTURED_SYMBOLS ) {
throw new Exception('insertStructuredAppendHeader wrong size');
}
if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {
throw new Exception('insertStructuredAppendHeader wrong index');
}
$buf = array($size,$parity);
try {
$entry = new QRinputItem(QR_MODE_STRUCTURE,buf);
array_unshift($this->items,$entry);
return 0;
} catch (Exception $e) {
return -1;
}
}
public function calcParity(){
$parity = 0;
foreach($this->items as $item) {
if($item->mode != QR_MODE_STRUCTURE) {
for($i=$item->size-1; $i>=0; $i--) {
$parity ^= $item->data[$i];
}
}
}
return $parity;
}
public static function checkModeNum($size,$data){
for($i=0; $i<$size; $i++) {
if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
return false;
}
}
return true;
}
public static function estimateBitsModeNum($size){
$w = (int)$size / 3;
$bits = $w 10;
switch($size - $w 3) {
case 1:
$bits += 4;
break;
case 2:
$bits += 7;
break;
default:
break;
}
return $bits;
}
public static $anTable = array(
-1,-1,
-1,
36,37,38,39,40,41,42,43,
0,2,5,9,14,15,17,18,19,20,21,23,24,
25,27,28,29,30,31,32,33,34,35,-1
);
public static function lookAnTable($c){
return (($c > 127)?-1:self::$anTable[$c]);
}
public static function checkModeAn($size,$data){
for($i=0; $i<$size; $i++) {
if (self::lookAnTable(ord($data[$i])) == -1) {
return false;
}
}
return true;
}
public static function estimateBitsModeAn($size){
$w = (int)($size / 2);
$bits = $w 11;
if($size & 1) {
$bits += 6;
}
return $bits;
}
public static function estimateBitsMode8($size){
return $size 8;
}
public function estimateBitsModeKanji($size){
return (int)(($size / 2) 13);
}
public static function checkModeKanji($size,$data){
if($size & 1)
return false;
for($i=0; $i<$size; $i+=2) {
$val = (ord($data[$i]) << 8) | ord($data[$i+1]);
if( $val < 0x8140
|| ($val > 0x9ffc && $val < 0xe040)
|| $val > 0xebbf) {
return false;
}
}
return true;
}
public static function check($mode,$data){
if($size <= 0)
return false;
switch($mode) {
case QR_MODE_NUM: return self::checkModeNum($size,$data); break;
case QR_MODE_AN: return self::checkModeAn($size,$data); break;
case QR_MODE_KANJI: return self::checkModeKanji($size,$data); break;
case QR_MODE_8: return true; break;
case QR_MODE_STRUCTURE: return true; break;
default:
break;
}
return false;
}
public function estimateBitStreamSize($version){
$bits = 0;
foreach($this->items as $item) {
$bits += $item->estimateBitStreamSizeOfEntry($version);
}
return $bits;
}
public function estimateVersion(){
$version = 0;
$prev = 0;
do {
$prev = $version;
$bits = $this->estimateBitStreamSize($prev);
$version = QRspec::getMinimumVersion((int)(($bits + 7) / 8),$this->level);
if ($version < 0) {
return -1;
}
} while ($version > $prev);
return $version;
}
public static function lengthOfCode($mode,$version,$bits){
$payload = $bits - 4 - QRspec::lengthIndicator($mode,$version);
switch($mode) {
case QR_MODE_NUM:
$chunks = (int)($payload / 10);
$remain = $payload - $chunks 10;
$size = $chunks 3;
if($remain >= 7) {
$size += 2;
} else if($remain >= 4) {
$size += 1;
}
break;
case QR_MODE_AN:
$chunks = (int)($payload / 11);
$remain = $payload - $chunks 11;
$size = $chunks 2;
if($remain >= 6)
$size++;
break;
case QR_MODE_8:
$size = (int)($payload / 8);
break;
case QR_MODE_KANJI:
$size = (int)(($payload / 13) 2);
break;
case QR_MODE_STRUCTURE:
$size = (int)($payload / 8);
break;
default:
$size = 0;
break;
}
$maxsize = QRspec::maximumWords($mode,$version);
if($size < 0) $size = 0;
if($size > $maxsize) $size = $maxsize;
return $size;
}
public function createBitStream(){
$total = 0;
foreach($this->items as $item) {
$bits = $item->encodeBitStream($this->version);
if($bits < 0)
return -1;
$total += $bits;
}
return $total;
}
public function convertData(){
$ver = $this->estimateVersion();
if($ver > $this->getVersion()) {
$this->setVersion($ver);
}
for(;;) {
$bits = $this->createBitStream();
if($bits < 0)
return -1;
$ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8),$this->level);
if($ver < 0) {
throw new Exception('WRONG VERSION');
return -1;
} else if($ver > $this->getVersion()) {
$this->setVersion($ver);
} else {
break;
}
}
return 0;
}
public function appendPaddingBit(&$bstream){
$bits = $bstream->size();
$maxwords = QRspec::getDataLength($this->version,$this->level);
$maxbits = $maxwords 8;
if ($maxbits == $bits) {
return 0;
}
if ($maxbits - $bits < 5) {
return $bstream->appendNum($maxbits - $bits,0);
}
$bits += 4;
$words = (int)(($bits + 7) / 8);
$padding = new QRbitstream();
$ret = $padding->appendNum($words 8 - $bits + 4,0);
if($ret < 0)
return $ret;
$padlen = $maxwords - $words;
if($padlen > 0) {
$padbuf = array();
for($i=0; $i<$padlen; $i++) {
$padbuf[$i] = ($i&1)?0x11:0xec;
}
$ret = $padding->appendBytes($padlen,$padbuf);
if($ret < 0)
return $ret;
}
$ret = $bstream->append($padding);
return $ret;
}
public function mergeBitStream(){
if($this->convertData() < 0) {
return null;
}
$bstream = new QRbitstream();
foreach($this->items as $item) {
$ret = $bstream->append($item->bstream);
if($ret < 0) {
return null;
}
}
return $bstream;
}
public function getBitStream(){
$bstream = $this->mergeBitStream();
if($bstream == null) {
return null;
}
$ret = $this->appendPaddingBit($bstream);
if($ret < 0) {
return null;
}
return $bstream;
}
public function getByteStream(){
$bstream = $this->getBitStream();
if($bstream == null) {
return null;
}
return $bstream->toByte();
}
}
class QRbitstream {
public $data = array();
public function size(){
return count($this->data);
}
public function allocate($setLength){
$this->data = array_fill(0,$setLength,0);
return 0;
}
public static function newFromNum($bits,$num){
$bstream = new QRbitstream();
$bstream->allocate($bits);
$mask = 1 << ($bits - 1);
for($i=0; $i<$bits; $i++) {
if($num & $mask) {
$bstream->data[$i] = 1;
} else {
$bstream->data[$i] = 0;
}
$mask = $mask >> 1;
}
return $bstream;
}
public static function newFromBytes($size,$data){
$bstream = new QRbitstream();
$bstream->allocate($size * 8);
$p=0;
for($i=0; $i<$size; $i++) {
$mask = 0x80;
for($j=0; $j<8; $j++) {
if($data[$i] & $mask) {
$bstream->data[$p] = 1;
} else {
$bstream->data[$p] = 0;
}
$p++;
$mask = $mask >> 1;
}
}
return $bstream;
}
public function append(QRbitstream $arg){
if (is_null($arg)) {
return -1;
}
if($arg->size() == 0) {
return 0;
}
if($this->size() == 0) {
$this->data = $arg->data;
return 0;
}
$this->data = array_values(array_merge($this->data,$arg->data));
return 0;
}
public function appendNum($bits,$num){
if ($bits == 0)
return 0;
$b = QRbitstream::newFromNum($bits,$num);
if(is_null($b))
return -1;
$ret = $this->append($b);
unset($b);
return $ret;
}
public function appendBytes($size,$data){
if ($size == 0)
return 0;
$b = QRbitstream::newFromBytes($size,$data);
if(is_null($b))
return -1;
$ret = $this->append($b);
unset($b);
return $ret;
}
public function toByte(){
$size = $this->size();
if($size == 0) {
return array();
}
$data = array_fill(0,(int)(($size + 7) / 8),0);
$bytes = (int)($size / 8);
$p = 0;
for($i=0; $i<$bytes; $i++) {
$v = 0;
for($j=0; $j<8; $j++) {
$v = $v << 1;
$v |= $this->data[$p];
$p++;
}
$data[$i] = $v;
}
if($size & 7) {
$v = 0;
for($j=0; $j<($size & 7); $j++) {
$v = $v << 1;
$v |= $this->data[$p];
$p++;
}
$data[$bytes] = $v;
}
return $data;
}
}
class QRsplit {
public $dataStr = '';
public $input;
public $modeHint;
public function construct($dataStr,$input,$modeHint){
$this->dataStr = $dataStr;
$this->input = $input;
$this->modeHint = $modeHint;
}
public static function isdigitat($str,$pos){
if ($pos >= strlen($str))
return false;
return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
}
public static function isalnumat($str,$pos){
if ($pos >= strlen($str))
return false;
return (QRinput::lookAnTable(ord($str[$pos])) >= 0);
}
public function identifyMode($pos){
if ($pos >= strlen($this->dataStr))
return QR_MODE_NUL;
$c = $this->dataStr[$pos];
if(self::isdigitat($this->dataStr,$pos)) {
return QR_MODE_NUM;
} else if(self::isalnumat($this->dataStr,$pos)) {
return QR_MODE_AN;
} else if($this->modeHint == QR_MODE_KANJI) {
if ($pos+1 < strlen($this->dataStr))
{
$d = $this->dataStr[$pos+1];
$word = (ord($c) << 8) | ord($d);
if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
return QR_MODE_KANJI;
}
}
}
return QR_MODE_8;
}
public function eatNum(){
$ln = QRspec::lengthIndicator(QR_MODE_NUM,$this->input->getVersion());
$p = 0;
while(self::isdigitat($this->dataStr,$p)) {
$p++;
}
$run = $p;
$mode = $this->identifyMode($p);
if($mode == QR_MODE_8) {
$dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ QRinput::estimateBitsMode8(1) // + 4 + l8
- QRinput::estimateBitsMode8($run + 1); // - 4 - l8
if($dif > 0) {
return $this->eat8();
}
}
if($mode == QR_MODE_AN) {
$dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ QRinput::estimateBitsModeAn(1) // + 4 + la
- QRinput::estimateBitsModeAn($run + 1);// - 4 - la
if($dif > 0) {
return $this->e
atan();
}
}
$ret = $this->input->append(QR_MODE_NUM,$run,str_split($this->dataStr));
if($ret < 0)
return -1;
return $run;
}
public function e
atan(){
$la = QRspec::lengthIndicator(QR_MODE_AN,$this->input->getVersion());
$ln = QRspec::lengthIndicator(QR_MODE_NUM,$this->input->getVersion());
$p = 0;
while(self::isalnumat($this->dataStr,$p)) {
if(self::isdigitat($this->dataStr,$p)) {
$q = $p;
while(self::isdigitat($this->dataStr,$q)) {
$q++;
}
$dif = QRinput::estimateBitsModeAn($p) // + 4 + la
+ QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
- QRinput::estimateBitsModeAn($q); // - 4 - la
if($dif < 0) {
break;
} else {
$p = $q;
}
} else {
$p++;
}
}
$run = $p;
if(!self::isalnumat($this->dataStr,$p)) {
$dif = QRinput::estimateBitsModeAn($run) + 4 + $la
+ QRinput::estimateBitsMode8(1) // + 4 + l8
- QRinput::estimateBitsMode8($run + 1); // - 4 - l8
if($dif > 0) {
return $this->eat8();
}
}
$ret = $this->input->append(QR_MODE_AN,str_split($this->dataStr));
if($ret < 0)
return -1;
return $run;
}
public function eatKanji(){
$p = 0;
while($this->identifyMode($p) == QR_MODE_KANJI) {
$p += 2;
}
$ret = $this->input->append(QR_MODE_KANJI,$p,str_split($this->dataStr));
if($ret < 0)
return -1;
return $run;
}
public function eat8(){
$la = QRspec::lengthIndicator(QR_MODE_AN,$this->input->getVersion());
$p = 1;
$dataStrLen = strlen($this->dataStr);
while($p < $dataStrLen) {
$mode = $this->identifyMode($p);
if($mode == QR_MODE_KANJI) {
break;
}
if($mode == QR_MODE_NUM) {
$q = $p;
while(self::isdigitat($this->dataStr,$q)) {
$q++;
}
$dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
- QRinput::estimateBitsMode8($q); // - 4 - l8
if($dif < 0) {
break;
} else {
$p = $q;
}
} else if($mode == QR_MODE_AN) {
$q = $p;
while(self::isalnumat($this->dataStr,$q)) {
$q++;
}
$dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ QRinput::estimateBitsModeAn($q - $p) + 4 + $la
- QRinput::estimateBitsMode8($q); // - 4 - l8
if($dif < 0) {
break;
} else {
$p = $q;
}
} else {
$p++;
}
}
$run = $p;
$ret = $this->input->append(QR_MODE_8,str_split($this->dataStr));
if($ret < 0)
return -1;
return $run;
}
public function splitString(){
while (strlen($this->dataStr) > 0)
{
if($this->dataStr == '')
return 0;
$mode = $this->identifyMode(0);
switch ($mode) {
case QR_MODE_NUM: $length = $this->eatNum(); break;
case QR_MODE_AN: $length = $this->e
atan(); break;
case QR_MODE_KANJI:
if ($hint == QR_MODE_KANJI)
$length = $this->eatKanji();
else $length = $this->eat8();
break;
default: $length = $this->eat8(); break;
}
if($length == 0) return 0;
if($length < 0) return -1;
$this->dataStr = substr($this->dataStr,$length);
}
}
public function toUpper(){
$stringLen = strlen($this->dataStr);
$p = 0;
while ($p<$stringLen) {
$mode = self::identifyMode(substr($this->dataStr,$p),$this->modeHint);
if($mode == QR_MODE_KANJI) {
$p += 2;
} else {
if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) {
$this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
}
$p++;
}
}
return $this->dataStr;
}
public static function splitStringToQRinput($string,QRinput $input,$modeHint,$casesensitive = true){
if(is_null($string) || $string == '\0' || $string == '') {
throw new Exception('empty string!!!');
}
$split = new QRsplit($string,$modeHint);
if(!$casesensitive)
$split->toUpper();
return $split->splitString();
}
}
class QRrsItem {
public $mm; // Bits per symbol
public $nn; // Symbols per block (= (1<<mm)-1)
public $alpha_to = array(); // log lookup table
public $index_of = array(); // Antilog lookup table
public $genpoly = array(); // Generator polynomial
public $nroots; // Number of generator roots = number of parity symbols
public $fcr; // First consecutive root,index form
public $prim; // Primitive element,index form
public $iprim; // prim-th root of 1,index form
public $pad; // Padding bytes in shortened block
public $gfpoly;
public function modnn($x){
while ($x >= $this->nn) {
$x -= $this->nn;
$x = ($x >> $this->mm) + ($x & $this->nn);
}
return $x;
}
public static function init_rs_char($symsize,$gfpoly,$fcr,$prim,$nroots,$pad){
// Common code for intializing a Reed-Solomon control block (char or int symbols)
// Copyright 2004 Phil Karn,KA9Q
// May be used under the terms of the GNU Lesser General Public License (LGPL)
$rs = null;
// Check parameter ranges
if($symsize < 0 || $symsize > 8) return $rs;
if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs;
if($prim <= 0 || $prim >= (1<<$symsize)) return $rs;
if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values!
if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding
$rs = new QRrsItem();
$rs->mm = $symsize;
$rs->nn = (1<<$symsize)-1;
$rs->pad = $pad;
$rs->alpha_to = array_fill(0,$rs->nn+1,0);
$rs->index_of = array_fill(0,0);
//
PHP style macro replacement ;)
$NN =& $rs->nn;
$A0 =& $NN;
// Generate Galois field lookup tables
$rs->index_of[0] = $A0; // log(zero) = -inf
$rs->alpha_to[$A0] = 0; // alpha*
-inf = 0
$sr = 1;
for($i=0; $i<$rs->nn; $i++) {
$rs->index_of[$sr] = $i;
$rs->alpha_to[$i] = $sr;
$sr <<= 1;
if($sr & (1<<$symsize)) {
$sr ^= $gfpoly;
}
$sr &= $rs->nn;
}
if($sr != 1){
// field generator polynomial is not primitive!
$rs = NULL;
return $rs;
}
/ Form RS code generator polynomial from its roots
/
$rs->genpoly = array_fill(0,$nroots+1,0);
$rs->fcr = $fcr;
$rs->prim = $prim;
$rs->nroots = $nroots;
$rs->gfpoly = $gfpoly;
/ Find prim-th root of 1,used in decoding
/
for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn)
; // intentional empty-body loop!
$rs->iprim = (int)($iprim / $prim);
$rs->genpoly[0] = 1;
for ($i = 0,$root=$fcr$prim; $i < $nroots; $i++,$root += $prim) {
$rs->genpoly[$i+1] = 1;
// Multiply rs->genpoly[] by @*
(root + x)
for ($j = $i; $j > 0; $j--) {
if ($rs->genpoly[$j] != 0) {
$rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)];
} else {
$rs->genpoly[$j] = $rs->genpoly[$j-1];
}
}
// rs->genpoly[0] can never be zero
$rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)];
}
// convert rs->genpoly[] to index form for quicker encoding
for ($i = 0; $i <= $nroots; $i++)
$rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]];
return $rs;
}
public function encode_rs_char($data,&$parity){
$MM =& $this->mm;
$NN =& $this->nn;
$ALPHA_TO =& $this->alpha_to;
$INDEX_OF =& $this->index_of;
$GENPOLY =& $this->genpoly;
$NROOTS =& $this->nroots;
$FCR =& $this->fcr;
$PRIM =& $this->prim;
$IPRIM =& $this->iprim;
$PAD =& $this->pad;
$A0 =& $NN;
$parity = array_fill(0,$NROOTS,0);
for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) {
$Feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
if($Feedback != $A0) {
// Feedback term is non-zero
// This line is unnecessary when GENPOLY[NROOTS] is unity,as it must
// always be for the polynomials constructed by init_rs()
$Feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $Feedback);
for($j=1;$j<$NROOTS;$j++) {
$parity[$j] ^= $ALPHA_TO[$this->modnn($Feedback + $GENPOLY[$NROOTS-$j])];
}
}
// Shift
array_shift($parity);
if($Feedback != $A0) {
array_push($parity,$ALPHA_TO[$this->modnn($Feedback + $GENPOLY[0])]);
} else {
array_push($parity,0);
}
}
}
}
class QRrs {
public static $items = array();
public static function init_rs($symsize,$pad){
foreach(self::$items as $rs) {
if($rs->pad != $pad) continue;
if($rs->nroots != $nroots) continue;
if($rs->mm != $symsize) continue;
if($rs->gfpoly != $gfpoly) continue;
if($rs->fcr != $fcr) continue;
if($rs->prim != $prim) continue;
return $rs;
}
$rs = QRrsItem::init_rs_char($symsize,$pad);
array_unshift(self::$items,$rs);
return $rs;
}
}
define('N1',3);
define('N2',3);
define('N3',40);
define('N4',10);
class QRmask {
public $runLength = array();
//----------------------------------------------------------------------
public function __construct(){
$this->runLength = array_fill(0,QRSPEC_WIDTH_MAX + 1,0);
}
public function writeFormatInformation($width,$mask,$level){
$blacks = 0;
$format = QRspec::getFormatInfo($mask,$level);
for($i=0; $i<8; $i++) {
if($format & 1) {
$blacks += 2;
$v = 0x85;
} else {
$v = 0x84;
}
$frame[8][$width - 1 - $i] = chr($v);
if($i < 6) {
$frame[$i][8] = chr($v);
} else {
$frame[$i + 1][8] = chr($v);
}
$format = $format >> 1;
}
for($i=0; $i<7; $i++) {
if($format & 1) {
$blacks += 2;
$v = 0x85;
} else {
$v = 0x84;
}
$frame[$width - 7 + $i][8] = chr($v);
if($i == 0) {
$frame[8][7] = chr($v);
} else {
$frame[8][6 - $i] = chr($v);
}
$format = $format >> 1;
}
return $blacks;
}
public function mask0($x,$y) { return ($x+$y)&1; }
public function mask1($x,$y) { return ($y&1); }
public function mask2($x,$y) { return ($x%3); }
public function mask3($x,$y) { return ($x+$y)%3; }
public function mask4($x,$y) { return (((int)($y/2))+((int)($x/3)))&1; }
public function mask5($x,$y) { return (($x$y)&1)+($x
$y)%3; }
public function mask6($x,$y) { return ((($x$y)&1)+($x
$y)%3)&1; }
public function mask7($x,$y) { return ((($x$y)%3)+(($x+$y)&1))&1; }
private function generateMaskNo($maskNo,$frame){
$bitMask = array_fill(0,0));
for($y=0; $y<$width; $y++) {
for($x=0; $x<$width; $x++) {
if(ord($frame[$y][$x]) & 0x80) {
$bitMask[$y][$x] = 0;
} else {
$maskFunc = call_user_func(array($this,'mask'.$maskNo),$y);
$bitMask[$y][$x] = ($maskFunc == 0)?1:0;
}
}
}
return $bitMask;
}
public static function serial($bitFrame){
$codeArr = array();
foreach ($bitFrame as $line)
$codeArr[] = join('',$line);
return gzcompress(join("\n",$codeArr),9);
}
public static function unserial($code){
$codeArr = array();
$codeLines = explode("\n",gzuncompress($code));
foreach ($codeLines as $line)
$codeArr[] = str_split($line);
return $codeArr;
}
public function makeMaskNo($maskNo,$s,&$d,$maskGenOnly = false){
$b = 0;
$bitMask = array();
$fileName = QR_CACHE
DIR.'mask'.$maskNo.DIRECTORY
SEPARATOR.'mask'.$width.'_'.$maskNo.'.dat';
if (QR_CACHEABLE) {
if (file_exists($fileName)) {
$bitMask = self::unserial(file_get_contents($fileName));
} else {
$bitMask = $this->generateMaskNo($maskNo,$d);
if (!file_exists(QR_CACHE
DIR.'mask'.$maskNo))
mkdir(QR_CACHE
DIR.'mask'.$maskNo);
file_put_contents($fileName,self::serial($bitMask));
}
} else {
$bitMask = $this->generateMaskNo($maskNo,$d);
}
if ($maskGenOnly)
return;
$d = $s;
for($y=0; $y<$width; $y++) {
for($x=0; $x<$width; $x++) {
if($bitMask[$y][$x] == 1) {
$d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
}
$b += (int)(ord($d[$y][$x]) & 1);
}
}
return $b;
}
public function makeMask($width,$maskNo,$level){
$masked = array_fill(0,str_repeat("\0",$width));
$this->makeMaskNo($maskNo,$masked);
$this->writeFormatInformation($width,$masked,$level);
return $masked;
}
public function calcN1N3($length){
$demerit = 0;
for($i=0; $i<$length; $i++) {
if($this->runLength[$i] >= 5) {
$demerit += (N1 + ($this->runLength[$i] - 5));
}
if($i & 1) {
if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) {
$fact = (int)($this->runLength[$i] / 3);
if(($this->runLength[$i-2] == $fact) &&
($this->runLength[$i-1] == $fact) &&
($this->runLength[$i+1] == $fact) &&
($this->runLength[$i+2] == $fact)) {
if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4
$fact))) {
$demerit += N3;
} else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 $fact))) {
$demerit += N3;
}
}
}
}
}
return $demerit;
}
public function evaluateSymbol($width,$frame){
$head = 0;
$demerit = 0;
for($y=0; $y<$width; $y++) {
$head = 0;
$this->runLength[0] = 1;
$frameY = $frame[$y];
if ($y>0)
$frameYM = $frame[$y-1];
for($x=0; $x<$width; $x++) {
if(($x > 0) && ($y > 0)) {
$b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
$w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
if(($b22 | ($w22 ^ 1))&1) {
$demerit += N2;
}
}
if(($x == 0) && (ord($frameY[$x]) & 1)) {
$this->runLength[0] = -1;
$head = 1;
$this->runLength[$head] = 1;
} else if($x > 0) {
if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
$head++;
$this->runLength[$head] = 1;
} else {
$this->runLength[$head]++;
}
}
}
$demerit += $this->calcN1N3($head+1);
}
for($x=0; $x<$width; $x++) {
$head = 0;
$this->runLength[0] = 1;
for($y=0; $y<$width; $y++) {
if($y == 0 && (ord($frame[$y][$x]) & 1)) {
$this->runLength[0] = -1;
$head = 1;
$this->runLength[$head] = 1;
} else if($y > 0) {
if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
$head++;
$this->runLength[$head] = 1;
} else {
$this->runLength[$head]++;
}
}
}
$demerit += $this->calcN1N3($head+1);
}
return $demerit;
}
public function mask($width,$level){
$minDemerit =
PHP_INT_MAX;
$bestMaskNum = 0;
$bestMask = array();
$checked_masks = array(0,7);
if (QR_FIND_FROM_RANDOM !== false) {
$howManuOut = 8-(QR_FIND_FROM_RANDOM % 9);
for ($i = 0; $i < $howManuOut; $i++) {
$remPos = rand (0,count($checked_masks)-1);
unset($checked_masks[$remPos]);
$checked_masks = array_values($checked_masks);
}
}
$bestMask = $frame;
foreach($checked_masks as $i) {
$mask = array_fill(0,$width));
$demerit = 0;
$blacks = 0;
$blacks = $this->makeMaskNo($i,$mask);
$blacks += $this->writeFormatInformation($width,$i,$level);
$blacks = (int)(100
$blacks / ($width $width));
$demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
$demerit += $this->evaluateSymbol($width,$mask);
if($demerit < $minDemerit) {
$minDemerit = $demerit;
$bestMask = $mask;
$bestMaskNum = $i;
}
}
return $bestMask;
}
}
class QRrsblock {
public $dataLength;
public $data = array();
public $eccLength;
public $ecc = array();
public function
construct($dl,$el,&$ecc,QRrsItem $rs){
$rs->encode_rs_char($data,$ecc);
$this->dataLength = $dl;
$this->data = $data;
$this->eccLength = $el;
$this->ecc = $ecc;
}
};
//##########################################################################
class QRrawcode {
public $version;
public $datacode = array();
public $ecccode = array();
public $blocks;
public $rsblocks = array(); //of RSblock
public $count;
public $dataLength;
public $eccLength;
public $b1;
public function construct(QRinput $input){
$spec = array(0,0);
$this->datacode = $input->getByteStream();
if(is_null($this->datacode)) {
throw new Exception('null imput string');
}
QRspec::getEccSpec($input->getVersion(),$input->getErrorCorrectionLevel(),$spec);
$this->version = $input->getVersion();
$this->b1 = QRspec::rsBlockNum1($spec);
$this->dataLength = QRspec::rsDataLength($spec);
$this->eccLength = QRspec::rsEccLength($spec);
$this->ecccode = array_fill(0,$this->eccLength,0);
$this->blocks = QRspec::rsBlockNum($spec);
$ret = $this->init($spec);
if($ret < 0) {
throw new Exception('block alloc error');
return null;
}
$this->count = 0;
}
public function init(array $spec){
$dl = QRspec::rsDataCodes1($spec);
$el = QRspec::rsEccCodes1($spec);
$rs = QRrs::init_rs(8,0x11d,255 - $dl - $el);
$blockNo = 0;
$dataPos = 0;
$eccPos = 0;
for($i=0; $i<QRspec::rsBlockNum1($spec); $i++) {
$ecc = array_slice($this->ecccode,$eccPos);
$this->rsblocks[$blockNo] = new QRrsblock($dl,array_slice($this->datacode,$dataPos),$ecc,$rs);
$this->ecccode = array_merge(array_slice($this->ecccode,$eccPos),$ecc);
$dataPos += $dl;
$eccPos += $el;
$blockNo++;
}
if(QRspec::rsBlockNum2($spec) == 0)
return 0;
$dl = QRspec::rsDataCodes2($spec);
$el = QRspec::rsEccCodes2($spec);
$rs = QRrs::init_rs(8,255 - $dl - $el);
if($rs == NULL) return -1;
for($i=0; $i<QRspec::rsBlockNum2($spec); $i++) {
$ecc = array_slice($this->ecccode,$ecc);
$dataPos += $dl;
$eccPos += $el;
$blockNo++;
}
return 0;
}
public function getCode(){
$ret;
if($this->count < $this->dataLength) {
$row = $this->count % $this->blocks;
$col = $this->count / $this->blocks;
if($col >= $this->rsblocks[0]->dataLength) {
$row += $this->b1;
}
$ret = $this->rsblocks[$row]->data[$col];
} else if($this->count < $this->dataLength + $this->eccLength) {
$row = ($this->count - $this->dataLength) % $this->blocks;
$col = ($this->count - $this->dataLength) / $this->blocks;
$ret = $this->rsblocks[$row]->ecc[$col];
} else {
return 0;
}
$this->count++;
return $ret;
}
}
class QRcode {
public $version;
public $width;
public $data;
public function encodeMask(QRinput $input,$mask){
if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) {
throw new Exception('wrong version');
}
if($input->getErrorCorrectionLevel() > QR_ECLEVEL_H) {
throw new Exception('wrong level');
}
$raw = new QRrawcode($input);
QRtools::markTime('after_raw');
$version = $raw->version;
$width = QRspec::getWidth($version);
$frame = QRspec::newFrame($version);
$filler = new FrameFiller($width,$frame);
if(is_null($filler)) {
return NULL;
}
// inteleaved data and ecc codes
for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) {
$code = $raw->getCode();
$bit = 0x80;
for($j=0; $j<8; $j++) {
$addr = $filler->next();
$filler->setFrameAt($addr,0x02 | (($bit & $code) != 0));
$bit = $bit >> 1;
}
}
QRtools::markTime('after_filler');
unset($raw);
// remainder bits
$j = QRspec::getRemainder($version);
for($i=0; $i<$j; $i++) {
$addr = $filler->next();
$filler->setFrameAt($addr,0x02);
}
$frame = $filler->frame;
unset($filler);
// masking
$maskObj = new QRmask();
if($mask < 0) {
if (QR_FIND_BEST_MASK) {
$masked = $maskObj->mask($width,$input->getErrorCorrectionLevel());
} else {
$masked = $maskObj->makeMask($width,(intval(QR_DEFAULT_MASK) % 8),$input->getErrorCorrectionLevel());
}
} else {
$masked = $maskObj->makeMask($width,$input->getErrorCorrectionLevel());
}
if($masked == NULL) {
return NULL;
}
QRtools::markTime('after_mask');
$this->version = $version;
$this->width = $width;
$this->data = $masked;
return $this;
}
public function encodeInput(QRinput $input){
return $this->encodeMask($input,-1);
}
public function encodeString8bit($string,$level){
if(string == NULL) {
throw new Exception('empty string!');
return NULL;
}
$input = new QRinput($version,$level);
if($input == NULL) return NULL;
$ret = $input->append($input,QR_MODE_8,strlen($string),str_split($string));
if($ret < 0) {
unset($input);
return NULL;
}
return $this->encodeInput($input);
}
public function encodeString($string,$hint,$casesensitive){
if($hint != QR_MODE_8 && $hint != QR_MODE_KANJI) {
throw new Exception('bad hint');
return NULL;
}
$input = new QRinput($version,$level);
if($input == NULL) return NULL;
$ret = QRsplit::splitStringToQRinput($string,$casesensitive);
if($ret < 0) {
return NULL;
}
return $this->encodeInput($input);
}
public static function png($text,$outfile = false,$level = QR_ECLEVEL_L,$size = 3,$margin = 4,$saveandprint=false){
$enc = QRencode::factory($level,$margin);
return $enc->encodePNG($text,$outfile,$saveandprint=false);
}
public static function text($text,$margin = 4){
$enc = QRencode::factory($level,$margin);
return $enc->encode($text,$outfile);
}
public static function raw($text,$margin);
return $enc->encodeRAW($text,$outfile);
}
}
class FrameFiller {
public $width;
public $frame;
public $x;
public $y;
public $dir;
public $bit;
public function __construct($width,&$frame){
$this->width = $width;
$this->frame = $frame;
$this->x = $width - 1;
$this->y = $width - 1;
$this->dir = -1;
$this->bit = -1;
}
public function setFrameAt($at,$val){
$this->frame[$at['y']][$at['x']] = chr($val);
}
public function getFrameAt($at){
return ord($this->frame[$at['y']][$at['x']]);
}
public function next(){
do {
if($this->bit == -1) {
$this->bit = 0;
return array('x'=>$this->x,'y'=>$this->y);
}
$x = $this->x;
$y = $this->y;
$w = $this->width;
if($this->bit == 0) {
$x--;
$this->bit++;
} else {
$x++;
$y += $this->dir;
$this->bit--;
}
if($this->dir < 0) {
if($y < 0) {
$y = 0;
$x -= 2;
$this->dir = 1;
if($x == 6) {
$x--;
$y = 9;
}
}
} else {
if($y == $w) {
$y = $w - 1;
$x -= 2;
$this->dir = -1;
if($x == 6) {
$x--;
$y -= 8;
}
}
}
if($x < 0 || $y < 0) return null;
$this->x = $x;
$this->y = $y;
} while(ord($this->frame[$y][$x]) & 0x80);
return array('x'=>$x,'y'=>$y);
}
} ;
class QRencode {
public $casesensitive = true;
public $eightbit = false;
public $version = 0;
public $size = 3;
public $margin = 4;
public $structured = 0; // not supported yet
public $level = QR_ECLEVEL_L;
public $hint = QR_MODE_8;
public static function factory($level = QR_ECLEVEL_L,$margin = 4){
$enc = new QRencode();
$enc->size = $size;
$enc->margin = $margin;
switch ($level.'') {
case '0':
case '1':
case '2':
case '3':
$enc->level = $level;
break;
case 'l':
case 'L':
$enc->level = QR_ECLEVEL_L;
break;
case 'm':
case 'M':
$enc->level = QR_ECLEVEL_M;
break;
case 'q':
case 'Q':
$enc->level = QR_ECLEVEL_Q;
break;
case 'h':
case 'H':
$enc->level = QR_ECLEVEL_H;
break;
}
return $enc;
}
public function encodeRAW($intext,$outfile = false){
$code = new QRcode();
if($this->eightbit) {
$code->encodeString8bit($intext,$this->version,$this->level);
} else {
$code->encodeString($intext,$this->level,$this->hint,$this->casesensitive);
}
return $code->data;
}
public function encode($intext,$this->casesensitive);
}
QRtools::markTime('after_encode');
if ($outfile!== false) {
file_put_contents($outfile,join("\n",QRtools::binarize($code->data)));
} else {
return QRtools::binarize($code->data);
}
}
public function encodePNG($intext,$saveandprint=false){
try {
ob_start();
$tab = $this->encode($intext);
$err = ob_get_contents();
ob_end_clean();
if ($err != '')
QRtools::log($outfile,$err);
$maxSize = (int)(QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin));
QRimage::png($tab,min(max(1,$this->size),$maxSize),$this->margin,$saveandprint);
} catch (Exception $e) {
QRtools::log($outfile,$e->getMessage());
}
}
}
保存为 class_qrcode.