Passed
Branch develop (7755a4)
by
unknown
31:27
created

barcode_gen_ean_bars()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 14
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 21
rs 9.4888
1
<?php
2
/* Copyright (C) 2004-2016 Laurent Destailleur     <[email protected]>
3
 * Copyright (C) 2004-2010 Folke Ashberg: Some lines of code were inspired from work
4
 *                         of Folke Ashberg into PHP-Barcode 0.3pl2, available as GPL
5
 *                         source code at http://www.ashberg.de/bar.
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
/**
22
 *	\file       htdocs/core/lib/barcode.lib.php
23
 *	\brief      Set of functions used for barcode generation
24
 *	\ingroup    core
25
 */
26
27
/* ******************************************************************** */
28
/*                          COLORS                                      */
29
/* ******************************************************************** */
30
$bar_color = array(0, 0, 0);
31
$bg_color = array(255, 255, 255);
32
$text_color = array(0, 0, 0);
33
34
35
/* ******************************************************************** */
36
/*                          FONT FILE                                   */
37
/* ******************************************************************** */
38
if (defined('DOL_DEFAULT_TTF_BOLD')) {
39
	$font_loc = constant('DOL_DEFAULT_TTF_BOLD');
40
}
41
// Automatic-Detection of Font if running Windows
42
// @CHANGE LDR
43
if (isset($_SERVER['WINDIR']) && @file_exists($_SERVER['WINDIR'])) {
44
	$font_loc = $_SERVER['WINDIR'].'\Fonts\arialbd.ttf';
45
}
46
if (empty($font_loc)) {
47
	die('DOL_DEFAULT_TTF_BOLD must de defined with full path to a TTF font.');
48
}
49
50
51
/* ******************************************************************** */
52
/*                          GENBARCODE                                  */
53
/* ******************************************************************** */
54
/* location of 'genbarcode'
55
 * leave blank if you don't have them :(
56
* genbarcode is needed to render encodings other than EAN-12/EAN-13/ISBN
57
*/
58
59
if (defined('PHP-BARCODE_PATH_COMMAND')) {
60
	$genbarcode_loc = constant('PHP-BARCODE_PATH_COMMAND');
61
} else {
62
	$genbarcode_loc = $conf->global->GENBARCODE_LOCATION;
63
}
64
65
66
67
68
/**
69
 * Print barcode
70
 *
71
 * @param	string	       $code		Code
72
 * @param	string	       $encoding	Encoding
73
 * @param	integer	       $scale		Scale
74
 * @param	string	       $mode		'png' or 'jpg' ...
75
 * @return	array|string   $bars		array('encoding': the encoding which has been used, 'bars': the bars, 'text': text-positioning info) or string with error message
76
 */
77
function barcode_print($code, $encoding = "ANY", $scale = 2, $mode = "png")
78
{
79
	dol_syslog("barcode.lib.php::barcode_print $code $encoding $scale $mode");
80
81
	$bars = barcode_encode($code, $encoding);
82
	if (!$bars || !empty($bars['error'])) {
83
		// Return error message instead of array
84
		if (empty($bars['error'])) {
85
			$error = 'Bad Value '.$code.' for encoding '.$encoding;
86
		} else {
87
			$error = $bars['error'];
88
		}
89
		dol_syslog('barcode.lib.php::barcode_print '.$error, LOG_ERR);
90
		return $error;
91
	}
92
	if (!$mode) {
93
		$mode = "png";
94
	}
95
	//if (preg_match("/^(text|txt|plain)$/i",$mode)) print barcode_outtext($bars['text'],$bars['bars']);
96
	//elseif (preg_match("/^(html|htm)$/i",$mode)) print barcode_outhtml($bars['text'],$bars['bars'], $scale,0, 0);
97
	//else
98
	barcode_outimage($bars['text'], $bars['bars'], $scale, $mode);
99
	return $bars;
100
}
101
102
/**
103
 * Encodes $code with $encoding using genbarcode OR built-in encoder if you don't have genbarcode only EAN-13/ISBN or UPC is possible
104
 *
105
 * You can use the following encodings (when you have genbarcode):
106
 *   ANY    choose best-fit (default)
107
 *   EAN    8 or 13 EAN-Code
108
 *   UPC    12-digit EAN
109
 *   ISBN   isbn numbers (still EAN-13)
110
 *   39     code 39
111
 *   128    code 128 (a,b,c: autoselection)
112
 *   128C   code 128 (compact form for digits)
113
 *   128B   code 128, full printable ascii
114
 *   I25    interleaved 2 of 5 (only digits)
115
 *   128RAW Raw code 128 (by Leonid A. Broukhis)
116
 *   CBR    Codabar (by Leonid A. Broukhis)
117
 *   MSI    MSI (by Leonid A. Broukhis)
118
 *   PLS    Plessey (by Leonid A. Broukhis)
119
 *
120
 * @param	string	$code		Code
121
 * @param	string	$encoding	Encoding
122
 * @return	array|false			array('encoding': the encoding which has been used, 'bars': the bars, 'text': text-positioning info)
123
 */
