1<?php
2
3namespace jucksearm\barcode\lib;
4
5use jucksearm\barcode\lib\Barcode2D;
6
7class QRcodeFactory
8{
9	private $_attributes;
10	private $_borderPx = 1;
11	private $_marginPx = 5;
12	private $_scalePx = 1;
13
14	public function __construct()
15	{
16		$this->_attributes = [
17			'code'  => null,
18			'emblem' => null,
19			'level' => 'L',
20			'file'  => null,
21			'size'   => 100,
22			'margin' => 1,
23			'color'  => '000',
24		];
25	}
26
27	public function __set($name, $value)
28	{
29		$setter = 'set' . $name;
30        if (method_exists($this, $setter)) {
31            // set property
32            $this->$setter($value);
33
34            return;
35         }
36	}
37
38	public function __get($name)
39	{
40		$getter = 'get' . $name;
41        if (method_exists($this, $getter)) {
42            // read property
43            return $this->$getter();
44        }
45	}
46
47	public function setAttribute($name, $value)
48	{
49		if ($value === null) return;
50
51		$this->_attributes[$name] = $value;
52	}
53
54	public function getAttribute($name)
55	{
56		return $this->_attributes[$name];
57	}
58
59	public function setAttributes($attrs = [])
60	{
61		array_merge($this->_attributes, $attrs);
62	}
63
64	public function getAttributes()
65	{
66		return $this->_attributes;
67	}
68
69	public function setCode($value)
70	{
71		$this->setAttribute('code', $value);
72		return $this;
73	}
74
75	public function getCode()
76	{
77		return $this->getAttribute('code');
78	}
79
80	public function setEmblem($value)
81	{
82		$this->setAttribute('emblem', $value);
83		return $this;
84	}
85
86	public function getEmblem()
87	{
88		if ($this->getAttribute('emblem') == null) return null;
89
90		$min = 10;
91		$emblem = [];
92		$emblem['image'] = $this->getAttribute('emblem');
93		$bcd = $this->barcode->getBarcodeArray();
94
95		$emblem['frame'] = $this->size;
96		$area = floor(($this->scale->size * $bcd['num_cols']) * 0.2);
97		$emblem['area'] = $area < $min ? $min:$area ;
98		$emblem['posX'] = floor(($emblem['frame'] - $emblem['area']) / 2);
99		$emblem['posY'] = $emblem['posX'];
100
101		return (object) $emblem;
102	}
103
104	public function setLevel($value)
105	{
106		if (!in_array($value, ['L','M','Q','H'])) return $this;
107
108		$this->setAttribute('level', $value);
109		return $this;
110	}
111
112	public function getLevel()
113	{
114		return $this->getAttribute('level');
115	}
116
117	public function setFile($value)
118	{
119		$this->setAttribute('file', $value);
120		return $this;
121	}
122
123	public function getFile()
124	{
125		return $this->getAttribute('file');
126	}
127
128	public function setSize($value)
129	{
130		$this->setAttribute('size', $value);
131		return $this;
132	}
133
134	public function getSize()
135	{
136		return $this->getAttribute('size') * $this->_scalePx;
137	}
138
139	public function setMargin($value)
140	{
141		$this->setAttribute('margin', $value);
142		return $this;
143	}
144
145	public function getMargin()
146	{
147		return $this->getAttribute('margin');
148	}
149
150	public function setColor($value)
151	{
152		$this->setAttribute('color', $value);
153		return $this;
154	}
155
156	public function getColor()
157	{
158		return $this->getAttribute('color');
159	}
160
161	public function getBarcode()
162	{
163		return new Barcode2D($this->code, 'QRCODE,' . $this->level);
164	}
165
166	public function getHexColor()
167	{
168		$color = $this->color;
169
170		return '#'.$color;
171	}
172
173	public function getRgbColor()
174	{
175		$color = $this->color;
176
177		if (strlen($color) > 3) {
178			$r = hexdec(substr($color, 0, 2));
179			$g = hexdec(substr($color, 2, 2));
180			$b = hexdec(substr($color, 4, 2));
181		} else {
182			$r = hexdec(substr($color, 0, 1).substr($color, 0, 1));
183			$g = hexdec(substr($color, 1, 1).substr($color, 1, 1));
184			$b = hexdec(substr($color, 2, 1).substr($color, 2, 1));
185		}
186
187		return [$r, $g, $b];
188	}
189
190	private function getScale()
191	{
192		$scale = [];
193		$bcd = $this->barcode->getBarcodeArray();
194
195		if ($this->margin > 0) {
196			$scale['frame'] = $this->size - ($this->_borderPx * 2);
197			$scale['margin'] = round($this->size * ($this->margin / 100));
198			$scale['area'] = $scale['frame'] - (($scale['margin'] * 2));
199			$scale['size'] = floor($scale['area'] / $bcd['num_cols']);
200			$scale['posX'] = $scale['margin'] + floor(($scale['area'] - ($bcd['num_cols'] * $scale['size'])) / 2);
201			$scale['posY'] = $scale['posX'];
202		} else {
203			$scale['frame'] = $this->size;
204			$scale['margin'] = 0;
205			$scale['area'] = $this->size;
206			$scale['size'] = floor($scale['area'] / $bcd['num_cols']);
207			$scale['posX'] = floor(($scale['frame'] - ($bcd['num_cols'] * $scale['size'])) / 2);
208			$scale['posY'] = $scale['posX'];
209		}
210
211		if ($this->size < $scale['frame'])
212			throw new \Exception('This size not render.');
213
214		return (object) $scale;
215	}
216
217	public function getQRcodeHtmlData()
218	{
219		$bcd = $this->barcode->getBarcodeArray();
220		$color = $this->hexColor;
221		$scale = $this->scale;
222
223		if ($this->margin > 0) {
224			$qrcodeData = '<div style="position:absolute; border: solid '.$this->_borderPx.'px '.$this->hexColor.'; width:'.($this->size - 2).'px; height:'.($this->size - 2).'px">';
225		} else {
226			$qrcodeData = '<div style="position:absolute; width:'.$this->size.'px; height:'.$this->size.'px">';
227		}
228
229		$qrcodeData .= '<div style="font-size:0;position:absolute; width:'.($scale->size * $bcd['num_cols']).'px;height:'.($scale->size * $bcd['num_rows']).'px; top:'.$scale->posY.'px; left:'.$scale->posX.'px" z-index:1;>'."\n";
230
231		$w = $scale->size;
232		$h = $scale->size;
233
234		// print bars
235		$y = 0;
236		// for each row
237		for ($r = 0; $r < $bcd['num_rows']; ++$r) {
238			$x = 0;
239			// for each column
240			for ($c = 0; $c < $bcd['num_cols']; ++$c) {
241				if ($bcd['bcode'][$r][$c] == 1) {
242					// draw a single barcode cell
243					$qrcodeData .= '<div style="background-color:'.$color.';width:'.$w.'px;height:'.$h.'px;position:absolute;left:'.$x.'px;top:'.$y.'px;">&nbsp;</div>'."\n";
244				}
245				$x += $w;
246			}
247			$y += $h;
248		}
249
250		$qrcodeData .= '</div>'."\n";
251
252		if ($this->emblem != null) {
253			$emblem = $this->emblem;
254			$qrcodeData .= '<div style="position:absolute; z-index:2; width:'.$emblem->area.'px; height:'.$emblem->area.'px; background-image:url('.$emblem->image.'); background-size: cover; background-repeat:no-repeat; background-position: center center; top:'.$emblem->posY.'px; left:'.$emblem->posX.'px"></div>'."\n";
255		}
256
257		$qrcodeData .= '</div>'."\n";
258
259		return $qrcodeData;
260	}
261
262	public function getQRcodePngData()
263	{
264		$bcd = $this->barcode->getBarcodeArray();
265		$this->_borderPx = 0;
266		$color = $this->rgbColor;
267		$scale = $this->scale;
268
269		$w = $bcd['num_cols'] * $scale->size;
270		$h = $bcd['num_rows'] * $scale->size;
271
272		if (function_exists('imagecreate')) {
273			$png = imagecreate($w, $h);
274			$bgcol = imagecolorallocate($png, 255, 255, 255);
275			imagecolortransparent($png, $bgcol);
276			$fgcol = imagecolorallocate($png, $color[0], $color[1], $color[2]);
277		} else {
278			return false;
279		}
280
281		// print bars
282		$y = 0;
283		// for each row
284		for ($r = 0; $r < $bcd['num_rows']; ++$r) {
285			$x = 0;
286			// for each column
287			for ($c = 0; $c < $bcd['num_cols']; ++$c) {
288				if ($bcd['bcode'][$r][$c] == 1) {
289					imagefilledrectangle($png, $x, $y, ($x + $scale->size - 1), ($y + $scale->size - 1), $fgcol);
290				}
291				$x += $scale->size;
292			}
293			$y += $scale->size;
294		}
295
296		$frame = imagecreatetruecolor($this->size, $this->size);
297		$blank = imagecreate($this->size, $this->size);
298		imagecolorallocate($blank, 255, 255, 255);
299		imagecopyresampled($frame, $blank, 0, 0, 0, 0, imagesx($blank), imagesy($blank), imagesx($blank), imagesy($blank));
300		imagedestroy($blank);
301		imagecopyresampled($frame, $png, $scale->posX, $scale->posY, 0, 0, imagesx($png), imagesy($png), imagesx($png), imagesy($png));
302		imagedestroy($png);
303
304		if ($this->emblem != null) {
305			$emblem = $this->emblem;
306			$emblemData = file_get_contents($emblem->image);
307			$emblemImage = imagecreatefromstring($emblemData);
308			imagecopyresampled($frame, $emblemImage, ((imagesx($frame) - $emblem->area) / 2), ((imagesy($frame) - $emblem->area) / 2), 0, 0, $emblem->area, $emblem->area, imagesx($emblemImage), imagesy($emblemImage));
309			imagedestroy($emblemImage);
310		}
311
312		if ($this->file === null) {
313			ob_start();
314			imagepng($frame);
315			$qrcodeData = ob_get_clean();
316		} else {
317			preg_match("/\.png$/", $this->file, $output);
318			if (count($output) == 0)
319				throw new \Exception('Incorrect file extension format.');
320
321			$filePath = explode(DIRECTORY_SEPARATOR, $this->file);
322			if (count($filePath) == 1 ) {
323				$savePath = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'tmp'.DIRECTORY_SEPARATOR.$this->file;
324			} else {
325				$savePath = $this->file;
326			}
327
328			if (!@imagepng($frame, $savePath))
329				throw new \Exception('Not found save path.');
330
331			$qrcodeData = file_get_contents($savePath);
332		}
333
334		imagedestroy($frame);
335
336		return $qrcodeData;
337	}
338
339	public function getQRcodeSvgData()
340	{
341		$bcd = $this->barcode->getBarcodeArray();
342		$color = $this->hexColor;
343		$scale = $this->scale;
344
345		$w = $bcd['num_cols'] * $scale->size;
346		$h = $bcd['num_rows'] * $scale->size;
347
348		$repstr = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
349		$qrcodeData = '<'.'?'.'xml version="1.0" standalone="no"'.'?'.'>'."\n";
350		$qrcodeData .= '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'."\n";
351		$qrcodeData .= '<svg width="'.$this->size.'" height="'.$this->size.'" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'."\n";
352		$qrcodeData .= "\t".'<desc>'.strtr($bcd['code'], $repstr).'</desc>'."\n";
353
354		if ($this->margin > 0) {
355			$qrcodeData .= "\t".'<g id="border" fill="none" stroke="'.$color.'">'."\n";
356			$qrcodeData .= "\t\t".'<rect height="'.$this->size.'" width="'.$this->size.'" y="0" x="0" />'."\n";
357			$qrcodeData .= "\t".'</g>'."\n";
358		}
359
360		$qrcodeData .= "\t".'<g id="bars" fill="'.$color.'" stroke="none">'."\n";
361		// print bars
362		$y = $scale->posY;
363		// for each row
364		for ($r = 0; $r < $bcd['num_rows']; ++$r) {
365			$x = $scale->posX;
366			// for each column
367			for ($c = 0; $c < $bcd['num_cols']; ++$c) {
368				if ($bcd['bcode'][$r][$c] == 1) {
369					// draw a single barcode cell
370					$qrcodeData .= "\t\t".'<rect x="'.$x.'" y="'.$y.'" width="'.$scale->size.'" height="'.$scale->size.'" />'."\n";
371				}
372				$x += $scale->size;
373			}
374			$y += $scale->size;
375		}
376		$qrcodeData .= "\t".'</g>'."\n";
377
378		if ($this->emblem != null) {
379			$emblem = $this->emblem;
380			$qrcodeData .= "\t".'<g id="emblem">'."\n";
381			$qrcodeData .= "\t\t".'<image xlink:href="'.$emblem->image.'" x="'.$emblem->posX.'" y="'.$emblem->posY.'" height="'.$emblem->area.'" width="'.$emblem->area.'"/>'."\n";
382			$qrcodeData .= "\t".'</g>'."\n";
383		}
384
385		$qrcodeData .= '</svg>'."\n";
386
387		if ($this->file != null) {
388			preg_match("/\.svg$/", $this->file, $output);
389			if (count($output) == 0)
390				throw new \Exception('Incorrect file extension format.');
391
392			$filePath = explode(DIRECTORY_SEPARATOR, $this->file);
393			if (count($filePath) == 1 ) {
394				$savePath = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'tmp'.DIRECTORY_SEPARATOR.$this->file;
395			} else {
396				$savePath = $this->file;
397			}
398
399			if (!@file_put_contents($savePath, $qrcodeData))
400				throw new \Exception('Not found save path.');
401		}
402
403		return $qrcodeData;
404	}
405
406	public function renderHTML()
407	{
408		$qrcodeData = $this->getQRcodeHtmlData();
409
410		header('Content-Type: text/html');
411		header('Content-Length: '.strlen($qrcodeData));
412		header('Cache-Control: no-cache');
413		header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
414
415		echo $qrcodeData;
416	}
417
418	public function renderPNG()
419	{
420		$qrcodeData = $this->getQRcodePngData();
421
422		header('Content-Type: image/png');
423		header('Content-Length: '.strlen($qrcodeData));
424		header('Cache-Control: no-cache');
425		header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
426
427		echo $qrcodeData;
428	}
429
430	public function renderSVG()
431	{
432		$qrcodeData = $this->getQRcodeSvgData();
433
434		header('Content-Type: image/svg+xml');
435		header('Content-Length: '.strlen($qrcodeData));
436		header('Cache-Control: no-cache');
437		header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
438
439		echo $qrcodeData;
440	}
441}