source: trunk/Packages/lazbarcodes/src/lbc_medical.pas

Last change on this file was 123, checked in by chronos, 3 years ago
  • Added: QR code image visible in contact others tab. It can be saved as image to file.
File size: 8.6 KB
Line 
1{ lbc_medical.pas - Handles Pharmacode One-Track and Two-Track,
2 CodaBar as well as Code 32
3
4 Based on Zint (done by Robin Stuart and the Zint team)
5 http://github.com/zint/zint
6 and Pascal adaption by TheUnknownOnes
7 http://theunknownones.net
8
9 Refactoring: W. Pamler
10}
11
12unit lbc_medical;
13
14{$mode objfpc}{$H+}
15
16interface
17
18uses
19 SysUtils, zint;
20
21function pharma_one(ASymbol: PZintSymbol; const ASource: String): Integer;
22function pharma_two(ASymbol: PZintSymbol; const ASource: String): Integer;
23function codabar(ASymbol: PZintSymbol; const ASource: String): Integer;
24function code32(Asymbol: PZintSymbol; const ASource: String): Integer;
25
26implementation
27
28uses
29 math,
30 lbc_code, lbc_helper;
31
32{ 'Pharmacode can represent only a single integer from 3 to 131070. Unlike other
33 commonly used one-dimensional barcode schemes, pharmacode does not store the
34 data in a form corresponding to the human-readable digits; the number is
35 encoded in binary, rather than decimal.
36 Pharmacode is read from right to left: with n as the bar position starting
37 at 0 on the right, each narrow bar adds 2n to the value and each wide bar
38 adds 2(2^n).
39 The minimum barcode is 2 bars and the maximum 16, so the smallest number
40 that could be encoded is 3 (2 narrow bars) and the biggest is 131070
41 (16 wide bars).'
42 - http://en.wikipedia.org/wiki/Pharmacode
43
44 This code uses the One Track Pharamacode calculating algorithm as recommended
45 by the specification at http://www.laetus.com/laetus.php?request=file&id=69 }
46function pharma_one(ASymbol: PZintSymbol; const ASource: string): Integer;
47var
48 tester, counter, error_number, src_len: Integer;
49 inter: String;
50 dest: String;
51begin
52 src_len := Length(ASource);
53
54 if (src_len > 6) then
55 begin
56 ASymbol^.SetErrorText('Input too long (max 6 characters)');
57 Result := ERROR_TOO_LONG;
58 exit;
59 end;
60
61 error_number := is_sane(NEON, ASource);
62 if (error_number = ERROR_INVALID_DATA) then
63 begin
64 ASymbol^.SetErrorText('Invalid characters in data');
65 Result := error_number;
66 exit;
67 end;
68
69 tester := StrToIntDef(ASource, 0);
70 if (tester < 3) or (tester > 131070) then
71 begin
72 ASymbol^.SetErrorText('Data out of range');
73 Result := ERROR_INVALID_DATA;
74 exit;
75 end;
76
77 inter := '';
78 repeat
79 if tester and 1 = 0 then
80 begin
81 inter := inter + 'W';
82 tester := (tester - 2) div 2;
83 end else
84 begin
85 inter := inter + 'N';
86 tester := (tester - 1) div 2;
87 end;
88 until (tester = 0);
89
90 dest := '';
91 for counter := Length(inter) downto 1 do
92 begin
93 if (inter[counter] = 'W') then
94 dest := dest + '32'
95 else
96 dest := dest + '12';
97 end;
98 expand(ASymbol, dest);
99
100 ASymbol^.SetText(PChar(ASource));
101
102 Result := error_number;
103end;
104
105
106{ This code uses the Two Track Pharmacode defined in the document at
107 http://www.laetus.com/laetus.php?request=file&id=69 and using a modified
108 algorithm from the One Track system. This standard accepts integet values
109 from 4 to 64570080. }
110
111function pharma_two_calc(ASymbol: PZintSymbol; const ASource: string;
112 var dest: string): Integer;
113var
114 tester, counter: Integer;
115 inter: String;
116 error_number: Integer;
117begin
118 tester := StrToIntDef(ASource, 0);
119
120 if (tester < 4) or (tester > 64570080) then
121 begin
122 ASymbol^.SetErrorText('Data out of range');
123 Result := ERROR_INVALID_DATA;
124 exit;
125 end;
126
127 error_number := 0;
128 inter := '';
129 repeat
130 case tester mod 3 of
131 0: begin
132 inter := inter + '3';
133 tester := (tester - 3) div 3;
134 end;
135 1: begin
136 inter := inter + '1';
137 tester := (tester - 1) div 3;
138 end;
139 2: begin
140 inter := inter + '2';
141 tester := (tester - 2) div 3;
142 end;
143 end;
144 until tester = 0;
145
146 dest := '';
147 for counter := Length(inter) downto 1 do
148 dest := dest + inter[counter];
149
150 Result := error_number;
151end;
152
153{ Draws the patterns for two track pharmacode }
154function pharma_two(ASymbol: PZintSymbol; const ASource: String): Integer;
155var
156 height_pattern: string = '';
157 i, src_len, writer, error_number: Integer;
158begin
159 src_len := Length(ASource);
160
161 if (src_len > 8) then
162 begin
163 ASymbol^.SetErrorText('Input too long (max 8 characters).');
164 Result := ERROR_TOO_LONG;
165 exit;
166 end;
167
168 error_number := is_sane(NEON, ASource);
169 if (error_number = ERROR_INVALID_DATA) then
170 begin
171 ASymbol^.SetErrorText('Invalid characters in data');
172 Result := error_number;
173 exit;
174 end;
175
176 error_number := pharma_two_calc(ASymbol, ASource, height_pattern);
177 if (error_number <> 0) then
178 begin
179 Result := error_number;
180 exit;
181 end;
182
183 writer := 0;
184 for i := 1 to Length(height_pattern) do
185 begin
186 if ((height_pattern[i] = '2') or (height_pattern[i] = '3')) then
187 begin
188 set_module(ASymbol, 0, writer);
189 end;
190 if ((height_pattern[i] = '1') or (height_pattern[i] = '3')) then
191 begin
192 set_module(ASymbol, 1, writer);
193 end;
194 Inc(writer, 2);
195 end;
196 ASymbol^.rows := 2;
197 ASymbol^.width := writer - 1;
198
199 ASymbol^.SetText(ASource);
200
201 Result := error_number;
202end;
203
204
205{ The Codabar system consisting of simple substitution }
206//chaosben: some changes where made based on the article at http://en.wikipedia.org/wiki/Codabar}
207function codabar(ASymbol: PZintSymbol; const ASource: String): Integer;
208const
209 CALCIUM =
210 '0123456789-$:/.+ABCD';
211 CODA_TABLE: array[0..19] of string = (
212 '11111221', '11112211', '11121121', '22111111', '11211211',
213 '21111211', '12111121', '12112111', '12211111', '21121111',
214 '11122111', '11221111', '21112121', '21211121', '21212111',
215 '11212121', '11221211', '12121121', '11121221', '11122211'
216 );
217 CODABAR_DELIMITERS: array[0..7] of Char = (
218 'A', 'B', 'C', 'D', 'T', 'N', '*', 'E'
219 );
220var
221 i, j: Integer;
222 localSource: String;
223begin
224 localSource := Uppercase(ASource);
225
226 // Replace alternate delimiters
227 for i := 1 to Length(localSource) do
228 for j := 4 to 7 do
229 if localSource[i] = CODABAR_DELIMITERS[j] then
230 localSource[i] := CODABAR_DELIMITERS[j - 4];
231
232 // Check correct usage of delimiters
233 for i := 2 to Length(localSource) - 1 do
234 begin
235 for j := Low(CODABAR_DELIMITERS) to High(CODABAR_DELIMITERS) do
236 begin
237 if localSource[i] = CODABAR_DELIMITERS[j] then
238 begin
239 ASymbol^.SetErrorText('The character "' + ASource[i] + '" can only be used as first and/or last character.');
240 Result := ERROR_INVALID_DATA;
241 exit;
242 end;
243 end;
244 end;
245
246 Result := basic_encoder(ASymbol, localsource,
247 60, CALCIUM, '', CODA_TABLE, '', nil, false);
248
249 if Result = 0 then
250 ASymbol^.SetText(ASource);
251end;
252
253
254{ Italian Pharmacode }
255function code32(ASymbol: PZintSymbol; const ASource: String): Integer;
256const
257 TABLE = '0123456789BCDFGHJKLMNPQRSTUVWXYZ';
258var
259 i, j, error_number, checksum, checkpart: Integer;
260 localstr: String = '';
261 resultstr: String = '';
262 pharmacode, divisor: Integer;
263 remainder: Integer = 0;
264 codeword: array[0..5] of Integer = (0, 0, 0, 0, 0, 0);
265begin
266 { Validate the input }
267 if (Length(ASource) > 8) then
268 begin
269 ASymbol^.SetErrorText('Input too long (max 8 characters)');
270 Result := ERROR_TOO_LONG;
271 exit;
272 end;
273
274 error_number := is_sane(NEON, ASource);
275 if (error_number = ERROR_INVALID_DATA) then
276 begin
277 ASymbol^.SetErrorText('Invalid characters in data');
278 Result := error_number;
279 exit;
280 end;
281
282 { Add leading zeros as required }
283 localstr := StringOfChar('0', 8 - Length(ASource)) + ASource;
284
285 { Calculate the check digit }
286 checksum := 0;
287 for i := 0 to 3 do
288 begin
289 j := i * 2 + 1;
290 checkpart := StrToInt(localstr[j]);
291 Inc(checksum, checkpart);
292 checkpart := 2 * StrToInt(localstr[j + 1]);
293 if (checkpart >= 10) then
294 inc(checksum, (checkpart - 10) + 1)
295 else
296 inc(checksum, checkpart);
297 end;
298
299 { Add check digit to data string }
300 localstr := localstr + IntToStr(checksum mod 10);
301
302 { Convert string into an integer value }
303 pharmacode := StrToIntDef(localstr, 0);
304
305 { Convert from decimal to base-32 }
306 divisor := 33554432;
307 for i := 5 downto 0 do
308 begin
309 DivMod(pharmacode, divisor, codeword[i], remainder);
310 pharmacode := remainder;
311 divisor := divisor div 32;
312 end;
313
314 { Look up values in 'Tabella di conversione' }
315 SetLength(resultstr, 6);
316 for i := 5 downto 0 do
317 resultstr[5 - i + 1] := TABLE[codeword[i]+1];
318
319 { Plot the barcode using Code 39 }
320 error_number := c39(ASymbol, resultstr);
321 if (error_number <> 0) then
322 begin
323 Result := error_number;
324 exit;
325 end;
326
327 { Override the normal text output with the Pharmacode number }
328 ASymbol^.SetText('A' + localstr);
329
330 Result := error_number;
331end;
332
333end.
334
Note: See TracBrowser for help on using the repository browser.