diff --git a/README.md b/README.md index 3f755b7..f695c6b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### Generate barcodes from a single PHP file. MIT license. * Output to PNG, GIF, JPEG, or SVG. - * Generates UPC-A, UPC-E, EAN-13, EAN-8, Code 39, Code 93, Code 128, Codabar, ITF, QR Code, and Data Matrix. + * Generates UPC-A, UPC-E, EAN-13, EAN-8, Code 39, Code 93, Code 128, GS1 128, Codabar, ITF, QR Code, GS1 QR Code, DataMatrix and GS1 DataMatrix. Use from a PHP script: @@ -50,13 +50,13 @@ barcode.php?f=svg&s=qr&d=HELLO%20WORLD&sf=8&ms=r&md=0.8 `s` - Symbology (type of barcode). One of: ``` - upc-a code-39 qr dmtx - upc-e code-39-ascii qr-l dmtx-s - ean-8 code-93 qr-m dmtx-r - ean-13 code-93-ascii qr-q gs1-dmtx - ean-13-pad code-128 qr-h gs1-dmtx-s - ean-13-nopad codabar gs1-dmtx-r - ean-128 itf + upc-a code-39 qr gs1-qr-q gs1-dmtx-r + upc-e code-39-ascii qr-l gs1-qr-h + ean-8 code-93 qr-m dmtx + ean-13 code-93-ascii qr-q dmtx-s + ean-13-pad code-128 qr-h dmtx-r + ean-13-nopad codabar gs1-qr-l gs1-dmtx + ean-128 itf gs1-qr-m gs1-dmtx-s ``` `d` - Data. For UPC or EAN, use `*` for missing digit. For Codabar, use `ABCD` or `ENT*` for start and stop characters. For QR, encode in Shift-JIS for kanji mode. @@ -110,3 +110,17 @@ barcode.php?f=svg&s=qr&d=HELLO%20WORLD&sf=8&ms=r&md=0.8 `ww` - Width of wide modules and spaces. Applies to Code 39, Codabar, and ITF only. Default is 3. `wn` - Width of narrow space between characters. Applies to Code 39 and Codabar only. Default is 1. + +#### Keywords: + +`\FNC1` +- used in `d` - Data option as a part of the data string. +- when used, it is replaced with a Group separator <GS> ASCII char 29 in encoded data string. +- it should be used to terminate variable length GS1 Application Identifiers in GS1 128 and GS1 DataMatrix barcodes. +- if needed, you can replace `\FNC1` keyword with `yourKeyword` in barcode.php file, the functionality will remain. Do not forget to replace all occurrences. +- available in barcode symbologies: +``` + ean-128 gs1-dmtx-s gs1-qr-l gs1-qr-q + gs1-dmtx gs1-dmtx-r gs1-qr-m gs1-qr-h +``` +- Do not confuse this with <FNC1> character. Initially, it was intended to use the <FNC1> character as a separator character, but since according to [this stackoverflow answer](https://stackoverflow.com/questions/31318648/what-is-the-actual-hex-binary-value-of-the-gs1-fnc1-character/31322815#31322815) by Terry Burton, FNC1 is a non-data character that requires special treatment. And I dont want to fizzle around this when it is not necessary. Instead, I decided to use the <GS> character. According to the [GS1 General Specifications](https://www.gs1.org/standards/barcodes-epcrfid-id-keys/gs1-general-specifications), the <FNC1> and <GS> characters are in the role of a separator character substitutes. The `\FNC1` remained as a keyword for its uniqueness. If you need, you can replace it with `\GS` or any other keyword you consider unique. diff --git a/barcode-gs1-variable-length.html b/barcode-gs1-variable-length.html new file mode 100644 index 0000000..da617d2 --- /dev/null +++ b/barcode-gs1-variable-length.html @@ -0,0 +1,92 @@ + + + + barcode.php gs1 variable length strings test + + + + + +

GS1-128 codes with variable length Application Identifiers

+

0

+

+

1

+

+

2

+

+ +

GS1-DataMatrix codes with variable length Application Identifiers

+

3

+
+ + +
+ +

4

+
+

+ +
+ +

5

+
+

+ +
+ +

GS1-QR codes with variable length Application Identifiers

+

3

+
+ + +
+ +

4

+
+

+ +
+ +

5

+
+

