Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Group Separator #26

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
92 changes: 92 additions & 0 deletions barcode-gs1-variable-length.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<html>

<head>
<title>barcode.php gs1 variable length strings test</title>
<style>
body {
font-family: Helvetica, sans-serif;
padding: 1em;
}

.imgW20rem {
width: 20rem;
}

.list-unstyled {
list-style-type: none;
}

.d-flex {
display: flex;
}
</style>
</head>

<body>

<h3>GS1-128 codes with variable length Application Identifiers</h3>
<p>0</p>
<p><img src="barcode.php?s=ean-128&ts=3&th=12&d=00858000000000000007"></p>
<p>1</p>
<p><img src="barcode.php?s=ean-128&ts=3&th=12&d=0085800000000000000702085800000000093720"></p>
<p>2</p>
<p><img src="barcode.php?s=ean-128&ts=3&th=12&d=0085800000000000000702085800000000093720\FNC110LOT123"></p>

<h3>GS1-DataMatrix codes with variable length Application Identifiers</h3>
<p>3</p>
<div class="d-flex">
<img src="barcode.php?s=gs1-dmtx&d=0108580000000009">
<ul class="list-unstyled">
<li>(01) 08580000000009</li>
</ul>
</div>

<p>4</p>
<div class="d-flex">
<img src="barcode.php?s=gs1-dmtx&d=010858000000000910LOT123"></p>
<ul class="list-unstyled">
<li>(01) 08580000000009</li>
<li>(10) LOT123</li>
</ul>
</div>

<p>5</p>
<div class="d-flex">
<img src="barcode.php?s=gs1-dmtx&d=010858000000000910LOT123\FNC1211234567"></p>
<ul class="list-unstyled">
<li>(01) 08580000000009</li>
<li>(10) LOT123</li>
<li>(21) 1234567</li>
</ul>
</div>

<h3>GS1-QR codes with variable length Application Identifiers</h3>
<p>3</p>
<div class="d-flex">
<img src="barcode.php?s=gs1-qr-m&d=0108580000000009">
<ul class="list-unstyled">
<li>(01) 08580000000009</li>
</ul>
</div>

<p>4</p>
<div class="d-flex">
<img src="barcode.php?s=gs1-qr-m&d=010858000000000910LOT123"></p>
<ul class="list-unstyled">
<li>(01) 08580000000009</li>
<li>(10) LOT123</li>
</ul>
</div>

<p>5</p>
<div class="d-flex">
<img src="barcode.php?s=gs1-qr-h&d=010858000000000910LOT123\FNC1211234567"></p>
<ul class="list-unstyled">
<li>(01) 08580000000009</li>
<li>(10) LOT123</li>
<li>(21) 1234567</li>
</ul>
</div>
</body>

</html>
67 changes: 46 additions & 21 deletions barcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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++) {
Expand All @@ -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])/';
Expand Down Expand Up @@ -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);
Expand All @@ -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));
Expand All @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down