124
function barcode_encode($code, $encoding)
125
{
126
	global $genbarcode_loc;
127
128
	if ((preg_match("/^upc$/i", $encoding))
129
	&& (preg_match("/^[0-9]{11,12}$/", $code))
130
	) {
131
		/* use built-in UPC-Encoder */
132
		dol_syslog("barcode.lib.php::barcode_encode Use barcode_encode_upc");
133
		$bars = barcode_encode_upc($code, $encoding);
134
	} elseif ((preg_match("/^ean$/i", $encoding))
135
136
	|| (($encoding) && (preg_match("/^isbn$/i", $encoding))
137
	&& ((strlen($code) == 9 || strlen($code) == 10) ||
138
	(((preg_match("/^978/", $code) && strlen($code) == 12) ||
139
	(strlen($code) == 13)))))
140
141
	|| ((!isset($encoding) || !$encoding || (preg_match("/^ANY$/i", $encoding)))
142
	&& (preg_match("/^[0-9]{12,13}$/", $code)))
143
	) {
144
		/* use built-in EAN-Encoder */
145
		dol_syslog("barcode.lib.php::barcode_encode Use barcode_encode_ean");
146
		$bars = barcode_encode_ean($code, $encoding);
147
	} elseif (file_exists($genbarcode_loc)) {	// For example C39
148
		/* use genbarcode */
149
		dol_syslog("barcode.lib.php::barcode_encode Use genbarcode ".$genbarcode_loc." code=".$code." encoding=".$encoding);
150
		$bars = barcode_encode_genbarcode($code, $encoding);
151
	} else {
152
		print "barcode_encode needs an external programm for encodings other then EAN/ISBN (code=".$code.", encoding=".$encoding.")<BR>\n";
153
		print "<UL>\n";
154
		print "<LI>download gnu-barcode from <A href=\"https://www.gnu.org/software/barcode/\">www.gnu.org/software/barcode/</A>\n";
155
		print "<LI>compile and install them\n";
156
		print "<LI>download genbarcode from <A href=\"http://www.ashberg.de/bar/\">www.ashberg.de/bar/</A>\n";
157
		print "<LI>compile and install them\n";
158
		print "<LI>specify path the genbarcode in barcode module setup\n";
159
		print "</UL>\n";
160
		print "<BR>\n";
161
		return false;
162
	}
163
164
	return $bars;
165
}
166
167
168
/**
169
 * Calculate EAN sum
170
 *
171
 * @param	string	$ean	EAN to encode
172
 * @return	integer			Sum
173
 */
174
function barcode_gen_ean_sum($ean)
175
{
176
	$even = true;
177
	$esum = 0;
178
	$osum = 0;
179
	$ln = strlen($ean) - 1;
180
	for ($i = $ln; $i >= 0; $i--) {
181
		if ($even) {
182
			$esum += $ean[$i];
183
		} else {
184
			$osum += $ean[$i];
185
		}
186
		$even = !$even;
187
	}
188
	return (10 - ((3 * $esum + $osum) % 10)) % 10;
189
}
190
191
192
/**
193
 * Generate EAN bars
194
 *
195
 * @param	string	$ean	EAN to encode
196
 * @return	string			Encoded EAN
197
 */
