Skip to content

Commit 8f45f3f

Browse files
authored
Merge pull request #188 from illuminatusds/add-telepen-1d-type
Add Telepen/Telepen Numeric
2 parents 10d3507 + 8279d3d commit 8f45f3f

6 files changed

Lines changed: 377 additions & 0 deletions

File tree

src/BarcodeGenerator.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
use Picqer\Barcode\Types\TypeRms4cc;
5959
use Picqer\Barcode\Types\TypeStandard2of5;
6060
use Picqer\Barcode\Types\TypeStandard2of5Checksum;
61+
use Picqer\Barcode\Types\TypeTelepen;
6162
use Picqer\Barcode\Types\TypeUpcA;
6263
use Picqer\Barcode\Types\TypeUpcE;
6364
use Picqer\Barcode\Types\TypeUpcExtension2;
@@ -90,6 +91,8 @@ abstract class BarcodeGenerator
9091
const TYPE_MSI_CHECKSUM = 'MSI+'; // MSI + CHECKSUM (modulo 11)
9192
const TYPE_POSTNET = 'POSTNET';
9293
const TYPE_PLANET = 'PLANET';
94+
const TYPE_TELEPEN_ALPHA = 'TELEPENALPHA';
95+
const TYPE_TELEPEN_NUMERIC = 'TELEPENNUMERIC';
9396
const TYPE_RMS4CC = 'RMS4CC'; // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
9497
const TYPE_KIX = 'KIX'; // KIX (Klant index - Customer index)
9598
const TYPE_IMB = 'IMB'; // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
@@ -203,6 +206,13 @@ protected function createDataBuilderForType(string $type)
203206

204207
case self::TYPE_PHARMA_CODE_TWO_TRACKS:
205208
return new TypePharmacodeTwoCode();
209+
210+
case self::TYPE_TELEPEN_ALPHA:
211+
return new TypeTelepen();
212+
213+
case self::TYPE_TELEPEN_NUMERIC:
214+
return new TypeTelepen('numeric');
215+
206216
}
207217

208218
throw new UnknownTypeException();

