Coverage for gs1_barcode_enginer_wrapper.py: 93%
150 statements
« prev ^ index » next coverage.py v6.4.1, created at 2022-07-12 15:52 +1000
« prev ^ index » next coverage.py v6.4.1, created at 2022-07-12 15:52 +1000
1# use wrapper around https://github.com/gs1/gs1-barcode-engine
2# cloned to gs1-barcode-engine
3# TODO(EDWARD): fix pathing
4# TODO(EDWARD): packaging
6import ctypes
7from dataclasses import dataclass
8from typing import Optional
11class Gs1GeneratorError(Exception):
12 pass
15# A. Create library
16c_library = ctypes.CDLL("build_artifacts/libgs1encoders.so")
19# define expected arguments and return types
21ctx_pointer_type = ctypes.c_void_p # hack
24c_library.gs1_encoder_init.restype = ctx_pointer_type
25c_library.gs1_encoder_init.argtypes = []
27c_library.gs1_encoder_setFormat.restype = ctypes.c_bool
28c_library.gs1_encoder_setFormat.argtypes = [ctx_pointer_type, ctypes.c_int]
30c_library.gs1_encoder_setOutFile.restype = ctypes.c_bool
31c_library.gs1_encoder_setOutFile.argtypes = [ctx_pointer_type, ctypes.c_char_p]
33c_library.gs1_encoder_setSym.restype = ctypes.c_bool
34c_library.gs1_encoder_setSym.argtypes = [ctx_pointer_type, ctypes.c_int]
36c_library.gs1_encoder_setDataStr.restype = ctypes.c_bool
37c_library.gs1_encoder_setDataStr.argtypes = [ctx_pointer_type, ctypes.c_char_p]
39c_library.gs1_encoder_setAIdataStr.restype = ctypes.c_bool
40c_library.gs1_encoder_setAIdataStr.argtypes = [ctx_pointer_type, ctypes.c_char_p]
43# gs1_encoder_setPixMult (gs1_encoder *ctx, int pixMult)
44c_library.gs1_encoder_setPixMult.restype = ctypes.c_bool
45c_library.gs1_encoder_setPixMult.argtypes = [ctx_pointer_type, ctypes.c_int]
48# gs1_encoder_setDeviceResolution (gs1_encoder *ctx, double resolution)
49c_library.gs1_encoder_setDeviceResolution.restype = ctypes.c_bool
50c_library.gs1_encoder_setDeviceResolution.argtypes = [ctx_pointer_type, ctypes.c_double]
53# gs1_encoder_setXdimension (gs1_encoder *ctx, double min, double target, double max)
54c_library.gs1_encoder_setXdimension.restype = ctypes.c_bool
55c_library.gs1_encoder_setXdimension.argtypes = [
56 ctx_pointer_type,
57 ctypes.c_double,
58 ctypes.c_double,
59 ctypes.c_double,
60]
63# gs1_encoder_setXundercut (gs1_encoder *ctx, int Xundercut)
64c_library.gs1_encoder_setXundercut.restype = ctypes.c_bool
65c_library.gs1_encoder_setXundercut.argtypes = [ctx_pointer_type, ctypes.c_int]
68# gs1_encoder_setYundercut (gs1_encoder *ctx, int Yundercut)
69c_library.gs1_encoder_setYundercut.restype = ctypes.c_bool
70c_library.gs1_encoder_setYundercut.argtypes = [ctx_pointer_type, ctypes.c_int]
73# gs1_encoder_setDmRows (gs1_encoder *ctx, int rows)
74c_library.gs1_encoder_setDmRows.restype = ctypes.c_bool
75c_library.gs1_encoder_setDmRows.argtypes = [ctx_pointer_type, ctypes.c_int]
78# gs1_encoder_setDmColumns (gs1_encoder *ctx, int columns)
79c_library.gs1_encoder_setDmColumns.restype = ctypes.c_bool
80c_library.gs1_encoder_setDmColumns.argtypes = [ctx_pointer_type, ctypes.c_int]
83c_library.gs1_encoder_encode.restype = ctypes.c_bool
84c_library.gs1_encoder_encode.argtypes = [ctx_pointer_type]
86c_library.gs1_encoder_getBufferSize.restype = ctypes.c_size_t
87c_library.gs1_encoder_getBufferSize.argtypes = [ctx_pointer_type]
89c_library.gs1_encoder_copyOutputBuffer.restype = ctypes.c_size_t
90c_library.gs1_encoder_copyOutputBuffer.argtypes = [
91 ctx_pointer_type,
92 ctypes.c_void_p,
93 ctypes.c_size_t,
94]
97c_library.gs1_encoder_getBuffer.restype = ctypes.c_size_t
98c_library.gs1_encoder_getBuffer.argtypes = [ctx_pointer_type]
101# c_library.gs1_encoder_free.restype=ctypes.
102c_library.gs1_encoder_free.argtypes = [ctx_pointer_type]
104c_library.gs1_encoder_getErrMsg.restype = ctypes.c_char_p
105c_library.gs1_encoder_getErrMsg.argtypes = [ctx_pointer_type]
108# TODO: handle errors properly
111@dataclass
112class ScalingParams:
113 pass
116@dataclass
117class PixelScaling(ScalingParams):
118 pix_mult: float
121@dataclass
122class DeviceDotScaling(ScalingParams):
123 resolution: float
124 min_x_dim: float
125 target_x_dim: float
126 max_x_dim: float
129def scaling_params_factory(args: dict) -> ScalingParams:
130 if "pix_mult" in args:
131 cls = PixelScaling
132 else:
133 cls = DeviceDotScaling
135 args["min_x_dim"] = (
136 args["min_x_dim"] if args.get("min_x_dim") is not None else 0
137 )
138 args["max_x_dim"] = (
139 args["max_x_dim"] if args.get("max_x_dim") is not None else 0
140 )
141 return cls(**args)
144@dataclass
145class DotScalingParams:
146 resolution: float
147 target_x_dim: float
148 min_x_dim: Optional[float] = None
149 max_x_dim: Optional[float] = None
152# TODO read these enums from object file
153gs1_encoder_sNONE = -1 # ///< None defined
154gs1_encoder_sDataBarOmni = 0 # ///< GS1 DataBar Omnidirectional
155gs1_encoder_sDataBarTruncated = 1 # ///< GS1 DataBar Truncated
156gs1_encoder_sDataBarStacked = 2 # ///< GS1 DataBar Stacked
157gs1_encoder_sDataBarStackedOmni = 3 # ///< GS1 DataBar Stacked Omnidirectional
158gs1_encoder_sDataBarLimited = 4 # ///< GS1 DataBar Limited
159gs1_encoder_sDataBarExpanded = 5 # ///< GS1 DataBar Expanded (Stacked)
160gs1_encoder_sUPCA = 6 # ///< UPC-A
161gs1_encoder_sUPCE = 7 # ///< UPC-E
162gs1_encoder_sEAN13 = 8 # ///< EAN-13
163gs1_encoder_sEAN8 = 9 # ///< EAN-8
164gs1_encoder_sGS1_128_CCA = 10 # ///< GS1-128 with CC-A or CC-B
165gs1_encoder_sGS1_128_CCC = 11 # ///< GS1-128 with CC-C
166gs1_encoder_sQR = 12 # ///< (GS1) QR Code
167gs1_encoder_sDM = 13 # ///< (GS1) Data Matrix
168gs1_encoder_sNUMSYMS = 14 # ///< Value is the number of symbologies
171# TODO read these enums from object file
172gs1_encoder_dBMP = 0
173gs1_encoder_dTIF = 1
174gs1_encoder_dRAW = 2
177def error_things(ctx, result):
179 if result: 179 ↛ 180line 179 didn't jump to line 180, because the condition on line 179 was never true
180 return
182 msg = c_library.gs1_encoder_getErrMsg(ctx)
183 raise Gs1GeneratorError(msg.decode("ascii"))
186def generate_gs1_datamatrix(
187 data: str,
188 x_undercut: Optional[float] = None,
189 y_undercut: Optional[float] = None,
190 dm_rows: Optional[int] = None,
191 dm_cols: Optional[int] = None,
192 scaling: Optional[dict] = None,
193) -> bytes:
195 if scaling:
196 scalingparams = scaling_params_factory(scaling)
197 else:
198 scalingparams = None
200 ctx = c_library.gs1_encoder_init(None)
202 try:
203 result = c_library.gs1_encoder_setFormat(ctx, gs1_encoder_dBMP)
204 if result is False: 204 ↛ 205line 204 didn't jump to line 205, because the condition on line 204 was never true
205 error_things(ctx, result)
207 result = c_library.gs1_encoder_setOutFile(
208 ctx, b""
209 ) # output to buffer, not a file
210 if result is False: 210 ↛ 211line 210 didn't jump to line 211, because the condition on line 210 was never true
211 error_things(ctx, result)
213 result = c_library.gs1_encoder_setSym(ctx, gs1_encoder_sDM)
214 if result is False: 214 ↛ 215line 214 didn't jump to line 215, because the condition on line 214 was never true
215 error_things(ctx, result)
217 result = c_library.gs1_encoder_setAIdataStr(ctx, data.encode("ascii"))
218 if result is False:
219 error_things(ctx, result)
221 # more configuration
223 if isinstance(scalingparams, PixelScaling):
224 # 1. Pixel-based scaling system (no real world dimensions)
225 result = c_library.gs1_encoder_setPixMult(ctx, scalingparams.pix_mult)
226 if result is False:
227 error_things(ctx, result)
228 elif isinstance(scalingparams, DeviceDotScaling):
229 # 2. Device-dot scaling system (real world dimensions)
230 # result=c_library.gs1_encoder_setDeviceResolution.argtypes=[ctx_pointer_type,ctypes.c_double]
231 # set device resolution in dots per unit. to be used with and before setXdimension
232 result = c_library.gs1_encoder_setDeviceResolution(
233 ctx, scalingparams.resolution
234 )
235 if result is False: 235 ↛ 236line 235 didn't jump to line 236, because the condition on line 235 was never true
236 error_things(ctx, result)
238 # result=c_library.gs1_encoder_setXdimension.argtypes=[ctx_pointer_type,ctypes.c_double,ctypes.c_double,ctypes.c_double]
239 result = c_library.gs1_encoder_setXdimension(
240 ctx,
241 scalingparams.min_x_dim,
242 scalingparams.target_x_dim,
243 scalingparams.max_x_dim,
244 )
245 if result is False:
246 error_things(ctx, result)
247 else:
248 if scalingparams is None: 248 ↛ 251line 248 didn't jump to line 251, because the condition on line 248 was never false
249 pass
250 else:
251 assert False
253 # calibration: set undercut
255 # result=c_library.gs1_encoder_setXundercut.argtypes=[ctx_pointer_type,ctypes.c_int]
256 if x_undercut is not None:
257 result = c_library.gs1_encoder_setXundercut(ctx, x_undercut)
258 if result is False:
259 error_things(ctx, result)
261 if y_undercut is not None:
262 # result=c_library.gs1_encoder_setYundercut.argtypes=[ctx_pointer_type,ctypes.c_int]
263 result = c_library.gs1_encoder_setYundercut(ctx, y_undercut)
264 if result is False:
265 error_things(ctx, result)
267 # cionfigure datamatrix
269 # result=c_library.gs1_encoder_setDmRows.argtypes=[ctx_pointer_type,ctypes.c_int]
271 if dm_rows is not None:
272 result = c_library.gs1_encoder_setDmRows(ctx, dm_rows)
273 if result is False:
274 error_things(ctx, result)
276 if dm_cols is not None:
277 # result=c_library.gs1_encoder_setDmColumns.argtypes=[ctx_pointer_type,ctypes.c_int]
278 result = c_library.gs1_encoder_setDmColumns(ctx, dm_cols)
279 if result is False:
280 error_things(ctx, result)
282 # do actual things
284 result = c_library.gs1_encoder_encode(ctx)
285 if result is False: 285 ↛ 286line 285 didn't jump to line 286, because the condition on line 285 was never true
286 error_things(ctx, result)
288 size = c_library.gs1_encoder_getBufferSize(ctx)
289 buffer = ctypes.create_string_buffer(size)
291 got_size = c_library.gs1_encoder_copyOutputBuffer(ctx, buffer, size)
292 assert got_size == size
294 return buffer.raw
295 finally:
296 c_library.gs1_encoder_free(ctx)
299# with open("itsaslive.bmp", "rb") as f:
300# baseline_data=f.read()
301# print()
304# things = get_bmp_data("(01)94210325403182(30)2(3922)0460(93)TQ")
305# assert things==baseline_data