source: trunk/Packages/lazbarcodes/src/lbc_helper.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: 19.1 KB
Line 
1unit lbc_helper;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 sysutils, types, zint;
9
10type
11 TCheckSumFunc = function(ASource: String): String;
12
13const
14 RHODIUM = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
15 SILVER = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd'; // 47 chars
16 NEON = '0123456789';
17 SODIUM = '0123456789-';
18
19 OPTION_ADD_CHECKSUM = 1;
20 OPTION_DISPLAY_CHECKSUM = 2;
21 OPTION_GROUPED_CHARS = 4;
22
23 NO_PADDING = #0;
24
25function IsTrue(aBoolean: Boolean): Boolean;
26function IsTrue(aInteger: Integer): Boolean;
27function iif(const aBoolean: integer; const aCh1,aCh2: pchar): pchar;
28function iif(const aBoolean: integer; const aCh1,aCh2: char): char;
29function iif(const aBoolean: Boolean; const aV1,aV2: Word): word;
30procedure concat(const aText: pchar; const aChar: char);
31procedure concat(const aText: pchar; const aText2: pchar);
32procedure concat(var aText: array of char; const aText2: pchar);
33procedure strcpy(const aText: pchar; const aText2: pchar);
34procedure strcpy(const aText: pchar; const aText2: TByteDynArray);
35procedure strcpy(var aText: array of char; const aText2: pchar);
36procedure strcpy(var aText: array of byte; const aText2: pchar);
37procedure strcpy(var AText: array of byte; const aText2: TByteDynArray);
38function posn(const aText: pchar; const aChar: Char): integer;
39function strlen(const aText: array of char): integer;
40function is_sane(test_string: PChar; source: PByte; length: Integer): Integer; overload;
41function is_sane(test_string: PChar; const source: TByteDynArray; length: Integer): Integer; overload;
42function is_sane(const AllowedChars, ASource: String): Integer; overload;
43function utf8toutf16(symbol: PointerTo_zint_symbol; source: PByte; vals: PInteger; length: PInteger): Integer;
44procedure set_module(symbol: PointerTo_zint_symbol; y_coord: Integer; x_coord: Integer);
45procedure unset_module(symbol: PointerTo_zint_symbol; y_coord: Integer; x_coord: Integer);
46function module_is_set(symbol: PointerTo_zint_symbol; y_coord: Integer; x_coord: Integer): Boolean;
47function PostInc(var v: integer): integer;
48function PostDec(var v: integer): integer;
49function is_extendable(symbology: Integer): Boolean;
50function is_stackable(symbology: Integer): Boolean;
51function NotBoolean(const aValue: integer): Boolean;
52function NotBoolean(const aValue: Boolean): Boolean;
53function latin1_process(symbol: PointerTo_zint_symbol; source: PBYTE; preprocessed: PBYTE; length: PInteger): Integer;
54function ctoi(c: char): integer;
55function ctoi(c: BYTE): integer;
56function BooleanNot(const aValue: integer): Boolean;
57procedure memset(const p: Pointer; const aValue: BYTE; const aSize: integer);
58function istwodigits(source: PByte; position: Integer): Boolean;
59procedure to_upper(a: PByte);
60procedure to_upper(const a: TByteDynArray);
61procedure Lookup(const Set_String: PChar; const ATable: array of String; const AData: Char; var ADest: TCharDynArray);
62procedure Lookup(const Set_String: PChar; const ATable: array of String; const AData: Byte; var ADest: TCharDynArray);
63
64procedure Lookup(const Set_String: String; const ATable: array of String; const AData: Char; var ADest: String);
65 {
66procedure lookup(const set_string : TArrayOfChar; const table : array of String; const data : Char; var dest : TArrayOfChar); overload;
67procedure lookup(const set_string : TArrayOfChar; const table : array of String; const data : Byte; var dest : TArrayOfChar); overload;
68procedure lookup(const ASet_string : String; const ATable : array of String; const AData : Byte; var ADest : TArrayOfChar); overload;
69procedure lookup(const ASet_string : String; const ATable : array of String; const AData : Char; var ADest : TArrayOfChar); overload;
70 }
71function itoc(AValue: Integer): Char;
72
73procedure expand(ASymbol: PointerTo_zint_symbol; AData: TCharDynArray);
74procedure expand(ASymbol: PointerTo_zint_symbol; const AData: string);
75
76function CharArrayToStr(AData: TCharDynArray): String;
77function StrToCharArray(AText: String): TCharDynArray;
78
79function basic_encoder(ASymbol: PZIntSymbol; const ASource: String;
80 MaxLen: Integer; const AllowedChars, StartCode: String;
81 const CharCodes: array of string; const StopCode: String;
82 CheckSumFunc: TCheckSumFunc; SourceInverted: Boolean): Integer;
83
84implementation
85
86uses
87 Math;
88
89function istwodigits(source: PBYTE; position: Integer): Boolean;
90begin
91 if (Char(source[position]) in ['0'..'9']) and (Char(source[position+1]) in ['0'..'9']) then begin
92 Result:=true;
93 end else begin
94 Result:=false;
95 end;
96end;
97
98function PostInc(var v: integer): integer;
99begin
100 Result:=v;
101 inc(v);
102end;
103
104function PostDec(var v: integer): integer;
105begin
106 Result:=v;
107 Dec(v);
108end;
109
110function IsTrue(aBoolean: Boolean): Boolean;
111begin
112 Result:=aBoolean;
113end;
114
115function IsTrue(aInteger: Integer): Boolean;
116begin
117 if aInteger=0 then Result:=false else Result:=true;
118end;
119
120function iif(const aBoolean: integer; const aCh1, aCh2: pchar): pchar;
121begin
122 if aBoolean=0 then begin
123 Result:=aCh2;
124 end else begin
125 Result:=aCh1;
126 end;
127end;
128
129function iif(const aBoolean: integer; const aCh1,aCh2: char): char;
130begin
131 if aBoolean=0 then begin
132 Result:=aCh2;
133 end else begin
134 Result:=aCh1;
135 end;
136end;
137
138function iif(const aBoolean: Boolean; const aV1,aV2: Word): word;
139begin
140 if aBoolean then Result:=aV1 else Result:=aV2;
141end;
142
143procedure concat(const aText: pchar; const aChar: char);
144var
145 i: integer;
146begin
147 i:=sysutils.strlen(aText);
148 aText[i]:=aChar;
149 inc(i);
150 aText[i]:=#0;
151end;
152
153procedure concat(const aText: pchar; const aText2: pchar);
154var
155 i: integer;
156begin
157 i:=sysutils.strlen(aText);
158 move(aText2^,aText[i],sysutils.strlen(aText2)+1);
159end;
160
161procedure concat(var aText: array of char; const aText2: pchar);
162begin
163 concat(pchar(@aText[0]), aText2);
164end;
165
166procedure strcpy(const aText: pchar; const aText2: pchar);
167begin
168 move(aText2^,aText^,sysutils.strlen(aText2)+1);
169end;
170
171procedure strcpy(const aText: pchar; const aText2: TByteDynArray);
172var
173 len: Integer;
174begin
175 len := sysutils.strlen(PChar(@aText2[0]));
176 move(aText2[0], aText^, len+1);
177end;
178
179procedure strcpy(var aText: array of char; const aText2: pchar);
180begin
181 move(aText2^,aText[0],sysutils.strlen(aText2)+1);
182end;
183
184procedure strcpy(var AText: array of byte; const aText2: PChar);
185begin
186 Move(aText2^, aText[0], sysUtils.strLen(aText2)+1);
187end;
188
189procedure strcpy(var AText: array of byte; const aText2: TByteDynArray);
190var
191 len: Integer;
192begin
193 len := sysUtils.strLen(PChar(@aText2[0]));
194 Move(aText2[0], aText[0], len+1);
195end;
196
197function posn(const aText: pchar; const aChar: Char): integer;
198var
199 p: Pchar;
200 c: integer;
201begin
202 p:=aText;
203 c:=0;
204 while p^<>#0 do begin
205 if p^<>aChar then begin
206 inc(c);
207 inc(p);
208 end else begin
209 Exit(c);
210 end;
211 end;
212 Result:=0;
213end;
214
215function strlen(const aText: array of char): integer;
216begin
217 Result:=sysutils.strlen(pchar(@aText[0]));
218end;
219
220{ Checks whether each character of ASource is contained in string AllowedChars.
221 Returns 0 if successful, otherwise error code ERROR_INVALID_DATA. }
222function is_sane(const AllowedChars, ASource: String): Integer;
223var
224 i: Integer;
225begin
226 for i := 1 to Length(ASource) do
227 if pos(ASource[i], AllowedChars) = 0 then
228 begin
229 Result := ERROR_INVALID_DATA;
230 exit;
231 end;
232 Result := 0;
233end;
234
235function is_sane(test_string: PChar; source: PByte; length: Integer): Integer;
236var
237 latch: Boolean;
238 j: Cardinal;
239 i: Integer;
240 lt: Cardinal;
241begin
242 {INITCODE} lt := sysutils.strlen (test_string);
243 i := 0;
244 while i < length do
245 begin
246 latch := FALSE;
247 j := 0;
248 while j < lt do
249 begin
250 if Boolean(source[i] = BYTE(test_string[j])) then
251 begin
252 latch := TRUE;
253 break;
254 end;
255 Inc (j);
256 end;
257 if Boolean(NotBoolean (latch)) then
258 begin
259 exit (ERROR_INVALID_DATA);
260 end;
261 Inc (i);
262 end;
263 exit (0);
264end;
265
266function is_sane(test_string: PChar; const source: TByteDynArray; Length: Integer): Integer;
267begin
268 Result := is_sane(test_string, PByte(@source[0]), Length);
269end;
270
271function utf8toutf16(symbol: PointerTo_zint_symbol; source: PBYTE; vals: PInteger; length: PInteger): Integer;
272var
273 error_number: Integer;
274 jpos: Integer;
275 bpos: Integer;
276 next: Integer;
277begin
278 bpos := 0;
279 jpos := 0;
280 error_number := 0;
281 next := 0;
282 repeat
283 if source[bpos] <= $7F then
284 begin
285 vals[jpos] := source[bpos];
286 next := bpos + 1;
287 Inc (jpos);
288 end else begin
289 if (source[bpos] >= $80) and (source[bpos] <= $BF) then
290 begin
291 strcpy (symbol^.errtxt, 'Corrupt Unicode data');
292 exit (ERROR_INVALID_DATA);
293 end;
294 if (source[bpos] >= $C0) and (source[bpos] <= $C1) then
295 begin
296 strcpy (symbol^.errtxt, 'Overlong encoding not supported');
297 exit (ERROR_INVALID_DATA);
298 end;
299 if (source[bpos] >= $C2) and (source[bpos] <= $DF) then
300 begin
301 vals[jpos] := ((source[bpos] and $1F) shl 6) + (source[bpos + 1] and $3F);
302 next := bpos + 2;
303 Inc (jpos);
304 end else begin
305 if (source[bpos] >= $E0) and (source[bpos] <= $EF) then
306 begin
307 vals[jpos] := ((source[bpos] and $0F) shl 12) + ((source[bpos + 1] and $3F) shl 6) + (source[bpos + 2] and $3F);
308 next := bpos + 3;
309 Inc (jpos);
310 end else begin
311 if source[bpos] >= $F0 then
312 begin
313 strcpy (symbol^.errtxt, 'Unicode sequences of more than 3 bytes not supported');
314 exit (ERROR_INVALID_DATA);
315 end;
316 end;
317 end;
318 end;
319 bpos := next;
320 until not (bpos < length^);
321 length^ := jpos;
322 exit (error_number);
323end;
324
325function module_is_set(symbol: PointerTo_zint_symbol; y_coord, x_coord: Integer): Boolean;
326begin
327 Result := symbol^.encoded_data[y_coord, x_coord];
328end;
329
330procedure set_module(symbol: PointerTo_zint_symbol; y_coord, x_coord: Integer);
331begin
332 symbol^.encoded_data[y_coord, x_coord] := true;
333end;
334
335procedure unset_module(symbol: PointerTo_zint_symbol; y_coord, x_coord: Integer);
336begin
337 symbol^.encoded_data[y_coord, x_coord] := false;
338end;
339
340procedure to_upper(a: PByte);
341begin
342 while a^<>0 do begin
343 if char(a^) in ['a'..'z'] then begin
344 a^:=Byte(upCase(char(a^)));
345 end;
346 inc(a);
347 end;
348end;
349
350procedure to_upper(const a: TByteDynArray);
351begin
352 to_upper(PByte(@a[0]));
353end;
354
355function ctoi(c: char): integer;
356begin
357 if c in ['0'..'9'] then begin
358 Result:=BYTE(c)-48;
359 end else begin
360 Result:=-1;
361 end;
362end;
363
364function ctoi(c: BYTE): integer;
365begin
366 Result:=ctoi(char(c));
367end;
368
369function BooleanNot(const aValue: integer): Boolean;
370begin
371 Result:=NotBoolean(aValue);
372end;
373
374procedure memset(const p: Pointer; const aValue: BYTE; const aSize: integer);
375begin
376 FillByte(PBYTE(p)^,aSize,aValue);
377end;
378
379function is_extendable(symbology: Integer): Boolean;
380begin
381 Result := true;
382 if symbology = BARCODE_EANX then exit;
383 if symbology = BARCODE_UPCA then exit;
384 if symbology = BARCODE_UPCE then exit;
385 if symbology = BARCODE_ISBNX then exit;
386 if symbology = BARCODE_UPCA_CC then exit;
387 if symbology = BARCODE_UPCE_CC then exit;
388 if symbology = BARCODE_EANX_CC then exit;
389 Result := false;
390end;
391
392function is_stackable(symbology: Integer): Boolean;
393begin
394 Result := true;
395 if symbology < BARCODE_PDF417 then exit;
396 if symbology = BARCODE_CODE128B then exit;
397 if symbology = BARCODE_ISBNX then exit;
398 if symbology = BARCODE_EAN14 then exit;
399 if symbology = BARCODE_NVE18 then exit;
400 if symbology = BARCODE_KOREAPOST then exit;
401 if symbology = BARCODE_PLESSEY then exit;
402 if symbology = BARCODE_TELEPEN_NUM then exit;
403 if symbology = BARCODE_ITF14 then exit;
404 if symbology = BARCODE_CODE32 then exit;
405 Result := false;
406end;
407
408function NotBoolean(const aValue: integer): Boolean;
409begin
410 Result := (aValue = 0);
411end;
412
413function NotBoolean(const aValue: Boolean): Boolean;
414begin
415 Result := not aValue;
416end;
417
418function latin1_process(symbol: PointerTo_zint_symbol; source: PBYTE; preprocessed: PBYTE; length: PInteger): Integer;
419var
420 next: Integer;
421 i: Integer;
422 j: Integer;
423begin
424 j := 0;
425 i := 0;
426 repeat
427 next := -1;
428 if Boolean(source[i] < 128) then
429 begin
430 preprocessed[j] := source[i];
431 Inc (j);
432 next := i + 1;
433 end else begin
434 if Boolean(source[i] = $C2) then
435 begin
436 preprocessed[j] := source[i + 1];
437 Inc (j);
438 next := i + 2;
439 end;
440 if Boolean(source[i] = $C3) then
441 begin
442 preprocessed[j] := source[i + 1] + 64;
443 Inc (j);
444 next := i + 2;
445 end;
446 end;
447 if Boolean(next = -1) then
448 begin
449 strcpy (symbol^.errtxt, 'error: Invalid character in input string (only Latin-1 characters supported)');
450 exit (ERROR_INVALID_DATA);
451 end;
452 i := next;
453 until not (i < length^);
454 preprocessed[j] := 0;
455 length^ := j;
456 exit (0);
457end;
458
459{ Replaces huge switch statements for looking up in tables }
460procedure Lookup(const Set_String: PChar; const ATable: array of String;
461 const AData: Char; var ADest: TCharDynArray);
462var
463 i, n: integer;
464begin
465 n := System.Strlen(Set_String);
466 for i := 0 to n-1 do
467 if AData = Set_String[i] then
468 concat(ADest, PChar(ATable[i]));
469end;
470
471procedure Lookup(const Set_String: PChar; const ATable: array of String;
472 const AData: Byte; var ADest: TCharDynArray);
473begin
474 LookUp(set_string, ATable, char(AData), ADest);
475end;
476
477{ Set_string is a string with the allowed characters.
478 ATable is the bar-space pattern for each of these allowed characters.
479 AData is the character to be encoded.
480 ADest is the output string.
481 The routines searches the character in the list of allowed characters and
482 appends its bar-space pattern to ADest. }
483procedure Lookup(const Set_String: String; const ATable: array of String;
484 const AData: Char; var ADest: String);
485var
486 i: Integer;
487begin
488 i := pos(AData, Set_String);
489 if i > 0 then
490 ADest := ADest + ATable[i-1];
491end;
492
493
494{ Converts an integer value to its hexadecimal character }
495function itoc(AValue: Integer): Char;
496begin
497 if (AValue >= 0) and (AValue <= 9) then
498 Result := Char(Ord('0') + AValue)
499 else
500 Result := Char(Ord('A') + (AValue - 10));
501end;
502
503
504{ Expands from a width pattern to a bit pattern */ }
505procedure expand(ASymbol: PointerTo_zint_symbol; const AData: String);
506var
507 reader, writer, i: Integer;
508 latch: boolean;
509begin
510 writer := 0;
511 latch := true;
512
513 for reader := 1 to Length(AData) do
514 begin
515 for i := 1 to ctoi(AData[reader]) do
516 begin
517 if latch then
518 set_module(ASymbol, ASymbol^.rows, writer);
519 inc(writer);
520 end;
521 latch := not latch;
522 end;
523
524 if ASymbol^.symbology <> BARCODE_PHARMA then
525 begin
526 if writer > ASymbol^.width then
527 ASymbol^.width := writer;
528 end else
529 begin
530 { Pharmacode One ends with a space - adjust for this }
531 if (writer > ASymbol^.width + 2) then
532 ASymbol^.width := writer - 2;
533 end;
534
535 ASymbol^.rows := ASymbol^.rows + 1;
536end;
537
538procedure expand(ASymbol: PointerTo_zint_symbol; AData: TCharDynArray);
539var
540 reader, n : Cardinal;
541 writer, i : Integer;
542 latch : Char;
543begin
544 n := strlen(AData);
545 writer := 0;
546 latch := '1';
547
548 for reader := 0 to n - 1 do
549 begin
550 for i := 0 to ctoi(AData[reader]) - 1 do
551 begin
552 if (latch = '1') then
553 set_module(ASymbol, ASymbol^.rows, writer);
554 Inc(writer);
555 end;
556
557 if latch = '1' then latch := '0' else latch := '1';
558 end;
559
560 if(ASymbol^.symbology <> BARCODE_PHARMA) then
561 begin
562 if(writer > ASymbol^.width) then
563 ASymbol^.width := writer;
564 end else
565 begin
566 { Pharmacode One ends with a space - adjust for this }
567 if(writer > ASymbol^.width + 2) then
568 ASymbol^.width := writer - 2;
569 end;
570
571 ASymbol^.rows := ASymbol^.rows + 1;
572end;
573
574function CharArrayToStr(AData: TCharDynArray): String;
575var
576 len: Integer;
577begin
578 Result := '';
579 len := Length(AData);
580 SetLength(Result, len);
581 if len > 0 then
582 Move(AData[0], Result[1], len*SizeOf(Char));
583end;
584
585function StrToCharArray(AText: String): TCharDynArray;
586var
587 len: Integer;
588begin
589 Result := nil;
590 len := Length(AText);
591 SetLength(Result, len + 1);
592 if len > 0 then
593 Move(AText[1], Result[0], len*SizeOf(Char));
594 Result[len] := #0;
595end;
596
597
598{ Basic encoding routine for barcodes - most of them work all in the same way
599 only with different parameters:
600 MaxLen - maximum length of input string
601 AllowedChars - a string containing the allowed characters for this barcode type.
602 If empty, all characters are allowed (any check musts be done by the caller).
603 StartCode - starting code pattern
604 CharCodes - code pattern for each allowed character
605 StopCode - ending code pattern
606 CheckSumFunc - function to calculate a checksum which is appended to the
607 input string
608 SourceInverted - input string is encoded from last to first char
609}
610function basic_encoder(ASymbol: PZIntSymbol; const ASource: String;
611 MaxLen: Integer; const AllowedChars, StartCode: String;
612 const CharCodes: array of string; const StopCode: String;
613 CheckSumFunc: TCheckSumFunc; SourceInverted: Boolean): Integer;
614var
615 i: Integer;
616 src, dest: String;
617 check: String;
618begin
619 Result := 0;
620
621 if Length(ASource) > MaxLen then
622 begin
623 ASymbol^.SetErrorText('Input too long (max ' + IntToStr(MaxLen) + ' digits).');
624 Result := ERROR_TOO_LONG;
625 exit;
626 end;
627
628 src := ASource;
629
630 // START character
631 dest := StartCode;
632
633 // Check whether all characters of the input string are allowed.
634 if AllowedChars <> '' then
635 begin
636 Result := is_sane(AllowedChars, ASource);
637 if Result = ERROR_INVALID_DATA then
638 begin
639 ASymbol^.SetErrorText('Invalid characters in data.');
640 exit;
641 end;
642
643 // Add encoded input string characters to destination string
644 if SourceInverted then
645 for i := Length(src) downto 1 do
646 lookup(AllowedChars, CharCodes, src[i], dest)
647 else
648 for i := 1 to Length(src) do
649 lookup(AllowedChars, CharCodes, src[i], dest);
650
651 // Add check digit(s) if a calculation function is provided
652 if (ASymbol^.Option and OPTION_ADD_CHECKSUM = OPTION_ADD_CHECKSUM) and
653 (CheckSumFunc <> nil) then
654 begin
655 check := CheckSumFunc(src);
656 src := src + check;
657 for i := 1 to Length(check) do
658 lookup(AllowedChars, CharCodes, check[i], dest);
659 end;
660 end
661 else
662 // All characters are allowed -> lookup not needed
663 begin
664 // Add encoded input string characters to destination string
665 if SourceInverted then
666 for i := Length(src) downto 1 do
667 dest := dest + CharCodes[ord(src[i])]
668 else
669 for i := 1 to Length(src) do
670 dest := dest + CharCodes[ord(src[i])];
671
672 // Add check digit(s) if a calculation function is provided
673 if (ASymbol^.Option and OPTION_ADD_CHECKSUM = OPTION_ADD_CHECKSUM) and
674 (ChecksumFunc <> nil) then
675 begin
676 check := CheckSumFunc(src);
677 src := src + check;
678 for i := 1 to Length(check) do
679 dest := dest + CharCodes[ord(check[i])];
680 end;
681 end;
682
683 // STOP character
684 dest := dest + StopCode;
685
686 // Expand the RLE-encoded bar/space widths to modules
687 // Example: '121' --> 1001 where 1 = black, 0 = white
688 // This information is stored in the ZintSymbol.
689 expand(ASymbol, dest);
690
691 // Store human-readable text
692 if ASymbol^.Option and OPTION_DISPLAY_CHECKSUM = OPTION_DISPLAY_CHECKSUM then
693 ASymbol^.SetText(src)
694 else
695 ASymbol^.SetText(ASource);
696end;
697
698
699end.
Note: See TracBrowser for help on using the repository browser.