src/Types/TypeTelepen.php

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
/**
3+
* Adapted by Darren Stephens <darren.stephens@durham.ac.uk>
4+
* from Java implementation of Telepen by <rstuart114@gmail.com> Robin Stuart
5+
* at https://github.com/woo-j/OkapiBarcode which uses the
6+
* Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Implements Telepen (also known as Telepen Alpha), and Telepen Numeric.
9+
*
10+
* Telepen can encode ASCII text input and includes a modulo-127 check digit.
11+
* Telepen Numeric allows compression of numeric data into a Telepen symbol. Data
12+
* can consist of pairs of numbers or pairs consisting of a numerical digit followed
13+
* by an X character. Telepen Numeric also includes a mod-127 check digit.
14+
*/
15+
16+
namespace Picqer\Barcode\Types;
17+
18+
use Picqer\Barcode\Barcode;
19+
use Picqer\Barcode\BarcodeBar;
20+
use Picqer\Barcode\Exceptions\InvalidFormatException;
21+
22+
class TypeTelepen implements TypeInterface
23+
{
24+
private const TELEPEN_START_CHAR = '_';
25+
private const TELEPEN_STOP_CHAR = 'z';
26+
private const TELEPEN_ALPHA = 'alpha';
27+
private const TELEPEN_NUMERIC = 'numeric';
28+
29+
private $telepen_lookup_table;
30+
private $mode;
31+
32+
public function __construct($m = 'alpha')
33+
{
34+
$this->mode = self::TELEPEN_ALPHA;
35+
if (strtolower($m) == 'numeric') {
36+
$this->mode = self::TELEPEN_NUMERIC;
37+
}
38+
$this->createTelepenConversionTable();
39+
}
40+
41+
public function getBarcodeData(string $code): Barcode
42+
{
43+
/* The stream we get from the telepen output gives us the
44+
* width of alternating black/white stripes
45+
*/
46+
47+
$encoded = $this->encode($code); //binary string
48+
$barcode = new Barcode($code);
49+
50+
$drawBar = true;
51+
for ($i = 0; $i < strlen($encoded); ++$i) {
52+
$barWidth = $encoded[$i];
53+
$barcode->addBar(new BarcodeBar($barWidth, 250, $drawBar));
54+
$drawBar = !$drawBar; //flip to other colour
55+
}
56+
57+
return $barcode;
58+
}
59+
60+
protected function encode($code) : string
61+
{
62+
$result = null;
63+
if ($this->mode == self::TELEPEN_ALPHA) {
64+
$result = $this->encodeAlpha($code);
65+
} else {
66+
$result = $this->encodeNumeric($code);
67+
}
68+
69+
return $result;
70+
}
71+
72+
protected function encodeAlpha($code) : string
73+
{
74+
// We aren't interested in the non-printable parts of the ASCII set
75+
if (
76+
!preg_match('/[ -~]+/', $code)
77+
) { // everything from ASCII32-ASCII127
78+
throw new InvalidFormatException("Invalid characters in data");
79+
}
80+
81+
$count = 0;
82+
83+
/* other implementations use the byte-chr-int type equivalence to work
84+
* with array indices in the conversion/lookup table. It's probably
85+
* better to be more explicit with php, hence the use of ord and chr here.
86+
*/
87+
88+
// begin with start char
89+
$dest = $this->telepen_lookup_table[ord(self::TELEPEN_START_CHAR)];
90+
91+
for ($i = 0; $i < strlen($code); $i++) {
92+
//$ascii_code = ord(substr($code, $i, 1));
93+
$ascii_code = ord($code[$i]);
94+
$dest .= ($this->telepen_lookup_table[$ascii_code]);
95+
$count += $ascii_code;
96+
}
97+
98+
// Now add check and terminator
99+
$check_digit = 127 - ($count % 127);
100+
if ($check_digit == 127) {
101+
$check_digit = 0;
102+
}
103+
104+
$dest .= $this->telepen_lookup_table[ord($check_digit)];
105+
$dest .= $this->telepen_lookup_table[ord(self::TELEPEN_STOP_CHAR)]; // Stop
106+
107+
return $dest;
108+
}
109+
110+
private function encodeNumeric(string $code) : string
111+
{
112+
113+
/* If input contains non-numeric or X, exit */
114+
if (!preg_match('/^[0-9X]+$/', $code)) {
115+
throw new InvalidFormatException("Invalid characters in data");
116+
}
117+
118+
/* If input is an odd length, exit */
119+
$t = '';
120+
if (strlen($code) % 2 > 0) {
121+
throw new InvalidFormatException("There must be an even number of digits");
122+
}
123+
124+
$count = 0;
125+
$dest = $this->telepen_lookup_table[ord(self::TELEPEN_START_CHAR)]; // begin with the start character _
126+
127+
for ($i = 0; $i < strlen($code); $i += 2) {
128+
$c1 = $code[$i];
129+
$c2 = $code[$i+1];
130+
/* Input nX is allowed, but Xn is not */
131+
if ($c1 == 'X') {
132+
throw new InvalidFormatException("Invalid position of X in data");
133+
}
134+
$glyph = null;
135+
if ($c2 == 'X') {
136+
$glyph = (ord($c1) - ord('0')) + 17;
137+
} else {
138+
$glyph = ((10 * (ord($c1) - ord('0'))) + (ord($c2) - ord('0'))) + 27;
139+
}
140+
$count += $glyph;
141+
$dest .= $this->telepen_lookup_table[$glyph];
142+
}
143+
144+
$check_digit = 127 - ($count % 127);
145+
if ($check_digit == 127) {
146+
$check_digit = 0;
147+
}
148+
149+
$dest .= $this->telepen_lookup_table[$check_digit];
150+
$dest .= $this->telepen_lookup_table[ord(self::TELEPEN_STOP_CHAR)]; // Stop
151+
152+
return $dest;
153+
}
154+
155+
/**
156+
* The table provides a representation of barcode patterns
157+
* for each character in the ASCII set. from ASCII0-127
158+
* Each barcode starts with "_" - ASCII95 0x5F,
159+
* and ends with "z" - ASCII122 0xFA.
160+
* More information about Telepen symbology is available from
161+
* https://v4l237.n3cdn1.secureserver.net/wp-content/uploads/2022/05/Barcode-Symbology-information-and-History.pdf
162+
*/
163+
private function createTelepenConversionTable()
164+
{
165+
$this->telepen_lookup_table = [
166+
"1111111111111111", "1131313111", "33313111", "1111313131",
167+
"3111313111", "11333131", "13133131", "111111313111",
168+
"31333111", "1131113131", "33113131", "1111333111",
169+
"3111113131", "1113133111", "1311133111", "111111113131",
170+
"3131113111", "11313331", "333331", "111131113111",
171+
"31113331", "1133113111", "1313113111", "1111113331",
172+
"31131331", "113111113111", "3311113111", "1111131331",
173+
"311111113111", "1113111331", "1311111331", "11111111113111",
174+
"31313311", "1131311131", "33311131", "1111313311",
175+
"3111311131", "11333311", "13133311", "111111311131",
176+
"31331131", "1131113311", "33113311", "1111331131",
177+
"3111113311", "1113131131", "1311131131", "111111113311",
178+
"3131111131", "1131131311", "33131311", "111131111131",
179+
"3111131311", "1133111131", "1313111131", "111111131311",
180+
"3113111311", "113111111131", "3311111131", "111113111311",
181+
"311111111131", "111311111311", "131111111311", "11111111111131",
182+
"3131311111", "11313133", "333133", "111131311111",
183+
"31113133", "1133311111", "1313311111", "1111113133",
184+
"313333", "113111311111", "3311311111", "11113333",
185+
"311111311111", "11131333", "13111333", "11111111311111",
186+
"31311133", "1131331111", "33331111", "1111311133",
187+
"3111331111", "11331133", "13131133", "111111331111",
188+
"3113131111", "1131111133", "33111133", "111113131111",
189+
"3111111133", "111311131111", "131111131111", "111111111133",
190+
"31311313", "113131111111", "3331111111", "1111311313",
191+
"311131111111", "11331313", "13131313", "11111131111111",
192+
"3133111111", "1131111313", "33111313", "111133111111",
193+
"3111111313", "111313111111", "131113111111", "111111111313",
194+
"313111111111", "1131131113", "33131113", "11113111111111",
195+
"3111131113", "113311111111", "131311111111", "111111131113",
196+
"3113111113", "11311111111111", "331111111111", "111113111113",
197+
"31111111111111", "111311111113", "131111111113"
198+
];
199+
}
200+
}

