Skip to content

Commit a98997e

Browse files
committed
Add Telepen/Telepn Numeric
1 parent 10d3507 commit a98997e

6 files changed

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

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)