QrCodeHelper.php 7.0 KB

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