QrCodeHelper.php 7.2 KB

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