2 //============================================================+
3 // File name : barcodes.php
5 // Last Update : 2008-07-16
7 // License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
8 // ----------------------------------------------------------------------------
9 // Copyright (C) 2008 Nicola Asuni - Tecnick.com S.r.l.
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU Lesser General Public License as published by
13 // the Free Software Foundation, either version 2.1 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Lesser General Public License for more details.
21 // You should have received a copy of the GNU Lesser General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 // See LICENSE.TXT file for more information.
25 // ----------------------------------------------------------------------------
27 // Description : PHP class to creates array representations for
28 // common 1D barcodes to be used with TCPDF.
30 // Author: Nicola Asuni
36 // 09044 Quartucciu (CA)
40 //============================================================+
43 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF.
44 * @package com.tecnick.tcpdf
45 * @abstract Functions for generating string representation of common 1D barcodes.
46 * @author Nicola Asuni
47 * @copyright 2008 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
48 * @link http://www.tcpdf.org
49 * @license http://www.gnu.org/copyleft/lesser.html LGPL
54 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www.tcpdf.org).<br>
56 * @package com.tecnick.tcpdf
58 * @author Nicola Asuni
59 * @link http://www.tcpdf.org
60 * @license http://www.gnu.org/copyleft/lesser.html LGPL
65 * @var array representation of barcode.
71 * This is the class constructor.
72 * Return an array representations for common 1D barcodes:<ul>
73 * <li>$arrcode["code"] code to be printed on text label</li>
74 * <li>$arrcode["maxh"] max bar height</li>
75 * <li>$arrcode["maxw"] max bar width</li>
76 * <li>$arrcode["bcode"][$k] single bar or space in $k position</li>
77 * <li>$arrcode["bcode"][$k]["t"] bar type: true = bar, false = space.</li>
78 * <li>$arrcode["bcode"][$k]["w"] bar width in units.</li>
79 * <li>$arrcode["bcode"][$k]["h"] bar height in units.</li>
80 * <li>$arrcode["bcode"][$k]["p"] bar top position (0 = top, 1 = middle)</li></ul>
81 * @param string $code code to print
82 * @param string $type type of barcode: <ul><li>C39 : CODE 39</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED with checksum</li><li>I25 : Interleaved 2 of 5</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extension</li><li>EAN5 : 5-Digits UPC-Based Extension</li><li>EAN8 : EAN 8</li><li>EAN13 : EAN 13</li><li>UPCA : UPC-A</li><li>UPCE : UPC-E</li><li>POSTNET : POSTNET</li><li>CODABAR : CODABAR</li></ul>
84 function __construct($code, $type) {
85 $this->setBarcode($code, $type);
89 * Return an array representations of barcode.
92 function getBarcodeArray() {
93 return $this->barcode_array;
98 * @param string $code code to print
99 * @param string $type type of barcode: <ul><li>C39 : CODE 39</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED with checksum</li><li>I25 : Interleaved 2 of 5</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extension</li><li>EAN5 : 5-Digits UPC-Based Extension</li><li>EAN8 : EAN 8</li><li>EAN13 : EAN 13</li><li>UPCA : UPC-A</li><li>UPCE : UPC-E</li><li>POSTNET : POSTNET</li><li>CODABAR : CODABAR</li></ul>
102 function setBarcode($code, $type) {
103 switch (strtoupper($type)) {
104 case "C39": { // CODE 39
105 $arrcode = $this->barcode_code39($code, false, false);
108 case "C39+": { // CODE 39 with checksum
109 $arrcode = $this->barcode_code39($code, false, true);
112 case "C39E": { // CODE 39 EXTENDED
113 $arrcode = $this->barcode_code39($code, true, false);
116 case "C39E+": { // CODE 39 EXTENDED with checksum
117 $arrcode = $this->barcode_code39($code, true, true);
120 case "I25": { // Interleaved 2 of 5
121 $arrcode = $this->barcode_i25($code);
124 case "C128A": { // CODE 128 A
125 $arrcode = $this->barcode_c128($code, "A");
128 case "C128B": { // CODE 128 B
129 $arrcode = $this->barcode_c128($code, "B");
132 case "C128C": { // CODE 128 C
133 $arrcode = $this->barcode_c128($code, "C");
136 case 'EAN2': { // 2-Digits UPC-Based Extension
137 $arrcode = $this->barcode_eanext($code, 2);
140 case 'EAN5': { // 5-Digits UPC-Based Extension
141 $arrcode = $this->barcode_eanext($code, 5);
144 case 'EAN8': { // EAN 8
145 $arrcode = $this->barcode_eanupc($code, 8);
148 case 'EAN13': { // EAN 13
149 $arrcode = $this->barcode_eanupc($code, 13);
152 case 'UPCA': { // UPC-A
153 $arrcode = $this->barcode_eanupc($code, 12);
156 case 'UPCE': { // UPC-E
157 $arrcode = $this->barcode_eanupc($code, 6);
160 case "POSTNET": { // POSTNET
161 $arrcode = $this->barcode_postnet($code);
164 case "CODABAR": { // CODABAR
165 $arrcode = $this->barcode_codabar($code);
169 $this->barcode_array = false;
172 $this->barcode_array = $arrcode;
177 * @param string $code code to represent.
178 * @param boolean $checksum if true add a checksum to the code
179 * @return array barcode representation.
182 function barcode_code39($code, $extended=false, $checksum=false) {
183 $chr['0'] = '111221211';
184 $chr['1'] = '211211112';
185 $chr['2'] = '112211112';
186 $chr['3'] = '212211111';
187 $chr['4'] = '111221112';
188 $chr['5'] = '211221111';
189 $chr['6'] = '112221111';
190 $chr['7'] = '111211212';
191 $chr['8'] = '211211211';
192 $chr['9'] = '112211211';
193 $chr['A'] = '211112112';
194 $chr['B'] = '112112112';
195 $chr['C'] = '212112111';
196 $chr['D'] = '111122112';
197 $chr['E'] = '211122111';
198 $chr['F'] = '112122111';
199 $chr['G'] = '111112212';
200 $chr['H'] = '211112211';
201 $chr['I'] = '112112211';
202 $chr['J'] = '111122211';
203 $chr['K'] = '211111122';
204 $chr['L'] = '112111122';
205 $chr['M'] = '212111121';
206 $chr['N'] = '111121122';
207 $chr['O'] = '211121121';
208 $chr['P'] = '112121121';
209 $chr['Q'] = '111111222';
210 $chr['R'] = '211111221';
211 $chr['S'] = '112111221';
212 $chr['T'] = '111121221';
213 $chr['U'] = '221111112';
214 $chr['V'] = '122111112';
215 $chr['W'] = '222111111';
216 $chr['X'] = '121121112';
217 $chr['Y'] = '221121111';
218 $chr['Z'] = '122121111';
219 $chr['-'] = '121111212';
220 $chr['.'] = '221111211';
221 $chr[' '] = '122111211';
222 $chr['*'] = '121121211';
223 $chr['$'] = '121212111';
224 $chr['/'] = '121211121';
225 $chr['+'] = '121112121';
226 $chr['%'] = '111212121';
228 $code = strtoupper($code);
231 $code = $this->encode_code39_ext($code);
233 if ($code === false) {
238 $code .= $this->checksum_code39($code);
240 // add start and stop codes
241 $code = "*".$code."*";
243 $bararray = array("code" => $code, "maxw" => 0, "maxh" => 1, "bcode" => array());
245 for($i=0; $i < strlen($code); $i++) {
247 if(!isset($chr[$char])) {
251 for($j=0; $j < 9; $j++) {
257 $w = $chr[$char][$j];
258 $bararray["bcode"][$k] = array("t" => $t, "w" => $w, "h" => 1, "p" => 0);
259 $bararray["maxw"] += $w;
262 $bararray["bcode"][$k] = array("t" => false, "w" => 1, "h" => 1, "p" => 0);
263 $bararray["maxw"] += 1;
270 * Encode a string to be used for CODE 39 Extended mode.
271 * @param string $code code to represent.
272 * @return encoded string.
275 function encode_code39_ext($code) {
277 chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C',
278 chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G',
279 chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '£K',
280 chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O',
281 chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S',
282 chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W',
283 chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A',
284 chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E',
285 chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C',
286 chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G',
287 chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K',
288 chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O',
289 chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
290 chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
291 chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F',
292 chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J',
293 chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
294 chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
295 chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
296 chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
297 chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
298 chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
299 chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K',
300 chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O',
301 chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C',
302 chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G',
303 chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K',
304 chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O',
305 chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S',
306 chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W',
307 chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P',
308 chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T');
310 for ($i = 0 ; $i < strlen($code); $i++) {
311 if (ord($code[$i]) > 127) {
314 $code_ext .= $encode[$code[$i]];
320 * Calculate CODE 39 checksum (modulo 43).
321 * @param string $code code to represent.
322 * @return char checksum.
325 function checksum_code39($code) {
327 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
328 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
329 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
330 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
332 for ($i=0 ; $i < strlen($code); $i++) {
333 $k = array_keys($chars, $code[$i]);
341 * Interleaved 2 of 5 barcodes.
342 * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
343 * @param string $code code to represent.
344 * @param boolean $checksum if true add a checksum to the code
345 * @return array barcode representation.
348 function barcode_i25($code) {
362 if((strlen($code) % 2) != 0) {
363 // add leading zero if code-length is odd
366 // add start and stop codes
367 $code = 'AA'.strtolower($code).'ZA';
369 $bararray = array("code" => $code, "maxw" => 0, "maxh" => 1, "bcode" => array());
371 for($i=0; $i < strlen($code); $i=$i+2) {
372 $char_bar = $code[$i];
373 $char_space = $code[$i+1];
374 if((!isset($chr[$char_bar])) OR (!isset($chr[$char_space]))) {
378 // create a bar-space sequence
380 for($s=0; $s < strlen($chr[$char_bar]); $s++){
381 $seq .= $chr[$char_bar][$s] . $chr[$char_space][$s];
383 for($j=0; $j < strlen($seq); $j++) {
390 $bararray["bcode"][$k] = array("t" => $t, "w" => $w, "h" => 1, "p" => 0);
391 $bararray["maxw"] += $w;
401 * @param string $code code to represent.
402 * @param string $type barcode type: A, B or C
403 * @return array barcode representation.
406 function barcode_c128($code, $type="B") {
511 '211412', /* 103 START A */
512 '211214', /* 104 START B */
513 '211232', /* 105 START C */
518 switch(strtoupper($type)) {
521 $keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
522 for($i = 0; $i < 32; $i++) {
529 $keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127);
535 if ((strlen($code) % 2) != 0) {
536 //echo "The length of barcode value must be even ($code). You must pad the number with zeros.\n";
539 for($i = 0; $i <= 99; $i++) {
543 for ($i=0; $i < (strlen($code) / 2); $i++) {
544 $new_code .= chr(intval($code[(2 * $i)].$code[(2 * $i + 1)]));
553 // calculate check character
555 for ($i=0; $i < strlen($code); $i++) {
556 $sum += (strpos($keys, $code[$i]) * ($i+1));
558 $check = ($sum % 103);
560 // add start, check and stop codes
561 $code = chr($startid).$code.chr($check).chr(106).chr(107);
562 $bararray = array("code" => $code, "maxw" => 0, "maxh" => 1, "bcode" => array());
564 $len = strlen($code);
565 for($i=0; $i < $len; $i++) {
566 $ck = strpos($keys, $code[$i]);
567 if (($i == 0) OR ($i > ($len-4))) {
568 $seq = $chr[ord($code[$i])];
569 } elseif(($ck >= 0) AND isset($chr[$ck])) {
575 for($j=0; $j < 6; $j++) {
582 $bararray["bcode"][$k] = array("t" => $t, "w" => $w, "h" => 1, "p" => 0);
583 $bararray["maxw"] += $w;
591 * EAN13 and UPC-A barcodes.
592 * EAN13: European Article Numbering international retail product code
593 * UPC-A: Universal product code seen on almost all retail products in the USA and Canada
594 * UPC-E: Short version of UPC symbol
595 * @param $code (string) code to represent.
596 * @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
597 * @return array barcode representation.
600 protected function barcode_eanupc($code, $len=13) {
604 $upce = true; // UPC-E mode
606 $data_len = $len - 1;
608 $code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
609 $code_len = strlen($code);
610 // calculate check digit
612 for ($i = 1; $i < $data_len; $i+=2) {
619 for ($i = 0; $i < $data_len; $i+=2) {
620 $sum_b += ($code[$i]);
625 $r = ($sum_a + $sum_b) % 10;
629 if ($code_len == $data_len) {
632 } elseif ($r !== intval($code[$data_len])) {
642 // convert UPC-A to UPC-E
643 $tmp = substr($code, 4, 3);
644 if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) {
645 // manufacturer code ends in 000, 100, or 200
646 $upce_code = substr($code, 2, 2).substr($code, 9, 3).substr($code, 4, 1);
648 $tmp = substr($code, 5, 2);
650 // manufacturer code ends in 00
651 $upce_code = substr($code, 2, 3).substr($code, 10, 2).'3';
653 $tmp = substr($code, 6, 1);
655 // manufacturer code ends in 0
656 $upce_code = substr($code, 2, 4).substr($code, 11, 1).'4';
658 // manufacturer code does not end in zero
659 $upce_code = substr($code, 2, 5).substr($code, 11, 1);
664 //Convert digits to bars
666 'A'=>array( // left odd parity
677 'B'=>array( // left even parity
701 '0'=>array('A','A','A','A','A','A'),
702 '1'=>array('A','A','B','A','B','B'),
703 '2'=>array('A','A','B','B','A','B'),
704 '3'=>array('A','A','B','B','B','A'),
705 '4'=>array('A','B','A','A','B','B'),
706 '5'=>array('A','B','B','A','A','B'),
707 '6'=>array('A','B','B','B','A','A'),
708 '7'=>array('A','B','A','B','A','B'),
709 '8'=>array('A','B','A','B','B','A'),
710 '9'=>array('A','B','B','A','B','A')
712 $upce_parities = array();
713 $upce_parities[0] = array(
714 '0'=>array('B','B','B','A','A','A'),
715 '1'=>array('B','B','A','B','A','A'),
716 '2'=>array('B','B','A','A','B','A'),
717 '3'=>array('B','B','A','A','A','B'),
718 '4'=>array('B','A','B','B','A','A'),
719 '5'=>array('B','A','A','B','B','A'),
720 '6'=>array('B','A','A','A','B','B'),
721 '7'=>array('B','A','B','A','B','A'),
722 '8'=>array('B','A','B','A','A','B'),
723 '9'=>array('B','A','A','B','A','B')
725 $upce_parities[1] = array(
726 '0'=>array('A','A','A','B','B','B'),
727 '1'=>array('A','A','B','A','B','B'),
728 '2'=>array('A','A','B','B','A','B'),
729 '3'=>array('A','A','B','B','B','A'),
730 '4'=>array('A','B','A','A','B','B'),
731 '5'=>array('A','B','B','A','A','B'),
732 '6'=>array('A','B','B','B','A','A'),
733 '7'=>array('A','B','A','B','A','B'),
734 '8'=>array('A','B','A','B','B','A'),
735 '9'=>array('A','B','B','A','B','A')
738 $seq = '101'; // left guard bar
740 $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
741 $p = $upce_parities[$code[1]][$r];
742 for ($i = 0; $i < 6; ++$i) {
743 $seq .= $codes[$p[$i]][$upce_code[$i]];
745 $seq .= '010101'; // right guard bar
747 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
748 $half_len = intval(ceil($len / 2));
750 for ($i = 0; $i < $half_len; ++$i) {
751 $seq .= $codes['A'][$code[$i]];
754 $p = $parities[$code[0]];
755 for ($i = 1; $i < $half_len; ++$i) {
756 $seq .= $codes[$p[$i-1]][$code[$i]];
759 $seq .= '01010'; // center guard bar
760 for ($i = $half_len; $i < $len; ++$i) {
761 $seq .= $codes['C'][$code[$i]];
763 $seq .= '101'; // right guard bar
765 $clen = strlen($seq);
767 for ($i = 0; $i < $clen; ++$i) {
769 if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq[$i] != $seq[($i+1)]))) {
770 if ($seq[$i] == '1') {
775 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
776 $bararray['maxw'] += $w;
786 * @param string $code zip code to represent. Must be a string containing a zip code of the form DDDDD or DDDDD-DDDD.
787 * @return array barcode representation.
790 function barcode_postnet($code) {
793 0 => Array(2,2,1,1,1),
794 1 => Array(1,1,1,2,2),
795 2 => Array(1,1,2,1,2),
796 3 => Array(1,1,2,2,1),
797 4 => Array(1,2,1,1,2),
798 5 => Array(1,2,1,2,1),
799 6 => Array(1,2,2,1,1),
800 7 => Array(2,1,1,1,2),
801 8 => Array(2,1,1,2,1),
802 9 => Array(2,1,2,1,1)
804 $bararray = array("code" => $code, "maxw" => 0, "maxh" => 2, "bcode" => array());
806 $code = str_replace("-", "", $code);
807 $code = str_replace(" ", "", $code);
808 $len = strlen($code);
809 // calculate checksum
811 for($i=0; $i < $len; $i++) {
812 $sum += intval($code[$i]);
814 if(($sum % 10) == 0) {
817 $code .= "".(10 - ($sum % 10))."";
818 $len = strlen($code);
820 $bararray["bcode"][$k++] = array("t" => 1, "w" => 1, "h" => 2, "p" => 0);
821 $bararray["bcode"][$k++] = array("t" => 0, "w" => 1, "h" => 2, "p" => 0);
822 $bararray["maxw"] += 2;
823 for ($i=0; $i < $len; $i++) {
824 for ($j=0; $j < 5; $j++) {
825 $h = $barlen[$code[$i]][$j];
827 $bararray["bcode"][$k++] = array("t" => 1, "w" => 1, "h" => $h, "p" => $p);
828 $bararray["bcode"][$k++] = array("t" => 0, "w" => 1, "h" => 2, "p" => 0);
829 $bararray["maxw"] += 2;
833 $bararray["bcode"][$k++] = array("t" => 1, "w" => 1, "h" => 2, "p" => 0);
834 $bararray["maxw"] += 1;
840 * @param string $code code to represent.
841 * @return array barcode representation.
844 function barcode_codabar($code) {
868 $bararray = array("code" => $code, "maxw" => 0, "maxh" => 1, "bcode" => array());
872 $code = "A".strtoupper($code)."A";
873 $len = strlen($code);
874 for($i=0; $i < $len; $i++) {
875 if (!isset($chr[$code[$i]])) {
878 $seq = $chr[$code[$i]];
879 for($j=0; $j < 8; $j++) {
886 $bararray["bcode"][$k] = array("t" => $t, "w" => $w, "h" => 1, "p" => 0);
887 $bararray["maxw"] += $w;
896 //============================================================+
898 //============================================================+