QrCodeHelper.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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. public $engine = 'google';
  45. public $url = 'http://chart.apis.google.com/chart?';
  46. /**
  47. * necessary
  48. * - chl: string $text
  49. * - choe: string $outputEncoding
  50. * - chs: size (...x...)
  51. */
  52. public $options = array('cht' => 'qr', 'chl' => '', 'choe' => '', 'chs' => '');
  53. public $ecLevels = array('H', 'Q', 'M', 'L'); # 30%..7%
  54. public $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->uri($text), $options);
  68. }
  69. /**
  70. * Just the url - without image tag
  71. * Note: cannot be url() due to AppHelper conflicts
  72. *
  73. * @param string $text
  74. * @return string Url
  75. * 2010-02-25 ms
  76. */
  77. public function uri($text) {
  78. $params = array();
  79. $params['chl'] = rawurlencode($text);
  80. return $this->_uri($params);
  81. }
  82. /**
  83. * @return string Url
  84. */
  85. protected function _uri($params = array()) {
  86. $params = array_merge($this->options, $params);
  87. $pieces = array();
  88. foreach ($params as $key => $value) {
  89. $pieces[] = $key . '=' . $value;
  90. }
  91. return $this->url . implode('&', $pieces);
  92. }
  93. /**
  94. * format a text in a specific format
  95. * - url, sms, tel, email, market, geo
  96. * @return string $formattedText
  97. * 2010-02-06 ms
  98. */
  99. public function formatText($text, $type = null) {
  100. switch ($type) {
  101. case 'text':
  102. break;
  103. case 'url':
  104. $text = $this->Html->url($text, true);
  105. break;
  106. case 'sms':
  107. $text = 'smsto:' . implode(':', (array)$text);
  108. break;
  109. case 'tel':
  110. $text = 'tel:' . $text;
  111. break;
  112. case 'email':
  113. $text = 'mailto:' . $text;
  114. break;
  115. case 'geo':
  116. $text = 'geo:' . implode(',', (array)$text); #like 77.1,11.8
  117. break;
  118. case 'market':
  119. $text = 'market://search?q=pname:' . $text;
  120. }
  121. return $text;
  122. }
  123. /**
  124. * generate mecard string
  125. * 1: name, nickname, note, birthday, sound
  126. * 1..n (as array or string): address, tel, url, email
  127. * for details on cards see:
  128. * http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/function/application/addressbook/index.html
  129. * example: MECARD: N:Docomo,Taro; SOUND:docomotaro; TEL:03XXXXXXXX; EMAIL:d@e.de;
  130. * @return string $mecard
  131. * 2010-02-26 ms
  132. */
  133. public function formatCard($data) {
  134. $data = (array)$data;
  135. $res = array();
  136. foreach ($data as $key => $val) {
  137. switch ($key) {
  138. case 'name':
  139. $res[] = 'N:' . $val; # //TODO: support array
  140. break;
  141. case 'nickname':
  142. $res[] = 'NICKNAME:' . $val;
  143. break;
  144. case 'sound':
  145. $res[] = 'SOUND:' . $val;
  146. break;
  147. case 'note':
  148. $val = str_replace(';', ',', $val);
  149. $res[] = 'NOTE:' . $val; //TODO: remove other invalid characters
  150. break;
  151. case 'birthday':
  152. if (strlen($val) !== 8) {
  153. $val = substr($val, 0, 4) . substr($val, 6, 2) . substr($val, 10, 2);
  154. }
  155. $res[] = 'BDAY:' . $val;
  156. break;
  157. case 'tel':
  158. $val = (array)$val;
  159. foreach ($val as $v) {
  160. $res[] = 'TEL:' . $v;
  161. }
  162. break;
  163. case 'video':
  164. $val = (array)$val;
  165. foreach ($val as $v) {
  166. $res[] = 'TEL-AV:' . $v;
  167. }
  168. break;
  169. case 'address':
  170. $val = (array)$val;
  171. foreach ($val as $v) {
  172. $res[] = 'ADR:' . $v; //TODO: reformat (array etc)
  173. }
  174. break;
  175. case 'org':
  176. $val = (array)$val;
  177. foreach ($val as $v) {
  178. $res[] = 'ORG:' . $v;
  179. }
  180. break;
  181. case 'role':
  182. $val = (array)$val;
  183. foreach ($val as $v) {
  184. $res[] = 'ROLE:' . $v;
  185. }
  186. break;
  187. case 'email':
  188. $val = (array)$val;
  189. foreach ($val as $v) {
  190. $res[] = 'EMAIL:' . $v;
  191. }
  192. break;
  193. case 'url':
  194. $val = (array)$val;
  195. foreach ($val as $v) {
  196. $res[] = 'URL:' . $this->Html->url($v, true);
  197. }
  198. break;
  199. }
  200. }
  201. return 'MECARD:' . implode(';', $res) . ';';
  202. }
  203. /**
  204. * //TODO
  205. * calendar event
  206. * e.g.: BEGIN:VEVENT SUMMARY:dfdfd DTSTART:20100226T092900Z DTEND:20100226T102900Z END:VEVENT
  207. * @see http://zxing.appspot.com/generator/
  208. * 2010-02-26 ms
  209. */
  210. public function formatEvent() {
  211. }
  212. /**
  213. * QrCodeHelper::format()
  214. *
  215. * @param mixed $protocol
  216. * @param mixed $string
  217. * @return string
  218. */
  219. public function format($protocol, $string) {
  220. return $protocol . ':' . $string;
  221. }
  222. /**
  223. * change size
  224. * result format: chs=<size>x<size>
  225. * @return boolean Success
  226. *
  227. * //TODO: automatic detection
  228. * //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)
  229. * //$ecCodes = array('L' => 58, 'M' => 66, 'Q' => 66, 'H' => 74);
  230. * 2010-02-06 ms
  231. */
  232. public function setSize($value) {
  233. if ($value === 'auto') {
  234. //TODO
  235. }
  236. $value = (int)$value;
  237. if ($value >= self::MIN_SIZE && $value <= self::MAX_SIZE) {
  238. $this->options['chs'] = $value. 'x' . $value;
  239. return true;
  240. }
  241. return false;
  242. }
  243. /**
  244. * change level and margin - optional
  245. * result format: chld=<EC level>|<margin>
  246. * @return boolean Success
  247. * 2010-02-06 ms
  248. */
  249. public function setLevel($level, $margin = null) {
  250. if (in_array($level, $this->ecLevels)) {
  251. if ($margin === null) {
  252. $margin = 4; # minimum
  253. }
  254. $this->options['chld'] = strtoupper($level). '|' . $margin;
  255. return true;
  256. }
  257. return false;
  258. }
  259. public function setEncoding() {
  260. //TODO
  261. }
  262. /**
  263. * QrCodeHelper::reset()
  264. *
  265. * @return void
  266. */
  267. public function reset() {
  268. $this->setSize(self::DEFAULT_SIZE);
  269. //$this->setLevel(QS_CODE_DEFAULT_LEVEL);
  270. $this->options['chld'] = '';
  271. $this->options['choe'] = Configure::read('App.encoding');
  272. }
  273. /**
  274. * show current options - for debugging only
  275. * 2010-02-06 ms
  276. */
  277. public function debug() {
  278. return $this->options;
  279. }
  280. /**
  281. * 25 => 21x21 (L)
  282. * ...
  283. * 4000 => 547x547 (L)
  284. * @param integer $length
  285. * @return integer $size
  286. * 2011-06-06 ms
  287. */
  288. protected function _findSuitableSize() {
  289. }
  290. }