198
function barcode_gen_ean_bars($ean)
199
{
200
	$digits = array(3211, 2221, 2122, 1411, 1132, 1231, 1114, 1312, 1213, 3112);
201
	$mirror = array("000000", "001011", "001101", "001110", "010011", "011001", "011100", "010101", "010110", "011010");
202
	$guards = array("9a1a", "1a1a1", "a1a7");
203
204
	$line = $guards[0];
205
	for ($i = 1; $i < 13; $i++) {
206
		$str = $digits[$ean[$i]];
207
		if ($i < 7 && $mirror[$ean[0]][$i - 1] == 1) {
208
			$line .= strrev($str);
209
		} else {
210
			$line .= $str;
211
		}
212
		if ($i == 6) {
213
			$line .= $guards[1];
214
		}
215
	}
216
	$line .= $guards[2];
217
218
	return $line;
219
}
220
221
/**
222
 * Encode EAN
223
 *
224
 * @param	string	$ean		Code
225
 * @param	string	$encoding	Encoding
226
 * @return	array				array('encoding': the encoding which has been used, 'bars': the bars, 'text': text-positioning info, 'error': error message if error)
227
 */
228
function barcode_encode_ean($ean, $encoding = "EAN-13")
229
{
230
	$ean = trim($ean);
231
	if (preg_match("/[^0-9]/i", $ean)) {
232
		return array("error"=>"Invalid encoding/code. encoding=".$encoding." code=".$ean." (not a numeric)", "text"=>"Invalid encoding/code. encoding=".$encoding." code=".$ean." (not a numeric)");
233
	}
234
	$encoding = strtoupper($encoding);
235
	if ($encoding == "ISBN") {
236
		if (!preg_match("/^978/", $ean)) {
237
			$ean = "978".$ean;
238
		}
239
	}
240
	if (preg_match("/^97[89]/", $ean)) {
241
		$encoding = "ISBN";
242
	}
243
	if (strlen($ean) < 12 || strlen($ean) > 13) {
244
		return array("error"=>"Invalid encoding/code. encoding=".$encoding." code=".$ean." (must have 12/13 numbers)", "text"=>"Invalid encoding/code. encoding=".$encoding." code=".$ean." (must have 12/13 numbers)");
245
	}
246
247
	$ean = substr($ean, 0, 12);
248
	$eansum = barcode_gen_ean_sum($ean);
249
	$ean .= $eansum;
250
	$bars = barcode_gen_ean_bars($ean);
251
252
	/* create text */
253
	$pos = 0;
254
	$text = "";
255
	for ($a = 0; $a < 13; $a++) {
256
		if ($a > 0) {
257
			$text .= " ";
258
		}
259
		$text .= "$pos:12:{$ean[$a]}";
260
		if ($a == 0) {
261
			$pos += 12;
262
		} elseif ($a == 6) {
263
			$pos += 12;
264
		} else {
265
			$pos += 7;
266
		}
267
	}
268
269
	return array(
270
		"error" => '',
271
		"encoding" => $encoding,
272
		"bars" => $bars,
273
		"text" => $text
274
	);
275
}
276
277
/**
278
 * Encode UPC
279
 *
280
 * @param	string	$upc		Code
281
 * @param	string	$encoding	Encoding
282
 * @return	array				array('encoding': the encoding which has been used, 'bars': the bars, 'text': text-positioning info, 'error': error message if error)
283
 */
