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

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 

5 

6import ctypes 

7from dataclasses import dataclass 

8from typing import Optional 

9 

10 

11class Gs1GeneratorError(Exception): 

12 pass 

13 

14 

15# A. Create library 

16c_library = ctypes.CDLL("build_artifacts/libgs1encoders.so") 

17 

18 

19# define expected arguments and return types 

20 

21ctx_pointer_type = ctypes.c_void_p # hack 

22 

23 

24c_library.gs1_encoder_init.restype = ctx_pointer_type 

25c_library.gs1_encoder_init.argtypes = [] 

26 

27c_library.gs1_encoder_setFormat.restype = ctypes.c_bool 

28c_library.gs1_encoder_setFormat.argtypes = [ctx_pointer_type, ctypes.c_int] 

29 

30c_library.gs1_encoder_setOutFile.restype = ctypes.c_bool 

31c_library.gs1_encoder_setOutFile.argtypes = [ctx_pointer_type, ctypes.c_char_p] 

32 

33c_library.gs1_encoder_setSym.restype = ctypes.c_bool 

34c_library.gs1_encoder_setSym.argtypes = [ctx_pointer_type, ctypes.c_int] 

35 

36c_library.gs1_encoder_setDataStr.restype = ctypes.c_bool 

37c_library.gs1_encoder_setDataStr.argtypes = [ctx_pointer_type, ctypes.c_char_p] 

38 

39c_library.gs1_encoder_setAIdataStr.restype = ctypes.c_bool 

40c_library.gs1_encoder_setAIdataStr.argtypes = [ctx_pointer_type, ctypes.c_char_p] 

41 

42 

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] 

46 

47 

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] 

51 

52 

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] 

61 

62 

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] 

66 

67 

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] 

71 

72 

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] 

76 

77 

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] 

81 

82 

83c_library.gs1_encoder_encode.restype = ctypes.c_bool 

84c_library.gs1_encoder_encode.argtypes = [ctx_pointer_type] 

85 

86c_library.gs1_encoder_getBufferSize.restype = ctypes.c_size_t 

87c_library.gs1_encoder_getBufferSize.argtypes = [ctx_pointer_type] 

88 

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] 

95 

96 

97c_library.gs1_encoder_getBuffer.restype = ctypes.c_size_t 

98c_library.gs1_encoder_getBuffer.argtypes = [ctx_pointer_type] 

99 

100 

101# c_library.gs1_encoder_free.restype=ctypes. 

102c_library.gs1_encoder_free.argtypes = [ctx_pointer_type] 

103 

104c_library.gs1_encoder_getErrMsg.restype = ctypes.c_char_p 

105c_library.gs1_encoder_getErrMsg.argtypes = [ctx_pointer_type] 

106 

107 

108# TODO: handle errors properly 

109 

110 

111@dataclass 

112class ScalingParams: 

113 pass 

114 

115 

116@dataclass 

117class PixelScaling(ScalingParams): 

118 pix_mult: float 

119 

120 

121@dataclass 

122class DeviceDotScaling(ScalingParams): 

123 resolution: float 

124 min_x_dim: float 

125 target_x_dim: float 

126 max_x_dim: float 

127 

128 

129def scaling_params_factory(args: dict) -> ScalingParams: 

130 if "pix_mult" in args: 

131 cls = PixelScaling 

132 else: 

133 cls = DeviceDotScaling 

134 

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) 

142 

143 

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 

150 

151 

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 

169 

170 

171# TODO read these enums from object file 

172gs1_encoder_dBMP = 0 

173gs1_encoder_dTIF = 1 

174gs1_encoder_dRAW = 2 

175 

176 

177def error_things(ctx, result): 

178 

179 if result: 179 ↛ 180line 179 didn't jump to line 180, because the condition on line 179 was never true

180 return 

181 

182 msg = c_library.gs1_encoder_getErrMsg(ctx) 

183 raise Gs1GeneratorError(msg.decode("ascii")) 

184 

185 

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: 

194 

195 if scaling: 

196 scalingparams = scaling_params_factory(scaling) 

197 else: 

198 scalingparams = None 

199 

200 ctx = c_library.gs1_encoder_init(None) 

201 

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) 

206 

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) 

212 

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) 

216 

217 result = c_library.gs1_encoder_setAIdataStr(ctx, data.encode("ascii")) 

218 if result is False: 

219 error_things(ctx, result) 

220 

221 # more configuration 

222 

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) 

237 

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 

252 

253 # calibration: set undercut 

254 

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) 

260 

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) 

266 

267 # cionfigure datamatrix 

268 

269 # result=c_library.gs1_encoder_setDmRows.argtypes=[ctx_pointer_type,ctypes.c_int] 

270 

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) 

275 

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) 

281 

282 # do actual things 

283 

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) 

287 

288 size = c_library.gs1_encoder_getBufferSize(ctx) 

289 buffer = ctypes.create_string_buffer(size) 

290 

291 got_size = c_library.gs1_encoder_copyOutputBuffer(ctx, buffer, size) 

292 assert got_size == size 

293 

294 return buffer.raw 

295 finally: 

296 c_library.gs1_encoder_free(ctx) 

297 

298 

299# with open("itsaslive.bmp", "rb") as f: 

300# baseline_data=f.read() 

301# print() 

302 

303 

304# things = get_bmp_data("(01)94210325403182(30)2(3922)0460(93)TQ") 

305# assert things==baseline_data