QrCodeHelper.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <?php
  2. /**
  3. * Example Url:
  4. * http://chart.apis.google.com/chart?cht=qr&chs=400x400&chl=SomeText
  5. */
  6. /*
  7. if (!defined('QS_CODE_MIN_SIZE')) {
  8. define('QS_CODE_MIN_SIZE', 58);
  9. }
  10. if (!defined('QS_CODE_MAX_SIZE')) {
  11. define('QS_CODE_MAX_SIZE', 540);
  12. }
  13. if (!defined('QS_CODE_DEFAULT_SIZE')) {
  14. define('QS_CODE_DEFAULT_SIZE', 74);
  15. }
  16. if (!defined('QS_CODE_DEFAULT_LEVEL')) {
  17. define('QS_CODE_DEFAULT_LEVEL', 'L');
  18. }
  19. */
  20. /**
  21. * QR Code Helper
  22. * based on google chart api
  23. * @see http://code.google.com/intl/de-DE/apis/chart/types.html#qrcodes
  24. *
  25. * alternative service api / engine: http://goqr.me/api-description/ (not available right now)
  26. * or: http://qrcode.kaywa.com/img.php
  27. *
  28. * NOTE: urls have a 2k limit - for the total amount of 4296 chars (7089 for numeric values only) you will need to send it via post
  29. *
  30. * TODO: set size according to text length automatically
  31. * 2010-02-06 ms
  32. */
  33. class QrCodeHelper extends AppHelper {
  34. public $helpers = array('Html');
  35. const MIN_SIZE = 58; # not readable anymore below this value
  36. const MAX_SIZE = 540; # max of 300000 pixels
  37. const DEFAULT_SIZE = 74; # 2x size
  38. const DEFAULT_LEVEL = 'L'; # highest correction level
  39. const SIZE_L = 58;
  40. const SIZE_M = 66;
  41. const SIZE_Q = 66;
  42. const SIZE_H = 74;
  43. protected $engine = 'google';
  44. protected $url = 'http://chart.apis.google.com/chart?';
  45. /**
  46. * necessary
  47. * - chl: string $text
  48. * - choe: string $outputEncoding
  49. * - chs: size (...x...)
  50. **/
  51. protected $options = array('cht'=>'qr', 'chl'=>'', 'choe'=>'', 'chs'=>'');
  52. protected $ecLevels = array('H', 'Q', 'M', 'L'); # 30%..7%
  53. protected $formattingTypes = array('url'=>'http', 'tel'=>'tel', 'sms'=>'smsto', 'card'=>'mecard');
  54. public function __construct(View $View, $settings = array()) {
  55. parent::__construct($View, $settings);
  56. $this->reset();
  57. }
  58. /**
  59. * main barcode display function
  60. * @param string $text (utf8 encoded)
  61. * @param array $imageOptions
  62. * NOTE: set size or level manually prior to calling this method
  63. * 2010-02-06 ms
  64. */
  65. public function image($text, $options = array()) {
  66. return $this->Html->image($this->url($text), $options);
  67. }
  68. /**
  69. * just the url - without image tag
  70. * 2010-02-25 ms
  71. */
  72. public function url($text) {
  73. $params = array();
  74. $params['chl'] = rawurlencode($text); //urlencode($text);
  75. return $this->_url($params);
  76. }
  77. protected function _url($params = array()) {
  78. $params = array_merge($this->options, $params);
  79. $pieces = array();
  80. foreach ($params as $key => $value) {
  81. $pieces[] = $key.'='.$value;
  82. }
  83. return $this->url.implode('&', $pieces);
  84. }
  85. /**
  86. * format a text in a specific format
  87. * - url, sms, tel, email, market, geo
  88. * @return string $formattedText
  89. * 2010-02-06 ms
  90. */
  91. public function formatText($text, $type = null) {
  92. switch ($type) {
  93. case 'text':
  94. break;
  95. case 'url':
  96. $text = $this->Html->url($text, true);
  97. break;
  98. case 'sms':
  99. $text = 'smsto:'.implode(':', (array)$text);
  100. break;
  101. case 'tel':
  102. $text = 'tel:'.$text;
  103. break;
  104. case 'email':
  105. $text = 'mailto:'.$text;
  106. break;
  107. case 'geo':
  108. $text = 'geo:'.implode(',', (array)$text); #like 77.1,11.8
  109. break;
  110. case 'market':
  111. $text = 'market://search?q=pname:'.$text;
  112. }
  113. return $text;
  114. }
  115. /**
  116. * generate mecard string
  117. * 1: name, nickname, note, birthday, sound
  118. * 1..n (as array or string): address, tel, url, email
  119. * for details on cards see:
  120. * http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/function/application/addressbook/index.html
  121. * example: MECARD: N:Docomo,Taro; SOUND:docomotaro; TEL:03XXXXXXXX; EMAIL:d@e.de;
  122. * @return string $mecard
  123. * 2010-02-26 ms
  124. */
  125. public function formatCard($data) {
  126. $data = (array)$data;
  127. $res = array();
  128. foreach ($data as $key => $val) {
  129. switch ($key) {
  130. case 'name':
  131. $res[] = 'N:'.$val; # //TODO: support array
  132. break;
  133. case 'nickname':
  134. $res[] = 'NICKNAME:'.$val;
  135. break;
  136. case 'sound':
  137. $res[] = 'SOUND:'.$val;
  138. break;
  139. case 'note':
  140. $val = str_replace(';', ',', $val);
  141. $res[] = 'NOTE:'.$val; //TODO: remove other invalid characters
  142. break;
  143. case 'birthday':
  144. if (strlen($val) !== 8) {
  145. $val = substr($val, 0, 4).substr($val, 6, 2).substr($val, 10, 2);
  146. }
  147. $res[] = 'BDAY:'.$val;
  148. break;
  149. case 'tel':
  150. $val = (array)$val;
  151. foreach ($val as $v) {
  152. $res[] = 'TEL:'.$v;
  153. }
  154. break;
  155. case 'video':
  156. $val = (array)$val;
  157. foreach ($val as $v) {
  158. $res[] = 'TEL-AV:'.$v;
  159. }
  160. break;
  161. case 'address':
  162. $val = (array)$val;
  163. foreach ($val as $v) {
  164. $res[] = 'ADR:'.$v; //TODO: reformat (array etc)
  165. }
  166. break;
  167. case 'org':
  168. $val = (array)$val;
  169. foreach ($val as $v) {
  170. $res[] = 'ORG:'.$v;
  171. }
  172. break;
  173. case 'role':
  174. $val = (array)$val;
  175. foreach ($val as $v) {
  176. $res[] = 'ROLE:'.$v;
  177. }
  178. break;
  179. case 'email':
  180. $val = (array)$val;
  181. foreach ($val as $v) {
  182. $res[] = 'EMAIL:'.$v;
  183. }
  184. break;
  185. case 'url':
  186. $val = (array)$val;
  187. foreach ($val as $v) {
  188. $res[] = 'URL:'.$this->Html->url($v, true);
  189. }
  190. break;
  191. }
  192. }
  193. return 'MECARD:'.implode(';', $res).';';
  194. }
  195. /**
  196. * //TODO
  197. * calendar event
  198. * e.g.: BEGIN:VEVENT SUMMARY:dfdfd DTSTART:20100226T092900Z DTEND:20100226T102900Z END:VEVENT
  199. * @see http://zxing.appspot.com/generator/
  200. * 2010-02-26 ms
  201. */
  202. public function formatEvent() {
  203. }
  204. public function format($protocol, $string) {
  205. return $protocol.':'.$string;
  206. }
  207. /**
  208. * change size
  209. * result format: chs=<size>x<size>
  210. * @return boolean $success
  211. *
  212. * //TODO: automatic detection
  213. * //default is 2x size (plus margin) of typical QR version for the error correction level (L=V.2, M/Q=V.3, H=V.4)
  214. * //$ecCodes = array('L' => 58, 'M' => 66, 'Q' => 66, 'H' => 74);
  215. * 2010-02-06 ms
  216. */
  217. public function setSize($value) {
  218. if ($value == 'auto') {
  219. //TODO
  220. }
  221. $value = (int)$value;
  222. if ($value >= self::MIN_SIZE && $value <= self::MAX_SIZE) {
  223. $this->options['chs'] = $value.'x'.$value;
  224. return true;
  225. }
  226. return false;
  227. }
  228. /**
  229. * change level and margin - optional
  230. * result format: chld=<EC level>|<margin>
  231. * @return boolean $success
  232. * 2010-02-06 ms
  233. */
  234. public function setLevel($level, $margin = null) {
  235. if (in_array($level, $this->ecLevels)) {
  236. if ($margin === null) {
  237. $margin = 4; # minimum
  238. }
  239. $this->options['chld'] = strtoupper($level).'|'.$margin;
  240. return true;
  241. }
  242. return false;
  243. }
  244. public function setEncoding() {
  245. //TODO
  246. }
  247. public function reset() {
  248. $this->setSize(self::DEFAULT_SIZE);
  249. //$this->setLevel(QS_CODE_DEFAULT_LEVEL);
  250. $this->options['chld'] = '';
  251. $this->options['choe'] = Configure::read('App.encoding');
  252. }
  253. /**
  254. * show current options - for debugging only
  255. * 2010-02-06 ms
  256. */
  257. public function debug() {
  258. return $this->options;
  259. }
  260. /**
  261. * 25 => 21x21 (L)
  262. * ...
  263. * 4000 => 547x547 (L)
  264. * @param int $length
  265. * @return int $size
  266. * 2011-06-06 ms
  267. */
  268. public function _findSuitableSize() {
  269. }
  270. }