284
function barcode_encode_upc($upc, $encoding = "UPC")
285
{
286
	$upc = trim($upc);
287
	if (preg_match("/[^0-9]/i", $upc)) {
288
		return array("error"=>"Invalid encoding/code. encoding=".$encoding." code=".$upc." (not a numeric)", "text"=>"Invalid encoding/code. encoding=".$encoding." code=".$upc." (not a numeric)");
289
	}
290
	$encoding = strtoupper($encoding);
291
	if (strlen($upc) < 11 || strlen($upc) > 12) {
292
		return array("error"=>"Invalid encoding/code. encoding=".$encoding." code=".$upc." (must have 11/12 numbers)", "text"=>"Invalid encoding/code. encoding=".$encoding." code=".$upc." (must have 11/12 numbers)");
293
	}
294
295
	$upc = substr("0".$upc, 0, 12);
296
	$eansum = barcode_gen_ean_sum($upc);
297
	$upc .= $eansum;
298
	$bars = barcode_gen_ean_bars($upc);
299
300
	/* create text */
301
	$pos = 0;
302
	$text = "";
303
	for ($a = 1; $a < 13; $a++) {
304
		if ($a > 1) {
305
			$text .= " ";
306
		}
307
		$text .= "$pos:12:{$upc[$a]}";
308
		if ($a == 1) {
309
			$pos += 15;
310
		} elseif ($a == 6) {
311
			$pos += 17;
312
		} elseif ($a == 11) {
313
			$pos += 15;
314
		} else {
315
			$pos += 7;
316
		}
317
	}
318
319
	return array(
320
		"error" => '',
321
		"encoding" => $encoding,
322
		"bars" => $bars,
323
		"text" => $text
324
	);
325
}
326
327
/**
328
 * Encode result of genbarcode command
329
 *
330
 * @param	string	$code		Code
331
 * @param	string	$encoding	Encoding
332
 * @return	array|false			array('encoding': the encoding which has been used, 'bars': the bars, 'text': text-positioning info)
333
 */
334
function barcode_encode_genbarcode($code, $encoding)
335
{
336
	global $genbarcode_loc;
337
338
	// Clean parameters
339
	if (preg_match("/^ean$/i", $encoding) && strlen($code) == 13) {
340
		$code = substr($code, 0, 12);
341
	}
342
	if (!$encoding) {
343
		$encoding = "ANY";
344
	}
345
	$encoding = preg_replace("/[\\\|]/", "_", $encoding);
346
	$code = preg_replace("/[\\\|]/", "_", $code);
347
348
	$command = escapeshellarg($genbarcode_loc);
349
	//$paramclear=" \"".str_replace("\"", "\\\"",$code)."\" \"".str_replace("\"", "\\\"",strtoupper($encoding))."\"";
350
	$paramclear = " ".escapeshellarg($code)." ".escapeshellarg(strtoupper($encoding));
351
352
	$fullcommandclear = $command." ".$paramclear." 2>&1";
353
	//print $fullcommandclear."<br>\n";exit;
354
355
	dol_syslog("Run command ".$fullcommandclear);
356
	$fp = popen($fullcommandclear, "r");
357
	if ($fp) {
358
		$bars = fgets($fp, 1024);
359
		$text = fgets($fp, 1024);
360
		$encoding = fgets($fp, 1024);
361
		pclose($fp);
362
	} else {
363
		dol_syslog("barcode.lib.php::barcode_encode_genbarcode failed to run popen ".$fullcommandclear, LOG_ERR);
364
		return false;
365
	}
366
	//var_dump($bars);
367
	$ret = array(
368
		"bars" => trim($bars),
369
		"text" => trim($text),
370
		"encoding" => trim($encoding),
371
		"error" => ""
372
	);
373
	//var_dump($ret);
374
	if (preg_match('/permission denied/i', $ret['bars'])) {
375
		$ret['error'] = $ret['bars'];
376
		$ret['bars'] = '';
377
		return $ret;
378
	}
379
	if (!$ret['bars']) {
380
		return false;
381
	}
382
	if (!$ret['text']) {
383
		return false;
384
	}
385
	if (!$ret['encoding']) {
386
		return false;
387
	}
388
	return $ret;
389
}
390
391
/**
392
 * Output image onto standard output, or onto disk if global filebarcode is defined
393
 *
394
 * @param	string	$text		the text-line (<position>:<font-size>:<character> ...)
395
 * @param	string	$bars   	where to place the bars  (<space-width><bar-width><space-width><bar-width>...)
396
 * @param	int		$scale		scale factor ( 1 < scale < unlimited (scale 50 will produce 5400x300 pixels when using EAN-13!!!))
397
 * @param	string	$mode   	png,gif,jpg (default='png')
398
 * @param	int		$total_y	the total height of the image ( default: scale * 60 )
399
 * @param	array	$space		default:  $space[top]   = 2 * $scale; $space[bottom]= 2 * $scale;  $space[left]  = 2 * $scale;  $space[right] = 2 * $scale;
400
 * @return	string|null
401
 */