tests/TypesTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,20 @@ public function test_generator_can_generate_pharma_code_2_tracks_barcode()
235235

236236
$this->assertGreaterThan(100, strlen($result));
237237
}
238+
239+
public function test_generator_can_generate_telepen_alpha_barcode()
240+
{
241+
$generator = new Picqer\Barcode\BarcodeGeneratorSVG();
242+
$result = $generator->getBarcode('1234567890ASCD', $generator::TYPE_TELEPEN_ALPHA);
243+
244+
$this->assertGreaterThan(100, strlen($result));
245+
}
246+
247+
public function test_generator_can_generate_telepen_numeric_barcode()
248+
{
249+
$generator = new Picqer\Barcode\BarcodeGeneratorSVG();
250+
$result = $generator->getBarcode('1234567890', $generator::TYPE_TELEPEN_NUMERIC);
251+
252+
$this->assertGreaterThan(100, strlen($result));
253+
}
238254
}

tests/VerifiedBarcodeTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class VerifiedBarcodeTest extends TestCase
4444
['type' => BarcodeGenerator::TYPE_CODE_11, 'barcodes' => ['123456789']],
4545
['type' => BarcodeGenerator::TYPE_PHARMA_CODE, 'barcodes' => ['123456789']],
4646
['type' => BarcodeGenerator::TYPE_PHARMA_CODE_TWO_TRACKS, 'barcodes' => ['123456789']],
47+
['type' => BarcodeGenerator::TYPE_TELEPEN_ALPHA, 'barcodes' => ['1234567890ASCD']],
48+
['type' => BarcodeGenerator::TYPE_TELEPEN_NUMERIC, 'barcodes' => ['1234567890']]
4749
];
4850

4951
public function testAllSupportedBarcodeTypes()
Lines changed: 95 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)