]> git.wh0rd.org - tt-rss.git/blame - lib/phpqrcode/qrencode.php
pngcrush.sh
[tt-rss.git] / lib / phpqrcode / qrencode.php
CommitLineData
fb70f26e
AD
1<?php\r
2/*\r
3 * PHP QR Code encoder\r
4 *\r
5 * Main encoder classes.\r
6 *\r
7 * Based on libqrencode C library distributed under LGPL 2.1\r
8 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>\r
9 *\r
10 * PHP QR Code is distributed under LGPL 3\r
11 * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>\r
12 *\r
13 * This library is free software; you can redistribute it and/or\r
14 * modify it under the terms of the GNU Lesser General Public\r
15 * License as published by the Free Software Foundation; either\r
16 * version 3 of the License, or any later version.\r
17 *\r
18 * This library is distributed in the hope that it will be useful,\r
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
21 * Lesser General Public License for more details.\r
22 *\r
23 * You should have received a copy of the GNU Lesser General Public\r
24 * License along with this library; if not, write to the Free Software\r
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
26 */\r
27 \r
28 class QRrsblock {\r
29 public $dataLength;\r
30 public $data = array();\r
31 public $eccLength;\r
32 public $ecc = array();\r
33 \r
34 public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs)\r
35 {\r
36 $rs->encode_rs_char($data, $ecc);\r
37 \r
38 $this->dataLength = $dl;\r
39 $this->data = $data;\r
40 $this->eccLength = $el;\r
41 $this->ecc = $ecc;\r
42 }\r
43 };\r
44 \r
45 //##########################################################################\r
46\r
47 class QRrawcode {\r
48 public $version;\r
49 public $datacode = array();\r
50 public $ecccode = array();\r
51 public $blocks;\r
52 public $rsblocks = array(); //of RSblock\r
53 public $count;\r
54 public $dataLength;\r
55 public $eccLength;\r
56 public $b1;\r
57 \r
58 //----------------------------------------------------------------------\r
59 public function __construct(QRinput $input)\r
60 {\r
61 $spec = array(0,0,0,0,0);\r
62 \r
63 $this->datacode = $input->getByteStream();\r
64 if(is_null($this->datacode)) {\r
65 throw new Exception('null imput string');\r
66 }\r
67\r
68 QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec);\r
69\r
70 $this->version = $input->getVersion();\r
71 $this->b1 = QRspec::rsBlockNum1($spec);\r
72 $this->dataLength = QRspec::rsDataLength($spec);\r
73 $this->eccLength = QRspec::rsEccLength($spec);\r
74 $this->ecccode = array_fill(0, $this->eccLength, 0);\r
75 $this->blocks = QRspec::rsBlockNum($spec);\r
76 \r
77 $ret = $this->init($spec);\r
78 if($ret < 0) {\r
79 throw new Exception('block alloc error');\r
80 return null;\r
81 }\r
82\r
83 $this->count = 0;\r
84 }\r
85 \r
86 //----------------------------------------------------------------------\r
87 public function init(array $spec)\r
88 {\r
89 $dl = QRspec::rsDataCodes1($spec);\r
90 $el = QRspec::rsEccCodes1($spec);\r
91 $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);\r
92 \r
93\r
94 $blockNo = 0;\r
95 $dataPos = 0;\r
96 $eccPos = 0;\r
97 for($i=0; $i<QRspec::rsBlockNum1($spec); $i++) {\r
98 $ecc = array_slice($this->ecccode,$eccPos);\r
99 $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);\r
100 $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);\r
101 \r
102 $dataPos += $dl;\r
103 $eccPos += $el;\r
104 $blockNo++;\r
105 }\r
106\r
107 if(QRspec::rsBlockNum2($spec) == 0)\r
108 return 0;\r
109\r
110 $dl = QRspec::rsDataCodes2($spec);\r
111 $el = QRspec::rsEccCodes2($spec);\r
112 $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);\r
113 \r
114 if($rs == NULL) return -1;\r
115 \r
116 for($i=0; $i<QRspec::rsBlockNum2($spec); $i++) {\r
117 $ecc = array_slice($this->ecccode,$eccPos);\r
118 $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);\r
119 $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);\r
120 \r
121 $dataPos += $dl;\r
122 $eccPos += $el;\r
123 $blockNo++;\r
124 }\r
125\r
126 return 0;\r
127 }\r
128 \r
129 //----------------------------------------------------------------------\r
130 public function getCode()\r
131 {\r
6f7798b6 132 $ret = 0;\r
fb70f26e
AD
133\r
134 if($this->count < $this->dataLength) {\r
135 $row = $this->count % $this->blocks;\r
136 $col = $this->count / $this->blocks;\r
137 if($col >= $this->rsblocks[0]->dataLength) {\r
138 $row += $this->b1;\r
139 }\r
140 $ret = $this->rsblocks[$row]->data[$col];\r
141 } else if($this->count < $this->dataLength + $this->eccLength) {\r
142 $row = ($this->count - $this->dataLength) % $this->blocks;\r
143 $col = ($this->count - $this->dataLength) / $this->blocks;\r
144 $ret = $this->rsblocks[$row]->ecc[$col];\r
145 } else {\r
146 return 0;\r
147 }\r
148 $this->count++;\r
149 \r
150 return $ret;\r
151 }\r
152 }\r
153\r
154 //##########################################################################\r
155 \r
156 class QRcode {\r
157 \r
158 public $version;\r
159 public $width;\r
160 public $data; \r
161 \r
162 //----------------------------------------------------------------------\r
163 public function encodeMask(QRinput $input, $mask)\r
164 {\r
165 if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) {\r
166 throw new Exception('wrong version');\r
167 }\r
168 if($input->getErrorCorrectionLevel() > QR_ECLEVEL_H) {\r
169 throw new Exception('wrong level');\r
170 }\r
171\r
172 $raw = new QRrawcode($input);\r
173 \r
174 QRtools::markTime('after_raw');\r
175 \r
176 $version = $raw->version;\r
177 $width = QRspec::getWidth($version);\r
178 $frame = QRspec::newFrame($version);\r
179 \r
180 $filler = new FrameFiller($width, $frame);\r
181 if(is_null($filler)) {\r
182 return NULL;\r
183 }\r
184\r
185 // inteleaved data and ecc codes\r
186 for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) {\r
187 $code = $raw->getCode();\r
188 $bit = 0x80;\r
189 for($j=0; $j<8; $j++) {\r
190 $addr = $filler->next();\r
191 $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));\r
192 $bit = $bit >> 1;\r
193 }\r
194 }\r
195 \r
196 QRtools::markTime('after_filler');\r
197 \r
198 unset($raw);\r
199 \r
200 // remainder bits\r
201 $j = QRspec::getRemainder($version);\r
202 for($i=0; $i<$j; $i++) {\r
203 $addr = $filler->next();\r
204 $filler->setFrameAt($addr, 0x02);\r
205 }\r
206 \r
207 $frame = $filler->frame;\r
208 unset($filler);\r
209 \r
210 \r
211 // masking\r
212 $maskObj = new QRmask();\r
213 if($mask < 0) {\r
214 \r
215 if (QR_FIND_BEST_MASK) {\r
216 $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());\r
217 } else {\r
218 $masked = $maskObj->makeMask($width, $frame, (intval(QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel());\r
219 }\r
220 } else {\r
221 $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());\r
222 }\r
223 \r
224 if($masked == NULL) {\r
225 return NULL;\r
226 }\r
227 \r
228 QRtools::markTime('after_mask');\r
229 \r
230 $this->version = $version;\r
231 $this->width = $width;\r
232 $this->data = $masked;\r
233 \r
234 return $this;\r
235 }\r
236 \r
237 //----------------------------------------------------------------------\r
238 public function encodeInput(QRinput $input)\r
239 {\r
240 return $this->encodeMask($input, -1);\r
241 }\r
242 \r
243 //----------------------------------------------------------------------\r
244 public function encodeString8bit($string, $version, $level)\r
245 {\r
246 if(string == NULL) {\r
247 throw new Exception('empty string!');\r
248 return NULL;\r
249 }\r
250\r
251 $input = new QRinput($version, $level);\r
252 if($input == NULL) return NULL;\r
253\r
254 $ret = $input->append($input, QR_MODE_8, strlen($string), str_split($string));\r
255 if($ret < 0) {\r
256 unset($input);\r
257 return NULL;\r
258 }\r
259 return $this->encodeInput($input);\r
260 }\r
261\r
262 //----------------------------------------------------------------------\r
263 public function encodeString($string, $version, $level, $hint, $casesensitive)\r
264 {\r
265\r
266 if($hint != QR_MODE_8 && $hint != QR_MODE_KANJI) {\r
267 throw new Exception('bad hint');\r
268 return NULL;\r
269 }\r
270\r
271 $input = new QRinput($version, $level);\r
272 if($input == NULL) return NULL;\r
273\r
274 $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive);\r
275 if($ret < 0) {\r
276 return NULL;\r
277 }\r
278\r
279 return $this->encodeInput($input);\r
280 }\r
281 \r
282 //----------------------------------------------------------------------\r
283 public static function png($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false) \r
284 {\r
285 $enc = QRencode::factory($level, $size, $margin);\r
286 return $enc->encodePNG($text, $outfile, $saveandprint=false);\r
287 }\r
288\r
289 //----------------------------------------------------------------------\r
290 public static function text($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4) \r
291 {\r
292 $enc = QRencode::factory($level, $size, $margin);\r
293 return $enc->encode($text, $outfile);\r
294 }\r
295\r
296 //----------------------------------------------------------------------\r
297 public static function raw($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4) \r
298 {\r
299 $enc = QRencode::factory($level, $size, $margin);\r
300 return $enc->encodeRAW($text, $outfile);\r
301 }\r
302 }\r
303 \r
304 //##########################################################################\r
305 \r
306 class FrameFiller {\r
307 \r
308 public $width;\r
309 public $frame;\r
310 public $x;\r
311 public $y;\r
312 public $dir;\r
313 public $bit;\r
314 \r
315 //----------------------------------------------------------------------\r
316 public function __construct($width, &$frame)\r
317 {\r
318 $this->width = $width;\r
319 $this->frame = $frame;\r
320 $this->x = $width - 1;\r
321 $this->y = $width - 1;\r
322 $this->dir = -1;\r
323 $this->bit = -1;\r
324 }\r
325 \r
326 //----------------------------------------------------------------------\r
327 public function setFrameAt($at, $val)\r
328 {\r
329 $this->frame[$at['y']][$at['x']] = chr($val);\r
330 }\r
331 \r
332 //----------------------------------------------------------------------\r
333 public function getFrameAt($at)\r
334 {\r
335 return ord($this->frame[$at['y']][$at['x']]);\r
336 }\r
337 \r
338 //----------------------------------------------------------------------\r
339 public function next()\r
340 {\r
341 do {\r
342 \r
343 if($this->bit == -1) {\r
344 $this->bit = 0;\r
345 return array('x'=>$this->x, 'y'=>$this->y);\r
346 }\r
347\r
348 $x = $this->x;\r
349 $y = $this->y;\r
350 $w = $this->width;\r
351\r
352 if($this->bit == 0) {\r
353 $x--;\r
354 $this->bit++;\r
355 } else {\r
356 $x++;\r
357 $y += $this->dir;\r
358 $this->bit--;\r
359 }\r
360\r
361 if($this->dir < 0) {\r
362 if($y < 0) {\r
363 $y = 0;\r
364 $x -= 2;\r
365 $this->dir = 1;\r
366 if($x == 6) {\r
367 $x--;\r
368 $y = 9;\r
369 }\r
370 }\r
371 } else {\r
372 if($y == $w) {\r
373 $y = $w - 1;\r
374 $x -= 2;\r
375 $this->dir = -1;\r
376 if($x == 6) {\r
377 $x--;\r
378 $y -= 8;\r
379 }\r
380 }\r
381 }\r
382 if($x < 0 || $y < 0) return null;\r
383\r
384 $this->x = $x;\r
385 $this->y = $y;\r
386\r
387 } while(ord($this->frame[$y][$x]) & 0x80);\r
388 \r
389 return array('x'=>$x, 'y'=>$y);\r
390 }\r
391 \r
392 } ;\r
393 \r
394 //########################################################################## \r
395 \r
396 class QRencode {\r
397 \r
398 public $casesensitive = true;\r
399 public $eightbit = false;\r
400 \r
401 public $version = 0;\r
402 public $size = 3;\r
403 public $margin = 4;\r
404 \r
405 public $structured = 0; // not supported yet\r
406 \r
407 public $level = QR_ECLEVEL_L;\r
408 public $hint = QR_MODE_8;\r
409 \r
410 //----------------------------------------------------------------------\r
411 public static function factory($level = QR_ECLEVEL_L, $size = 3, $margin = 4)\r
412 {\r
413 $enc = new QRencode();\r
414 $enc->size = $size;\r
415 $enc->margin = $margin;\r
416 \r
417 switch ($level.'') {\r
418 case '0':\r
419 case '1':\r
420 case '2':\r
421 case '3':\r
422 $enc->level = $level;\r
423 break;\r
424 case 'l':\r
425 case 'L':\r
426 $enc->level = QR_ECLEVEL_L;\r
427 break;\r
428 case 'm':\r
429 case 'M':\r
430 $enc->level = QR_ECLEVEL_M;\r
431 break;\r
432 case 'q':\r
433 case 'Q':\r
434 $enc->level = QR_ECLEVEL_Q;\r
435 break;\r
436 case 'h':\r
437 case 'H':\r
438 $enc->level = QR_ECLEVEL_H;\r
439 break;\r
440 }\r
441 \r
442 return $enc;\r
443 }\r
444 \r
445 //----------------------------------------------------------------------\r
446 public function encodeRAW($intext, $outfile = false) \r
447 {\r
448 $code = new QRcode();\r
449\r
450 if($this->eightbit) {\r
451 $code->encodeString8bit($intext, $this->version, $this->level);\r
452 } else {\r
453 $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);\r
454 }\r
455 \r
456 return $code->data;\r
457 }\r
458\r
459 //----------------------------------------------------------------------\r
460 public function encode($intext, $outfile = false) \r
461 {\r
462 $code = new QRcode();\r
463\r
464 if($this->eightbit) {\r
465 $code->encodeString8bit($intext, $this->version, $this->level);\r
466 } else {\r
467 $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);\r
468 }\r
469 \r
470 QRtools::markTime('after_encode');\r
471 \r
472 if ($outfile!== false) {\r
473 file_put_contents($outfile, join("\n", QRtools::binarize($code->data)));\r
474 } else {\r
475 return QRtools::binarize($code->data);\r
476 }\r
477 }\r
478 \r
479 //----------------------------------------------------------------------\r
480 public function encodePNG($intext, $outfile = false,$saveandprint=false) \r
481 {\r
482 try {\r
483 \r
484 ob_start();\r
485 $tab = $this->encode($intext);\r
486 $err = ob_get_contents();\r
487 ob_end_clean();\r
488 \r
489 if ($err != '')\r
490 QRtools::log($outfile, $err);\r
491 \r
492 $maxSize = (int)(QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin));\r
493 \r
494 QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint);\r
495 \r
496 } catch (Exception $e) {\r
497 \r
498 QRtools::log($outfile, $e->getMessage());\r
499 \r
500 }\r
501 }\r
502 }\r