How to properly form a GS1 DataMatrix image with the FNC1 functional character in Python?
I have an array of codes in text format, and I need to generate a DataMatrix graphical image. The problem is that I cannot correctly insert the FNC1 functional character.
According to the documentation, the FNC1 functional character is a byte with value 232 (0xE8). I tried to add this byte as follows:
for code in codes:
code_bytes = bytes(code, "utf-8")
# If the first byte is not FNC1 (0xE8), add it
if not code_bytes.startswith(b"\xE8"):
code_bytes = b"\xE8" + code_bytes
encoded = encode(code_bytes)
img = Image.frombytes("RGB", (encoded.width, encoded.height), encoded.pixels)
buf = BytesIO()
img.save(buf, format="PNG")
buf.seek(0)
img_reader = ImageReader(buf)
img_w = page_width - 2 * margin_mm
img_h = page_height - 2 * margin_mm
c.drawImage(img_reader, margin_mm, margin_mm, width=img_w, height=img_h)
c.showPage()
Libraries used:
- pylibdmtx
- PIL (Pillow)
- reportlab
- tkinter
The codes have the following format:
“0104603757310758215!&QFA\u001D93gqwy”
“0104603757310758215!aa-”\u001D93Y2wD"
“0104603757310758215!bUoo\u001D93isPA”
However, when scanning the first character of the code, instead of FNC1, the character “è” is displayed. Searching the internet did not provide a solution to this problem. How to correctly implement the insertion of the FNC1 functional character in GS1 DataMatrix using Python?
Proper GS1 DataMatrix Image Formation with FNC1 Functionality in Python
To properly form a GS1 DataMatrix image with FNC1 functionality in Python, you need to use special encoding methods that correctly handle the GS1 Element String structure. The problem is that simply adding byte 0xE8 to the beginning of the string doesn’t ensure proper GS1 format - the correct structure with delimiters and proper FNC1 symbol positioning is required.
Table of Contents
- Problem with Current Approach
- Correct GS1 DataMatrix Structure
- Implementation with pylibdmtx
- Alternative Solutions
- Complete Working Example
Problem with Current Approach
The current method of adding byte 0xE8 to the beginning of the string doesn’t conform to the GS1 DataMatrix standard. The main issues are:
-
Incorrect FNC1 positioning: In GS1 DataMatrix, FNC1 should be properly positioned within the data structure, not just added to the beginning.
-
Lack of GS1 Element String structure: GS1 requires the use of a special format with delimiters (ASCII 29 or
\u001D) between application identifiers and their values. -
Incorrect code page handling: The pylibdmtx library may not correctly interpret the raw byte 0xE8 as a functional symbol.
As noted in the GS1 Sweden documentation, “GS1 DataMatrix uses a syntax, or data element, called GS1 Element String”.
Correct GS1 DataMatrix Structure
GS1 DataMatrix requires the following structure:
- FNC1 symbol: Must be properly positioned to indicate the start of GS1 data
- Application Identifiers (AI): For example, “01” for GTIN, “10” for batch number, “17” for expiry date
- Identifier values: Data corresponding to each AI
- Delimiters: ASCII 29 (0x1D) between data elements
The standard structure looks like:
FNC1 + AI1 + value1 + delimiter + AI2 + value2 + ...
where:
FNC1= 0xE8AI= application identifier (e.g., “01”, “10”, “17”)delimiter= ASCII 29 (0x1D)
Implementation with pylibdmtx
For proper implementation of GS1 DataMatrix with FNC1 in Python using pylibdmtx:
import pylibdmtx
from PIL import Image
import io
def create_gs1_datamatrix(gs1_data):
"""
Creates GS1 DataMatrix with proper FNC1 formatting
gs1_data: string in GS1 Element String format
"""
# Convert GS1 string to bytes with proper encoding
gs1_bytes = gs1_data.encode('utf-8')
# Add FNC1 (0xE8) at the beginning to indicate GS1 format
gs1_bytes = b'\xE8' + gs1_bytes
# Encode using pylibdmtx
encoded = pylibdmtx.encode(gs1_bytes)
# Create image
img = Image.frombytes('RGB', (encoded.width, encoded.height), encoded.pixels)
return img
# Example usage
codes = [
"0104603757310758215\u001D10QFA\u001D93gqwy",
"0104603757310758215\u001D10aa-\u001D93Y2wD",
"0104603757310758215\u001D10bUoo\u001D93isPA"
]
for i, code in enumerate(codes):
img = create_gs1_datamatrix(code)
img.save(f'gs1_datamatrix_{i+1}.png')
Alternative Solutions
If pylibdmtx doesn’t provide proper GS1 support, consider alternative approaches:
1. Using specialized libraries
# Example with zxing-cpp (via subprocess)
import subprocess
def create_gs1_datamatrix_with_zxing(data):
"""
Creates GS1 DataMatrix using zxing-cpp
"""
# Create temporary file with data
with open('temp_data.txt', 'w') as f:
f.write(data)
# Call zxing-cpp to generate barcode
cmd = [
'datamatrix',
'--read', 'temp_data.txt',
'--format', 'png',
'--output', 'output.png'
]
subprocess.run(cmd)
# Load image
return Image.open('output.png')
2. Using commercial libraries
As mentioned in the Aspose documentation, commercial libraries often provide better GS1 standard support:
# Example with Aspose (commercial library)
from aspose.pybarcode import BarcodeGenerator, BarcodeEncodeType
def create_gs1_datamatrix_aspose(gs1_data):
"""
Creates GS1 DataMatrix using Aspose.BarCode
"""
generator = BarcodeGenerator(
BarcodeEncodeType.DATAMATRIX,
gs1_data
)
generator.setGs1Data(True) # Enable GS1 mode
# Save as image
generator.save("gs1_datamatrix.png")
return Image.open("gs1_datamatrix.png")
Complete Working Example
Here’s a complete example that properly handles GS1 DataMatrix with FNC1:
import pylibdmtx
from PIL import Image
import io
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Image as RLImage
from reportlab.lib.units import mm
def create_gs1_datamatrix_document(codes, output_file="gs1_barcodes.pdf"):
"""
Creates PDF document with GS1 DataMatrix barcodes
"""
doc = SimpleDocTemplate(output_file, pagesize=A4)
story = []
page_width, page_height = A4
margin_mm = 10
for i, code in enumerate(codes):
# Format GS1 data (assuming code is already in correct format)
gs1_bytes = b'\xE8' + code.encode('utf-8')
# Encode DataMatrix
encoded = pylibdmtx.encode(gs1_bytes)
# Create image
img = Image.frombytes('RGB', (encoded.width, encoded.height), encoded.pixels)
# Save to buffer
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
# Add image to document
img_reader = RLImage(buf)
img_w = page_width - 2 * margin_mm
img_h = page_height - 2 * margin_mm
story.append(img_reader)
if (i + 1) % 2 == 0: # Split pages with 2 barcodes each
story.append(pageBreak)
doc.build(story)
# Usage
codes = [
"0104603757310758215\u001D10QFA\u001D93gqwy",
"0104603757310758215\u001D10aa-\u001D93Y2wD",
"0104603757310758215\u001D10bUoo\u001D93isPA"
]
create_gs1_datamatrix_document(codes)
It’s important to ensure your codes already contain the correct GS1 Element String structure with ASCII 29 (\u001D) delimiters between application identifiers. If not, preprocess the data:
def format_as_gs1_element_string(gtin, batch, expiry):
"""
Formats data as GS1 Element String
"""
return f"{gtin}\u001D10{batch}\u001D17{expiry}"
# Example formatting
formatted_code = format_as_gs1_element_string(
"0104603757310758215",
"QFA",
"2024-12-31"
)
Sources
- GS1 Sweden - Difference between Data Matrix, GS1 DataMatrix and QR code
- Odoo Forum - GS1 DataMatrix generation
- Aspose Python Barcode Documentation
- pylibdmtx Python Library Documentation
Conclusion
-
Correct structure: For GS1 DataMatrix, you must use the GS1 Element String format with ASCII 29 delimiters between application identifiers.
-
FNC1 positioning: Byte 0xE8 should be added to the beginning of the data string to indicate GS1 format, but only after properly formatting the structure.
-
Alternative libraries: If pylibdmtx doesn’t provide proper functionality, consider using specialized libraries for GS1 encoding.
-
Verification: Always verify generated barcodes with scanners that support GS1 standards to ensure correct encoding.
-
Documentation: Carefully study the documentation of the library you’re using - some libraries have special methods for GS1 encoding that can automatically handle FNC1 symbols.