+ +
+ + + \ No newline at end of file diff --git a/barcode.php b/barcode.php index b111501..7f2a7fb 100644 --- a/barcode.php +++ b/barcode.php @@ -239,11 +239,15 @@ private function dispatch_encode($symbology, $data, $options) { case 'codabar' : return $this->codabar_encode($data); case 'itf' : return $this->itf_encode($data); case 'itf14' : return $this->itf_encode($data); - case 'qr' : return $this->qr_encode($data, 0); - case 'qrl' : return $this->qr_encode($data, 0); - case 'qrm' : return $this->qr_encode($data, 1); - case 'qrq' : return $this->qr_encode($data, 2); - case 'qrh' : return $this->qr_encode($data, 3); + case 'qr' : return $this->qr_encode($data, 0, false); + case 'qrl' : return $this->qr_encode($data, 0, false); + case 'qrm' : return $this->qr_encode($data, 1, false); + case 'qrq' : return $this->qr_encode($data, 2, false); + case 'qrh' : return $this->qr_encode($data, 3, false); + case 'gs1qrl' : return $this->qr_encode($data, 0, true); + case 'gs1qrm' : return $this->qr_encode($data, 1, true); + case 'gs1qrq' : return $this->qr_encode($data, 2, true); + case 'gs1qrh' : return $this->qr_encode($data, 3, true); case 'dmtx' : return $this->dmtx_encode($data,false,false); case 'dmtxs' : return $this->dmtx_encode($data,false,false); case 'dmtxr' : return $this->dmtx_encode($data, true,false); @@ -1414,7 +1418,8 @@ private function code_93_ascii_encode($data) { private function code_128_encode($data, $dstate, $fnc1) { $data = preg_replace('/[\x80-\xFF]/', '', $data); - $label = preg_replace('/[\x00-\x1F\x7F]/', ' ', $data); + $labelData = str_replace('\FNC1', '', $data); + $label = preg_replace('/[\x00-\x1F\x7F]/', ' ', $labelData); $chars = $this->code_128_normalize($data, $dstate, $fnc1); $checksum = $chars[0] % 103; for ($i = 1, $n = count($chars); $i < $n; $i++) { @@ -1437,6 +1442,7 @@ private function code_128_encode($data, $dstate, $fnc1) { } private function code_128_normalize($data, $dstate, $fnc1) { + $data = str_replace('\FNC1', chr(29), $data); $detectcba = '/(^[0-9]{4,}|^[0-9]{2}$)|([\x60-\x7F])|([\x00-\x1F])/'; $detectc = '/(^[0-9]{6,}|^[0-9]{4,}$)/'; $detectba = '/([\x60-\x7F])|([\x00-\x1F])/'; @@ -1727,8 +1733,9 @@ private function itf_encode($data) { /* - - - - QR ENCODER - - - - */ - private function qr_encode($data, $ecl) { - list($mode, $vers, $ec, $data) = $this->qr_encode_data($data, $ecl); + private function qr_encode($data, $ecl, $fnc1) { + $data = str_replace('\FNC1', chr(29), $data); + list($mode, $vers, $ec, $data) = $this->qr_encode_data($data, $ecl, $fnc1); $data = $this->qr_encode_ec($data, $ec, $vers); list($size, $mtx) = $this->qr_create_matrix($vers, $data); list($mask, $mtx) = $this->qr_apply_best_mask($mtx, $size); @@ -1741,7 +1748,7 @@ private function qr_encode($data, $ecl) { ); } - private function qr_encode_data($data, $ecl) { + private function qr_encode_data($data, $ecl, $fnc1) { $mode = $this->qr_detect_mode($data); $version = $this->qr_detect_version($data, $mode, $ecl); $version_group = (($version < 10) ? 0 : (($version < 27) ? 1 : 2)); @@ -1751,18 +1758,19 @@ private function qr_encode_data($data, $ecl) { if ($mode == 3) $max_chars <<= 1; $data = substr($data, 0, $max_chars); /* Convert from character level to bit level. */ + error_log($mode, 0); switch ($mode) { case 0: - $code = $this->qr_encode_numeric($data, $version_group); + $code = $this->qr_encode_numeric($data, $version_group, $fnc1); break; case 1: - $code = $this->qr_encode_alphanumeric($data, $version_group); + $code = $this->qr_encode_alphanumeric($data, $version_group, $fnc1); break; case 2: - $code = $this->qr_encode_binary($data, $version_group); + $code = $this->qr_encode_binary($data, $version_group, $fnc1); break; case 3: - $code = $this->qr_encode_kanji($data, $version_group); + $code = $this->qr_encode_kanji($data, $version_group, $fnc1); break; } for ($i = 0; $i < 4; $i++) $code[] = 0; @@ -1812,8 +1820,12 @@ private function qr_detect_version($data, $mode, $ecl) { return 40; } - private function qr_encode_numeric($data, $version_group) { - $code = array(0, 0, 0, 1); + private function qr_encode_numeric($data, $version_group, $fnc1) { + if ($fnc1 === true) { + $code = array(0, 1, 0, 1, 0, 0, 0, 1); + } else { + $code = array(0, 0, 0, 1); + } $length = strlen($data); switch ($version_group) { case 2: /* 27 - 40 */ @@ -1855,9 +1867,13 @@ private function qr_encode_numeric($data, $version_group) { return $code; } - private function qr_encode_alphanumeric($data, $version_group) { + private function qr_encode_alphanumeric($data, $version_group, $fnc1) { $alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'; - $code = array(0, 0, 1, 0); + if ($fnc1 === true) { + $code = array(0, 1, 0, 1, 0, 0, 1, 0); + } else { + $code = array(0, 0, 1, 0); + } $length = strlen($data); switch ($version_group) { case 2: /* 27 - 40 */ @@ -1907,8 +1923,12 @@ private function qr_encode_alphanumeric($data, $version_group) { return $code; } - private function qr_encode_binary($data, $version_group) { - $code = array(0, 1, 0, 0); + private function qr_encode_binary($data, $version_group, $fnc1) { + if ($fnc1 === true) { + $code = array(0, 1, 0, 1, 0, 1, 0, 0); + } else { + $code = array(0, 1, 0, 0); + } $length = strlen($data); switch ($version_group) { case 2: /* 27 - 40 */ @@ -1945,8 +1965,12 @@ private function qr_encode_binary($data, $version_group) { return $code; } - private function qr_encode_kanji($data, $version_group) { - $code = array(1, 0, 0, 0); + private function qr_encode_kanji($data, $version_group, $fnc1) { + if ($fnc1 === true) { + $code = array(0, 1, 0, 1, 1, 0, 0, 0); + } else { + $code = array(1, 0, 0, 0); + } $length = strlen($data); switch ($version_group) { case 2: /* 27 - 40 */ @@ -2885,6 +2909,7 @@ private function dmtx_encode($data, $rect, $fnc1) { private function dmtx_encode_data($data, $rect, $fnc1) { /* Convert to data codewords. */ + $data = str_replace('\FNC1', chr(29), $data); $edata = ($fnc1 ? array(232) : array()); $length = strlen($data); $offset = 0;