Coverage for gs1_barcode_enginer_wrapper.py: 93%

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

6 import ctypes

7 from dataclasses import dataclass

8 from typing import Optional

9

10

11 class Gs1GeneratorError(Exception):

12 pass

13

14

15# A. Create library

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

17

18

19# define expected arguments and return types

20

21 ctx_pointer_type = ctypes.c_void_p # hack

22

23

24 c_library.gs1_encoder_init.restype = ctx_pointer_type

25 c_library.gs1_encoder_init.argtypes = []

26

27 c_library.gs1_encoder_setFormat.restype = ctypes.c_bool

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

29

30 c_library.gs1_encoder_setOutFile.restype = ctypes.c_bool

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

32

33 c_library.gs1_encoder_setSym.restype = ctypes.c_bool

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

35

36 c_library.gs1_encoder_setDataStr.restype = ctypes.c_bool

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

38

39 c_library.gs1_encoder_setAIdataStr.restype = ctypes.c_bool

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

41

42

43# gs1_encoder_setPixMult (gs1_encoder *ctx, int pixMult)

44 c_library.gs1_encoder_setPixMult.restype = ctypes.c_bool

45 c_library.gs1_encoder_setPixMult.argtypes = [ctx_pointer_type, ctypes.c_int]

46

47

48# gs1_encoder_setDeviceResolution (gs1_encoder *ctx, double resolution)

49 c_library.gs1_encoder_setDeviceResolution.restype = ctypes.c_bool

50 c_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)

54 c_library.gs1_encoder_setXdimension.restype = ctypes.c_bool

55 c_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)

64 c_library.gs1_encoder_setXundercut.restype = ctypes.c_bool

65 c_library.gs1_encoder_setXundercut.argtypes = [ctx_pointer_type, ctypes.c_int]

66

67

68# gs1_encoder_setYundercut (gs1_encoder *ctx, int Yundercut)

69 c_library.gs1_encoder_setYundercut.restype = ctypes.c_bool

70 c_library.gs1_encoder_setYundercut.argtypes = [ctx_pointer_type, ctypes.c_int]

71

72

73# gs1_encoder_setDmRows (gs1_encoder *ctx, int rows)

74 c_library.gs1_encoder_setDmRows.restype = ctypes.c_bool

75 c_library.gs1_encoder_setDmRows.argtypes = [ctx_pointer_type, ctypes.c_int]

76

77

78# gs1_encoder_setDmColumns (gs1_encoder *ctx, int columns)

79 c_library.gs1_encoder_setDmColumns.restype = ctypes.c_bool

80 c_library.gs1_encoder_setDmColumns.argtypes = [ctx_pointer_type, ctypes.c_int]

81

82

83 c_library.gs1_encoder_encode.restype = ctypes.c_bool

84 c_library.gs1_encoder_encode.argtypes = [ctx_pointer_type]

85

86 c_library.gs1_encoder_getBufferSize.restype = ctypes.c_size_t

87 c_library.gs1_encoder_getBufferSize.argtypes = [ctx_pointer_type]

88

89 c_library.gs1_encoder_copyOutputBuffer.restype = ctypes.c_size_t

90 c_library.gs1_encoder_copyOutputBuffer.argtypes = [

91 ctx_pointer_type,

92 ctypes.c_void_p,

93 ctypes.c_size_t,

94]

95

96

97 c_library.gs1_encoder_getBuffer.restype = ctypes.c_size_t

98 c_library.gs1_encoder_getBuffer.argtypes = [ctx_pointer_type]

99

100

101# c_library.gs1_encoder_free.restype=ctypes.

102 c_library.gs1_encoder_free.argtypes = [ctx_pointer_type]

103

104 c_library.gs1_encoder_getErrMsg.restype = ctypes.c_char_p

105 c_library.gs1_encoder_getErrMsg.argtypes = [ctx_pointer_type]

106

107

108# TODO: handle errors properly

109

110

111@dataclass

112 class ScalingParams:

113 pass

114

115

116@dataclass

117 class PixelScaling(ScalingParams):

118 pix_mult: float

119

120

121@dataclass

122 class DeviceDotScaling(ScalingParams):

123 resolution: float

124 min_x_dim: float

125 target_x_dim: float

126 max_x_dim: float

127

128

129 def 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

145 class 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

153 gs1_encoder_sNONE = -1 # ///< None defined

154 gs1_encoder_sDataBarOmni = 0 # ///< GS1 DataBar Omnidirectional

155 gs1_encoder_sDataBarTruncated = 1 # ///< GS1 DataBar Truncated

156 gs1_encoder_sDataBarStacked = 2 # ///< GS1 DataBar Stacked

157 gs1_encoder_sDataBarStackedOmni = 3 # ///< GS1 DataBar Stacked Omnidirectional

158 gs1_encoder_sDataBarLimited = 4 # ///< GS1 DataBar Limited

159 gs1_encoder_sDataBarExpanded = 5 # ///< GS1 DataBar Expanded (Stacked)

160 gs1_encoder_sUPCA = 6 # ///< UPC-A

161 gs1_encoder_sUPCE = 7 # ///< UPC-E

162 gs1_encoder_sEAN13 = 8 # ///< EAN-13

163 gs1_encoder_sEAN8 = 9 # ///< EAN-8

164 gs1_encoder_sGS1_128_CCA = 10 # ///< GS1-128 with CC-A or CC-B

165 gs1_encoder_sGS1_128_CCC = 11 # ///< GS1-128 with CC-C

166 gs1_encoder_sQR = 12 # ///< (GS1) QR Code

167 gs1_encoder_sDM = 13 # ///< (GS1) Data Matrix

168 gs1_encoder_sNUMSYMS = 14 # ///< Value is the number of symbologies

169

170

171# TODO read these enums from object file

172 gs1_encoder_dBMP = 0

173 gs1_encoder_dTIF = 1

174 gs1_encoder_dRAW = 2

175

176

177 def 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

186 def 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