A task is to generate GS1 DataMatrix code (later in text - GS1 DM) in python using any suitable library/module. The issue is how to include leading FNC1 functional symbol when generating a DM code. GS1 DM is being used widely in serialization and track & trace area, e.g. for medicines serialization. GS1 DataMatrix is a normal DataMatrix (later in text - DM) but with the leading FNC1 symbol that lets the scanning device identify that this is a GS1 DataMatrix and not a usual DataMatrix.
Examples of user data to be included into GS1 DataMatrix are:
01048123231123452172KAVEK5HqOQW{GS}91KZF0{GS}92endxMQYZAymmN9rTCsWwm4gcU1oihAn726B86OLBnh0=
01048702231123452179LimKz7sKYyF{GS}91KZF0{GS}92VllFTqxoBYKxfjjd/uLpXe4Sbm3+3foXpen0DLGIgWQ=
These examples are unique identifiers of individual medicine packages + so called crypto-key and crypto-code. Such identifiers are used in serialization in CIS countries like Kazakhstan, Uzbekistan and Belarus, Russia. {GS1} is a group separator symbol with ASCII 29 code. This is not relevant for the task itself, just to give you some context.
**What I was able to do: **generate DataMatrix code (but not a GS1 DataMatrix code) with required user content using python module pylibdmtx.
**What is an issue: **I failed to find any solution to include the FNC1 symbol into the DataMatrix code to “convert” this code to GS1 DataMatrix when I generate the DM using the mentioned python library.
**My findings and questions so far **(please correct my if I’m wrong):
- FNC1 symbol is not included into either the ASCII codes table or into UTF-8 or ASCII 256 symbols table (whatever ASCII 256 means as there is no such specific encoding). As I understand, it’s not included into any existing encoding as FNC1: a) doesn’t have a graphical representation and b) is not intended to be a part of any encoding as it’s a functional “symbol” which is a part of GS1 DM standard itself. Whatever this means.
- In many sources / links I’ve found information that FNC1 has a code 232 in “extended ASCII table”. But, as I understand, this is not true as FNC1 is not included into any encoding table for the reasons I’ve given in the previous point. E.g. for UTF-8 or other encodings, the symbol with code 232 is “è” or some other symbol which is definitely not the FNC1 as FNC1 can’t have any graphical representation.
- If you use some online tool for DM code generation (like this https://barcode.tec-it.com/en/GS1DataMatrix, try to put there
0104607045191708211NSBA931BA63A\F91EE07\F92ooqoeKYx9KuYMj2ZZudhhFVAM5ip2M/R3bo5+Je4QE4=
where \F is to include GS group separator symbol. Leading FNC1 is added automatically by tool), then FNC1 is included into DataMatrix and other code formats by using escape sequences, e.g. in the form of {FNC1}. But it doesn’t answer the question on how to include FNC1 into DM when I generate it myself by python code. Some tools include FNC1 as a leading symbol automatically when you select GS1 DataMatrix format. - When you generate DM, a code FNC1 should be included directly into DM by library/module itself. Library should include FNC1 at the moment of DM code “rendering” / drawing. But I haven’t found any library for python that generates an exactly GS1 DM code, but only DM code.
- Existing libraries for DataMatrix generation for python are mostly very purely documented, especially on usage details or examples. Or even don’t have any documentation at all.
- When I scan a DM code (which has a GS1 DM format) on a different real physical packs of medicines by any scanning app on a mobile phone, I always get the {GS1} symbol (group separator symbol with code 29 in ASCII table) as a leading symbol for data encoded into DM. But, as I understand, FNC1 as a leading symbol should be skipped by a scanner and serves purely for GS1 DM format identification. So I’m wondering whether this is a wrong processing of GS1 DM on mobile app side or a wrong GS1 DM building by manufacturers (much less likely). Or also maybe I understand the issue in a wrong way.
- I haven't found any suitable answer on stack overflow although there are a bunch of them on the same issue
Here is my complete code:
from PIL import Image
from pylibdmtx.pylibdmtx import encode
from datetime import datetime
import os
# FNC1 = chr(232)
# FNC1 = FNC1.encode("utf-8")
# FNC1 = bytes.fromhex("E8").decode('utf-8')
GS1 = chr(29)
DMs = []
class Dm:
def __init__(self, gtin, sn, key, code):
self.gtin = gtin
self.sn = sn
self.key = key
self.code = code
self.dmImg = self.generateDmImg()
def strToUTF8(self, str):
return str.encode('utf_8')
def __str__(self):
return self.getDmContent()
def getSgtin(self):
return "01" + self.gtin + "21" + self.sn
def getDmContent(self):
str = "01" + self.gtin + "21" + self.sn + GS1 + "91" + self.key + GS1 + "92" + self.code
# str = FNC1 + "01" + self.gtin + "21" + self.sn + GS1 + "91" + self.key + GS1 + "92" + self.code
return str
def generateDmImg(self):
dmContent = self.getDmContent()
encoded = encode(dmContent)
dmImg = Image.frombytes('RGB', (encoded.width, encoded.height), encoded.pixels)
return dmImg
def saveDmImgToFile(self, filename):
self.dmImg.save(filename)
def read_file(filename):
# read text strings from the file
with open(filename) as f:
text_strings = f.readlines()
return text_strings
def getListOfDMs(text_strings):
DMs = []
for text in text_strings:
if (len(text.strip()) > 0):
text_split = text.split(";")
dm = Dm(text_split[0].strip(), text_split[1].strip(), text_split[2].strip(), text_split[3].strip())
DMs.append(dm)
return DMs
# ============ MAIN PROGRAM =================
# res = read_file("src_files/source1.csv")
res = read_file("source1.csv")
DMs = getListOfDMs(res)
# for dm in DMs:
# print(dm)
# generate a PNG image for each text string
i = 1
for dm in DMs:
now = datetime.now()
timestamp = now.strftime("%d_%m_%Y_%H_%M_%S")
dm.saveDmImgToFile(os.path.join("results", dm.sn + "_" + timestamp + "_" + str(i) + ".png"))
i += 1
# dm = DMs[0]
# now = datetime.now()
# timestamp = now.strftime("%d_%m_%Y_%H_%M_%S")
# dm.saveDmImgToFile(os.path.join("results", dm.sn + "_" + timestamp + "_" + str(i) + ".png"))
Here is an text example of a source1.csv file (4 columns are: GTIN, SN, Crypto-Key, Crypto-Code):
04812323112345;72KAVEK5HqOQW;KZF0;endxMQYZAymmN9rTCsWwm4gcU1oihAn726B86OLBnh0=
04812323112345;79LimKz7sKYyF;KZF0;VllFTqxoBYKxfjjd/uLpXe4Sbm3+3foXpen0DLGIgWQ=
04812323112345;7GaGJ1jCrfxID;KZF0;c2l2SH8LOYmbZK95S8ENwhCQXjmwKWd5BgfA+Pwv6nE=
04812323112345;7HKrat9IGg2wZ;KZF0;cmt5NuKP23flUwDyJolFj7NMVR+K7vALjEO3ktFToGY=
AIM Code type must be decoded as "]d2" for GS1 DataMatrix by the scanner. "]d1" is the DataMatrix. In order to do that you need to have the first character as FNC1, but most libraries do not encode it correctly to identify the code as GS1 for that purpose. treepoem supports GS1 DataMatrix and adds FNC1 and GS characters automatically where needed.