402
function barcode_outimage($text, $bars, $scale = 1, $mode = "png", $total_y = 0, $space = '')
403
{
404
	global $bar_color, $bg_color, $text_color;
405
	global $font_loc, $filebarcode;
406
407
	//print "$text, $bars, $scale, $mode, $total_y, $space, $font_loc, $filebarcode<br>";
408
	//var_dump($text);
409
	//var_dump($bars);
410
	//var_dump($font_loc);
411
412
	/* set defaults */
413
	if ($scale < 1) {
414
		$scale = 2;
415
	}
416
	$total_y = (int) $total_y;
417
	if ($total_y < 1) {
418
		$total_y = (int) $scale * 60;
419
	}
420
	if (!$space) {
421
		$space = array('top'=>2 * $scale, 'bottom'=>2 * $scale, 'left'=>2 * $scale, 'right'=>2 * $scale);
422
	}
423
424
	/* count total width */
425
	$xpos = 0;
426
	$width = true;
427
	$ln = strlen($bars);
428
	for ($i = 0; $i < $ln; $i++) {
429
		$val = strtolower($bars[$i]);
430
		if ($width) {
431
			$xpos += $val * $scale;
432
			$width = false;
433
			continue;
434
		}
435
		if (preg_match("/[a-z]/", $val)) {
436
			/* tall bar */
437
			$val = ord($val) - ord('a') + 1;
438
		}
439
		$xpos += $val * $scale;
440
		$width = true;
441
	}
442
443
	/* allocate the image */
444
	$total_x = ($xpos) + $space['right'] + $space['right'];
445
	$xpos = $space['left'];
446
	if (!function_exists("imagecreate")) {
447
		print "You don't have the gd2 extension enabled<br>\n";
448
		return "";
449
	}
450
	$im = imagecreate($total_x, $total_y);
451
	/* create two images */
452
	$col_bg = ImageColorAllocate($im, $bg_color[0], $bg_color[1], $bg_color[2]);
453
	$col_bar = ImageColorAllocate($im, $bar_color[0], $bar_color[1], $bar_color[2]);
454
	$col_text = ImageColorAllocate($im, $text_color[0], $text_color[1], $text_color[2]);
455
	$height = round($total_y - ($scale * 10));
456
	$height2 = round($total_y - $space['bottom']);
457
458
	/* paint the bars */
459
	$width = true;
460
	$ln = strlen($bars);
461
	for ($i = 0; $i < $ln; $i++) {
462
		$val = strtolower($bars[$i]);
463
		if ($width) {
464
			$xpos += $val * $scale;
465
			$width = false;
466
			continue;
467
		}
468
		if (preg_match("/[a-z]/", $val)) {
469
			/* tall bar */
470
			$val = ord($val) - ord('a') + 1;
471
			$h = $height2;
472
		} else {
473
			$h = $height;
474
		}
475
		imagefilledrectangle($im, $xpos, $space['top'], $xpos + ($val * $scale) - 1, $h, $col_bar);
476
		$xpos += $val * $scale;
477
		$width = true;
478
	}
479
480
	$chars = explode(" ", $text);
481
	foreach ($chars as $v) {
482
		if (trim($v)) {
483
			$inf = explode(":", $v);
484
			$fontsize = $scale * ($inf[1] / 1.8);
485
			$fontheight = $total_y - ($fontsize / 2.7) + 2;
486
			imagettftext($im, $fontsize, 0, $space['left'] + ($scale * $inf[0]) + 2, $fontheight, $col_text, $font_loc, $inf[2]);
487
		}
488
	}
489
490
	/* output the image */
491
	$mode = strtolower($mode);
492
	if ($mode == 'jpg' || $mode == 'jpeg') {
493
		header("Content-Type: image/jpeg; name=\"barcode.jpg\"");
494
		imagejpeg($im);
495
	} elseif ($mode == 'gif') {
496
		header("Content-Type: image/gif; name=\"barcode.gif\"");
497
		imagegif($im);
498
	} elseif (!empty($filebarcode)) {
499
		// To wxrite into  afile onto disk
500
		imagepng($im, $filebarcode);
501
	} else {
502
		header("Content-Type: image/png; name=\"barcode.png\"");
503
		imagepng($im);
504
	}
505
}
506