Skip to content

Commit

Permalink
Fix invalid UPC-E in some cases (#66)
Browse files Browse the repository at this point in the history
* Fix invalid UPC-E in some cases

* Fix upcaToUpce() conversion issue
  • Loading branch information
shinbin authored Jan 5, 2024
1 parent 7197206 commit 0be4a76
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 72 deletions.
4 changes: 4 additions & 0 deletions barcode/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 2.2.6

- Fix invalid UPC-E in some cases [shinbin]

## 2.2.5

- Fix EAN2 incorrect encoding issue [shinbin]
Expand Down
154 changes: 85 additions & 69 deletions barcode/lib/src/upce.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,55 +72,71 @@ class BarcodeUpcE extends BarcodeEan {

/// Convert an UPC-A barcode to a short version UPC-E
String upcaToUpce(String data) {
final exp = RegExp(r'^[01](\d\d+)([0-2]000[05-9])(\d*)\d$');
final match = exp.firstMatch(data);

if (match == null) {
//Basic checking of string headers and lengths.
if ( RegExp(r'^[01]\d{11}$').firstMatch(data) == null) {
throw BarcodeException('Unable to convert "$data" to $name Barcode');
}

final left = match.group(1);
final right = match.group(3);
String? last;

switch (match.group(2)) {
case '00000':
if (left!.length == 2) {
last = '0';
} else if (left.length == 3) {
last = '3';
} else if (left.length == 4) {
last = '4';
}
break;
case '10000':
last = '1';
break;
case '20000':
last = '2';
break;
case '00005':
last = '5';
break;
case '00006':
last = '6';
break;
case '00007':
last = '7';
break;
case '00008':
last = '8';
break;
case '00009':
last = '9';
break;
//Refer to https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E
//Both algorithms below are correct and have been tested.
//Algorithm 1: https://gist.github.com/corpit/8204456 (Implement in this function)
//Algorithm 2: https://www.keepautomation.com/upca/upca-to-upce-conversion.html

// Algorithm 1=>
final mc = data.substring(1,6); //manufacturer code
final pc = data.substring(6,11); //product code

if(['000', '100', '200'].contains(mc.substring(mc.length - 3)) && int.parse(pc) <= 999){
//if manufacturer_code[-3:] in ["000", "100", "200"] and int(product_code) <= 999:
// upce = manufacturer_code[:2] + product_code[-3:] + manufacturer_code[2]
return '${mc.substring(0,2)}${pc.substring(pc.length-3)}${mc[2]}';
}

if (last == null) {
else if(mc.substring(mc.length - 2) == '00' && int.parse(pc) <= 99){
//elif manufacturer_code[-2:] == '00' and int(product_code) <= 99:
// upce = manufacturer_code[:3] + product_code[-2:] + "3"
return '${mc.substring(0,3)}${pc.substring(pc.length-2)}3';
}
else if(mc.substring(mc.length - 1) == '0' && int.parse(pc) <= 9){
//elif manufacturer_code[-1] == "0" and int(product_code) <= 9:
// upce = manufacturer_code[:4] + product_code[-1] + "4"
return '${mc.substring(0,4)}${pc.substring(pc.length-1)}4';
}
else if(mc.substring(mc.length - 1) != '0' && [5, 6, 7, 8, 9].contains(int.parse(pc))){
//elif manufacturer_code[-1] != "0" and int(product_code) in [5,6,7,8,9]:
// upce = manufacturer_code + product_code[-1]
return mc + pc.substring(pc.length-1);
}
else {
throw BarcodeException('Unable to convert "$data" to $name Barcode');
}

return left! + right! + last;

// Algorithm 2=>
/*
if([0x35, 0x36, 0x37, 0x38, 0x39].contains(data.codeUnits[10]) && data.substring(6,10) == '0000' && data[5] != '0') {
//If the 11th code of UPC-A equals to 5, 6, 7, 8 or 9, the 7th to 10th code are all 0, and the 6th is not 0),
//adding the 2nd to 6th code and 11th code of UPC-A to present the 1st to 6th of UPC-E.
return data.substring(1,6) + data[10];
}
else if(data.substring(5,10) == '00000' && data[4] != '0'){
// If the 6th to 10th code are all 0, and the 5th is not 0,
// adding the 2nd to 5th code, 11th code of UPC-A and a digit 4 to present the 1st to 6th of UPC-E.
return '${data.substring(1,5)}${data[10]}4';
}
else if([0x30, 0x31, 0x32].contains(data.codeUnits[3]) && data.substring(4,8) == '0000'){
// If the 4th code is 0, 1, or 2, and the 5th to 8th code are all 0,
// adding the 2nd, 3rd, 9th, 10th, 11th, and 4th code of UPC-A to present the 1st to 6th of UPC-E.
return '${data.substring(1,3)}${data.substring(8,11)}${data[3]}';
}
else if([0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39].contains(data.codeUnits[3]) && data.substring(4,9) == '00000'){
//If the 4th code is 3, 4, 5, 6, 7, 8, 9, and the 5th to 9th code are all 0,
//adding the 2nd, 3rd, 4th, 10th, 11th code of UPC-A and a digit 3 to present the 1st to 6th of UPC-E.
return '${data.substring(1,4)}${data.substring(9,11)}3';
}
else {
throw BarcodeException('Unable to convert "$data" to $name Barcode');
}
*/
}

/// Convert a short version UPC-E barcode to a full length UPC-A
Expand Down Expand Up @@ -232,12 +248,12 @@ class BarcodeUpcE extends BarcodeEan {

@override
double marginLeft(
bool drawText,
double width,
double height,
double fontHeight,
double textPadding,
) {
bool drawText,
double width,
double height,
double fontHeight,
double textPadding,
) {
if (!drawText) {
return 0;
}
Expand All @@ -247,12 +263,12 @@ class BarcodeUpcE extends BarcodeEan {

@override
double marginRight(
bool drawText,
double width,
double height,
double fontHeight,
double textPadding,
) {
bool drawText,
double width,
double height,
double fontHeight,
double textPadding,
) {
if (!drawText) {
return 0;
}
Expand All @@ -262,14 +278,14 @@ class BarcodeUpcE extends BarcodeEan {

@override
double getHeight(
int index,
int count,
double width,
double height,
double fontHeight,
double textPadding,
bool drawText,
) {
int index,
int count,
double width,
double height,
double fontHeight,
double textPadding,
bool drawText,
) {
if (!drawText) {
return super.getHeight(
index,
Expand All @@ -293,13 +309,13 @@ class BarcodeUpcE extends BarcodeEan {

@override
Iterable<BarcodeElement> makeText(
String data,
double width,
double height,
double fontHeight,
double textPadding,
double lineWidth,
) sync* {
String data,
double width,
double height,
double fontHeight,
double textPadding,
double lineWidth,
) sync* {
if (data.length <= 8) {
// Try to convert UPC-E to UPC-A
data = upceToUpca(data);
Expand Down
2 changes: 1 addition & 1 deletion barcode/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: >-
homepage: https://github.com/DavBfr/dart_barcode/tree/master/barcode
repository: https://github.com/DavBfr/dart_barcode
issue_tracker: https://github.com/DavBfr/dart_barcode/issues
version: 2.2.5
version: 2.2.6

environment:
sdk: ">=2.12.0 <4.0.0"
Expand Down
2 changes: 1 addition & 1 deletion barcode/test/ean_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,6 @@ void main() {
throw Exception('upce is not a BarcodeEan');
}

expect(upce.normalize('1'), equals('010000000009'));
expect(upce.normalize('1'), equals('01000009'));
});
}
60 changes: 59 additions & 1 deletion barcode/test/upce_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,16 @@ void main() {
expect(bc.upcaToUpce('012100003454'), equals('123451'));
expect(bc.upcaToUpce('012200003453'), equals('123452'));
expect(bc.upcaToUpce('012300000451'), equals('123453'));
expect(bc.upcaToUpce('012340000053'), equals('123405'));
expect(bc.upcaToUpce('012340000053'), equals('123454'));
expect(bc.upcaToUpce('012345000058'), equals('123455'));
expect(bc.upcaToUpce('012345000065'), equals('123456'));
expect(bc.upcaToUpce('012345000072'), equals('123457'));
expect(bc.upcaToUpce('012345000089'), equals('123458'));
expect(bc.upcaToUpce('012345000096'), equals('123459'));
expect(bc.upcaToUpce('000010000052'), equals('000154'));
expect(bc.upcaToUpce('010200000809'), equals('100802'));
}
//For output data, please refer to https://barcodeqrcode.com/convert-upc-a-to-upc-e/
});

test('Barcode UPC-E', () {
Expand All @@ -63,4 +66,59 @@ void main() {
expect(bc.isValid('11234593'), isTrue);
expect(bc.isValid('123456789'), isFalse);
});

test('Barcode UPC-E normalize (fallback)', () {
final bc = Barcode.upcE( fallback: true);
if (bc is BarcodeUpcE) {
expect(bc.normalize('18740000015'), equals('18741538'));
expect(bc.normalize('48347295752'), equals('483472957520'));
expect(bc.normalize('555555'), equals('05555550'));
expect(bc.normalize('212345678992'), equals('212345678992'));
expect(bc.normalize('014233365553'), equals('014233365553'));
}
});

test('Barcode UPC-E normalize', () {
final bc = Barcode.upcE();
if (bc is BarcodeUpcE) {
expect(bc.normalize('01008029'), equals('01008029'));
expect(bc.normalize('0100802'), equals('01008029'));
expect(bc.normalize('100802'), equals('01008029'));
expect(bc.normalize('1'), equals('01000009'));

expect(bc.normalize('100902'), equals('01009028'));
expect(bc.normalize('100965'), equals('01009651'));
expect(bc.normalize('107444'), equals('01074448'));
expect(bc.normalize('000100'), equals('00001009'));

expect(bc.normalize('042100005264'), equals('04252614'));
expect(bc.normalize('020600000019'), equals('02060139'));
expect(bc.normalize('040350000077'), equals('04035747'));
expect(bc.normalize('020201000050'), equals('02020150'));
expect(bc.normalize('020204000064'), equals('02020464'));
expect(bc.normalize('023456000073'), equals('02345673'));
expect(bc.normalize('020204000088'), equals('02020488'));
expect(bc.normalize('020201000098'), equals('02020198'));
expect(bc.normalize('127200002013'), equals('12720123'));
expect(bc.normalize('042100005264'), equals('04252614'));

//For special case : input '000105' etc.
//It's converted to '000010000052' ('L-MMMM0-0000P-C').
//This UPC-E code ends in 5, but its UPC-A manufacturer code is "00010" and its last digit should not be zero.
//'000010000052' should be paired with the last code of UPC-E being 4.
//'000010000052' converted to UPC-E will be '00001542'

//'000105' does not comply with the encoding principles for conversion from UPC-A to UPC-E,
//but it can applies to the conversion rules.
//It will be normalized to a new value when converting back to UPC-E.
//'000105' , '00001052' will be normalized to '00001542' , not '00001052'.
expect(bc.normalize('000105'), equals('00001542'));
expect(bc.normalize('00001052'), equals('00001542'));
expect(bc.normalize('000154'), equals('00001542'));
expect(bc.normalize('00001542'), equals('00001542'));
expect(bc.normalize('000010000052'), equals('00001542'));
expect(bc.normalize('001054'), equals('00000514')); //another special case
}
});

}

0 comments on commit 0be4a76

Please sign in to comment.