1 {
2  *****************************************************************************
3   This file is part of LazUtils.
4 
5   See the file COPYING.modifiedLGPL.txt, included in this distribution,
6   for details about the license.
7  *****************************************************************************
8 
9   This unit includes string routines which are based on UTF-16 implementations,
10   although it might also include routines for other encodings.
11 
12   A UTF-16 based implementation for LowerCase, for example, is faster in WideString
13   and UnicodeString then the default UTF-8 implementation.
14 
15   Currently this unit includes only UTF8LowerCaseViaTables which is based on a
16   UTF-16 table, but it might be extended to include various UTF-16 routines.
17 }
18 unit LazUTF16;
19 
20 {$IFDEF FPC}
21  {$mode objfpc}{$H+}
22 {$ENDIF}
23 
24 interface
25 
26 uses
27   Classes, SysUtils
28 {$IFDEF FPC}
29   ,lazutf8
30 {$ENDIF}
31   ;
32 
33 {$IFnDEF FPC}
34 type
35   PtrInt = NativeInt;
36 {$ENDIF}
37 
UTF16CharacterLengthnull38 function UTF16CharacterLength(p: PWideChar): integer;
UTF16Lengthnull39 function UTF16Length(const s: widestring): PtrInt; overload;
UTF16Lengthnull40 function UTF16Length(p: PWideChar; WordCount: PtrInt): PtrInt; overload;
UTF16Copynull41 function UTF16Copy(const s: UnicodeString; StartCharIndex, CharCount: PtrInt): Unicodestring;
UTF16CharStartnull42 function UTF16CharStart(P: PWideChar; Len, CharIndex: PtrInt): PWideChar;
UTF16Posnull43 function UTF16Pos(const SearchForText, SearchInText: UnicodeString; StartPos: PtrInt = 1): PtrInt;
UTF16CharacterToUnicodenull44 function UTF16CharacterToUnicode(p: PWideChar; out CharLen: integer): Cardinal;
UnicodeToUTF16null45 function UnicodeToUTF16(u: cardinal): widestring;
IsUTF16CharValidnull46 function IsUTF16CharValid(AChar, ANextChar: WideChar): Boolean;
IsUTF16StringValidnull47 function IsUTF16StringValid(AWideStr: widestring): Boolean;
Utf16StringReplacenull48 function Utf16StringReplace(const S, OldPattern, NewPattern: WideString; Flags: TReplaceFlags): WideString; Inline;
Utf16StringReplacenull49 function Utf16StringReplace(const S, OldPattern, NewPattern: WideString; Flags: TReplaceFlags; out Count: Integer): WideString;
50 
UnicodeLowercasenull51 function UnicodeLowercase(u: cardinal): cardinal;
52 {$IFDEF FPC}
UTF8LowerCaseViaTablesnull53 function UTF8LowerCaseViaTables(const s: string): string;
54 {$ENDIF}
55 
56 implementation
57 
UTF16CharacterLengthnull58 function UTF16CharacterLength(p: PWideChar): integer;
59 // returns length of UTF16 character in number of words
60 // The endianess of the machine will be taken.
61 begin
62   if p<>nil then begin
63     if (ord(p[0]) < $D800) or (ord(p[0]) > $DFFF) then
64       Result:=1
65     else
66       Result:=2;
67   end else begin
68     Result:=0;
69   end;
70 end;
71 
UTF16Lengthnull72 function UTF16Length(const s: widestring): PtrInt;
73 begin
74   Result:=UTF16Length(PWideChar(s),length(s));
75 end;
76 
UTF16Lengthnull77 function UTF16Length(p: PWideChar; WordCount: PtrInt): PtrInt;
78 var
79   CharLen: LongInt;
80 begin
81   Result:=0;
82   while (WordCount>0) do begin
83     inc(Result);
84     CharLen:=UTF16CharacterLength(p);
85     inc(p,CharLen);
86     dec(WordCount,CharLen);
87   end;
88 end;
89 
UTF16Copynull90 function UTF16Copy(const s: UnicodeString; StartCharIndex, CharCount: PtrInt): Unicodestring;
91 // returns substring
92 var
93   StartPos: PWideChar;
94   EndPos: PWideChar;
95   MaxBytes: PtrInt;
96 begin
97   StartPos:=UTF16CharStart(PWideChar(s),length(s),StartCharIndex-1);
98   if StartPos=nil then
99     Result:=''
100   else begin
101     MaxBytes:=PtrInt(PWideChar(s)+length(s)-StartPos);
102     EndPos:=UTF16CharStart(StartPos,MaxBytes,CharCount);
103     if EndPos=nil then
104       Result:=copy(s,StartPos-PWideChar(s)+1,MaxBytes)
105     else
106       Result:=copy(s,StartPos-PWideChar(s)+1,EndPos-StartPos);
107   end;
108 end;
109 
UTF16CharStartnull110 function UTF16CharStart(P: PWideChar; Len, CharIndex: PtrInt): PWideChar;
111 // Len is the length in words of P.
112 // CharIndex is the position of the desired UnicodeChar (starting at 0).
113 var
114   CharLen: LongInt;
115 begin
116   Result:=P;
117   if Result=nil then Exit;
118   while (CharIndex>0) and (Len>0) do
119   begin
120     CharLen:=UTF16CharacterLength(Result);
121     dec(Len,CharLen);
122     dec(CharIndex);
123     inc(Result,CharLen);
124   end;
125   if (CharIndex<>0) or (Len<0) then
126     Result:=nil;
127 end;
128 
IndexOfWideCharnull129 function IndexOfWideChar(Const Buf: PWideChar; Len: PtrInt; b: WideChar): PtrInt;
130 begin
131   for Result:=0 to len-1 do
132     if buf[Result]=b then
133       Exit;
134   Result:=-1;
135 end;
136 
137 // Helper used by UTF16Pos
UTF16PosPnull138 function UTF16PosP(SearchForText: PWideChar; SearchForTextLen: PtrInt;
139   SearchInText: PWideChar; SearchInTextLen: PtrInt): PWideChar;
140 // returns the position where SearchInText starts in SearchForText
141 // returns nil if not found
142 var
143   p: PtrInt;
144 begin
145   Result:=nil;
146   if (SearchForText=nil) or (SearchForTextLen=0) or (SearchInText=nil) then
147     exit;
148   while SearchInTextLen>0 do begin
149     p:=IndexOfWideChar(SearchInText,SearchInTextLen,SearchForText^);
150     if p<0 then exit;
151     inc(SearchInText, p);
152     dec(SearchInTextLen, p);
153     if SearchInTextLen<SearchForTextLen then exit;
154     if CompareMem(SearchInText,SearchForText,SearchForTextLen * 2) then
155       exit(SearchInText);
156     inc(SearchInText);
157     dec(SearchInTextLen);
158   end;
159 end;
160 
UTF16Posnull161 function UTF16Pos(const SearchForText, SearchInText: UnicodeString; StartPos: PtrInt = 1): PtrInt;
162 // returns the character index, where the SearchForText starts in SearchInText
163 // an optional StartPos can be given (in UTF-16 codepoints, not in word)
164 // returns 0 if not found
165 var
166   i: PtrInt;
167   p: PWideChar;
168   StartPosP: PWideChar;
169 begin
170   Result:=0;
171   if StartPos=1 then
172   begin
173     i:=System.Pos(SearchForText,SearchInText);
174     if i>0 then
175       Result:=UTF16Length(PWideChar(SearchInText),i-1)+1;
176   end
177   else if StartPos>1 then
178   begin
179     // skip
180     StartPosP:=UTF16CharStart(PWideChar(SearchInText),Length(SearchInText),StartPos-1);
181     if StartPosP=nil then exit;
182     // search
183     p:=UTF16PosP(PWideChar(SearchForText),length(SearchForText),
184                 StartPosP,length(SearchInText)+PWideChar(SearchInText)-StartPosP);
185     // get UTF-8 position
186     if p=nil then exit;
187     Result:=StartPos+UTF16Length(StartPosP,p-StartPosP);
188   end;
189 end;
190 
UTF16CharacterToUnicodenull191 function UTF16CharacterToUnicode(p: PWideChar; out CharLen: integer): Cardinal;
192 var
193   w1: cardinal;
194   w2: Cardinal;
195 begin
196   if p<>nil then begin
197     w1:=ord(p[0]);
198     if (w1 < $D800) or (w1 > $DFFF) then begin
199       // is 1 word character
200       Result:=w1;
201       CharLen:=1;
202     end else begin
203       // could be 2 word character
204       w2:=ord(p[1]);
205       if (w2>=$DC00) then begin
206         // is 2 word character
207         Result:=(w1-$D800) shl 10 + (w2-$DC00) + $10000;
208         CharLen:=2;
209       end else begin
210         // invalid character
211         Result:=w1;
212         CharLen:=1;
213       end;
214     end;
215   end else begin
216     Result:=0;
217     CharLen:=0;
218   end;
219 end;
220 
UnicodeToUTF16null221 function UnicodeToUTF16(u: cardinal): widestring;
222 begin
223   // u should be <= $10FFFF to fit into UTF-16
224 
225   if u < $10000 then
226     // Note: codepoints $D800 - $DFFF are reserved
227     Result:=system.widechar(u)
228   else
229     Result:=system.widechar($D800+((u - $10000) shr 10))+system.widechar($DC00+((u - $10000) and $3ff));
230 end;
231 
232 // Specification here: http://unicode.org/faq/utf_bom.html#utf16-7
233 // Q: Are there any 16-bit values that are invalid?
234 // A: Unpaired surrogates are invalid in UTFs. These include any value in the
235 // range D800 to DBFF not followed by a value in the range DC00 to DFFF,
236 // or any value in the range DC00 to DFFF not preceded by a value in the range D800 to DBFF. [AF]
237 //
238 // Use ANextChar = #0 to indicate that there is no next char
IsUTF16CharValidnull239 function IsUTF16CharValid(AChar, ANextChar: WideChar): Boolean;
240 begin
241   if AChar = #0 then Exit(False);
242   Result := ((AChar >= #$D800) and (AChar <= #$DBFF)) and not ((ANextChar >= #$DC00) and (ANextChar <= #$DFFF));
243   //Result := (Word(AChar) in [$D800..$DBFF]) and not (Word(ANextChar) in [$DC00..$DFFF]); <= generates range check error
244   Result := not Result;
245 end;
246 
IsUTF16StringValidnull247 function IsUTF16StringValid(AWideStr: widestring): Boolean;
248 var
249   i: Integer;
250 begin
251   Result := True;
252   for i := 1 to Length(AWideStr)-1 do
253   begin
254     Result := Result and IsUTF16CharValid(AWideStr[i], AWideStr[i+1]);
255     if not Result then Exit;
256   end;
257 end;
258 
Utf16StringReplacenull259 function Utf16StringReplace(const S, OldPattern, NewPattern: WideString;
260   Flags: TReplaceFlags): WideString;
261 var
262   DummyCount: Integer;
263 begin
264   Result := Utf16StringReplace(S, OldPattern, NewPattern, Flags, DummyCount);
265 end;
266 
267 //Same as SysUtil.StringReplace but for WideStrings/UnicodeStrings, since it's not available in fpc yet
268 function Utf16StringReplace(const S, OldPattern, NewPattern: WideString;
269                             Flags: TReplaceFlags; out Count: Integer): WideString;
270 var
271   Srch, OldP, RemS: WideString; // Srch and OldP can contain WideUpperCase versions of S,OldPattern
272   P: Integer;
273 begin
274   Srch:=S;
275   OldP:=OldPattern;
276   Count := 0;
277   if rfIgnoreCase in Flags then
278   begin
279     Srch:=WideUpperCase(Srch);
280     OldP:=WideUpperCase(OldP);
281   end;
282   RemS:=S;
283   Result:='';
284   while (Length(Srch)<>0) do
285   begin
286     P:=Pos(OldP, Srch);
287     if P=0 then
288     begin
289       Result:=Result+RemS;
290       Srch:='';
291     end
292     else
293     begin
294       Inc(Count);
295       Result:=Result+Copy(RemS,1,P-1)+NewPattern;
296       P:=P+Length(OldP);
297       RemS:=Copy(RemS,P,Length(RemS)-P+1);
298       if not (rfReplaceAll in Flags) then
299       begin
300         Result:=Result+RemS;
301         Srch:='';
302       end
303       else
304         Srch:=Copy(Srch,P,Length(Srch)-P+1);
305     end;
306   end;
307 end;
308 
309 
310 // Lowercase Unicode Tables which match UTF-16 but also UTF-32
311 var
312   UnicodeLower00C0_00DE: array[$00C0..$00DE] of word;
313   UnicodeLower0100_024E: array[$0100..$024E] of word;
314   UnicodeLower0386_03AB: array[$0386..$03AB] of word;
315   UnicodeLower03D8_042F: array[$03D8..$042F] of word;
316   UnicodeLower0460_0512: array[$0460..$0512] of word;
317   UnicodeLower1E00_1FFC: array[$1E00..$1FFC] of word;
318   UnicodeLower2126_2183: array[$2126..$2183] of word;
319   UnicodeLower2C60_2CE2: array[$2C60..$2CE2] of word;
320 
321 procedure InitUnicodeTables;
322 var
323   i: Integer;
324 begin
325   for i:=Low(UnicodeLower00C0_00DE) to High(UnicodeLower00C0_00DE) do
326     UnicodeLower00C0_00DE[i]:=i+32;
327   UnicodeLower00C0_00DE[$00D7]:=$00D7;
328 
329   for i:=Low(UnicodeLower0100_024E) to High(UnicodeLower0100_024E) do
330     UnicodeLower0100_024E[i]:=i;
331   UnicodeLower0100_024E[$0100]:=$0101;
332   UnicodeLower0100_024E[$0102]:=$0103;
333   UnicodeLower0100_024E[$0104]:=$0105;
334   UnicodeLower0100_024E[$0106]:=$0107;
335   UnicodeLower0100_024E[$0108]:=$0109;
336   UnicodeLower0100_024E[$010A]:=$010B;
337   UnicodeLower0100_024E[$010C]:=$010D;
338   UnicodeLower0100_024E[$010E]:=$010F;
339   UnicodeLower0100_024E[$0110]:=$0111;
340   UnicodeLower0100_024E[$0112]:=$0113;
341   UnicodeLower0100_024E[$0114]:=$0115;
342   UnicodeLower0100_024E[$0116]:=$0117;
343   UnicodeLower0100_024E[$0118]:=$0119;
344   UnicodeLower0100_024E[$011A]:=$011B;
345   UnicodeLower0100_024E[$011C]:=$011D;
346   UnicodeLower0100_024E[$011E]:=$011F;
347   UnicodeLower0100_024E[$0120]:=$0121;
348   UnicodeLower0100_024E[$0122]:=$0123;
349   UnicodeLower0100_024E[$0124]:=$0125;
350   UnicodeLower0100_024E[$0126]:=$0127;
351   UnicodeLower0100_024E[$0128]:=$0129;
352   UnicodeLower0100_024E[$012A]:=$012B;
353   UnicodeLower0100_024E[$012C]:=$012D;
354   UnicodeLower0100_024E[$012E]:=$012F;
355   UnicodeLower0100_024E[$0130]:=$0069;
356   UnicodeLower0100_024E[$0132]:=$0133;
357   UnicodeLower0100_024E[$0134]:=$0135;
358   UnicodeLower0100_024E[$0136]:=$0137;
359   UnicodeLower0100_024E[$0139]:=$013A;
360   UnicodeLower0100_024E[$013B]:=$013C;
361   UnicodeLower0100_024E[$013D]:=$013E;
362   UnicodeLower0100_024E[$013F]:=$0140;
363   UnicodeLower0100_024E[$0141]:=$0142;
364   UnicodeLower0100_024E[$0143]:=$0144;
365   UnicodeLower0100_024E[$0145]:=$0146;
366   UnicodeLower0100_024E[$0147]:=$0148;
367   UnicodeLower0100_024E[$014A]:=$014B;
368   UnicodeLower0100_024E[$014C]:=$014D;
369   UnicodeLower0100_024E[$014E]:=$014F;
370   UnicodeLower0100_024E[$0150]:=$0151;
371   UnicodeLower0100_024E[$0152]:=$0153;
372   UnicodeLower0100_024E[$0154]:=$0155;
373   UnicodeLower0100_024E[$0156]:=$0157;
374   UnicodeLower0100_024E[$0158]:=$0159;
375   UnicodeLower0100_024E[$015A]:=$015B;
376   UnicodeLower0100_024E[$015C]:=$015D;
377   UnicodeLower0100_024E[$015E]:=$015F;
378   UnicodeLower0100_024E[$0160]:=$0161;
379   UnicodeLower0100_024E[$0162]:=$0163;
380   UnicodeLower0100_024E[$0164]:=$0165;
381   UnicodeLower0100_024E[$0166]:=$0167;
382   UnicodeLower0100_024E[$0168]:=$0169;
383   UnicodeLower0100_024E[$016A]:=$016B;
384   UnicodeLower0100_024E[$016C]:=$016D;
385   UnicodeLower0100_024E[$016E]:=$016F;
386   UnicodeLower0100_024E[$0170]:=$0171;
387   UnicodeLower0100_024E[$0172]:=$0173;
388   UnicodeLower0100_024E[$0174]:=$0175;
389   UnicodeLower0100_024E[$0176]:=$0177;
390   UnicodeLower0100_024E[$0178]:=$00FF;
391   UnicodeLower0100_024E[$0179]:=$017A;
392   UnicodeLower0100_024E[$017B]:=$017C;
393   UnicodeLower0100_024E[$017D]:=$017E;
394   UnicodeLower0100_024E[$0181]:=$0253;
395   UnicodeLower0100_024E[$0182]:=$0183;
396   UnicodeLower0100_024E[$0184]:=$0185;
397   UnicodeLower0100_024E[$0186]:=$0254;
398   UnicodeLower0100_024E[$0187]:=$0188;
399   UnicodeLower0100_024E[$0189]:=$0256;
400   UnicodeLower0100_024E[$018A]:=$0257;
401   UnicodeLower0100_024E[$018B]:=$018C;
402   UnicodeLower0100_024E[$018E]:=$01DD;
403   UnicodeLower0100_024E[$018F]:=$0259;
404   UnicodeLower0100_024E[$0190]:=$025B;
405   UnicodeLower0100_024E[$0191]:=$0192;
406   UnicodeLower0100_024E[$0193]:=$0260;
407   UnicodeLower0100_024E[$0194]:=$0263;
408   UnicodeLower0100_024E[$0196]:=$0269;
409   UnicodeLower0100_024E[$0197]:=$0268;
410   UnicodeLower0100_024E[$0198]:=$0199;
411   UnicodeLower0100_024E[$019C]:=$026F;
412   UnicodeLower0100_024E[$019D]:=$0272;
413   UnicodeLower0100_024E[$019F]:=$0275;
414   UnicodeLower0100_024E[$01A0]:=$01A1;
415   UnicodeLower0100_024E[$01A2]:=$01A3;
416   UnicodeLower0100_024E[$01A4]:=$01A5;
417   UnicodeLower0100_024E[$01A6]:=$0280;
418   UnicodeLower0100_024E[$01A7]:=$01A8;
419   UnicodeLower0100_024E[$01A9]:=$0283;
420   UnicodeLower0100_024E[$01AC]:=$01AD;
421   UnicodeLower0100_024E[$01AE]:=$0288;
422   UnicodeLower0100_024E[$01AF]:=$01B0;
423   UnicodeLower0100_024E[$01B1]:=$028A;
424   UnicodeLower0100_024E[$01B2]:=$028B;
425   UnicodeLower0100_024E[$01B3]:=$01B4;
426   UnicodeLower0100_024E[$01B5]:=$01B6;
427   UnicodeLower0100_024E[$01B7]:=$0292;
428   UnicodeLower0100_024E[$01B8]:=$01B9;
429   UnicodeLower0100_024E[$01BC]:=$01BD;
430   UnicodeLower0100_024E[$01C4]:=$01C6;
431   UnicodeLower0100_024E[$01C5]:=$01C6;
432   UnicodeLower0100_024E[$01C7]:=$01C9;
433   UnicodeLower0100_024E[$01C8]:=$01C9;
434   UnicodeLower0100_024E[$01CA]:=$01CC;
435   UnicodeLower0100_024E[$01CB]:=$01CC;
436   UnicodeLower0100_024E[$01CD]:=$01CE;
437   UnicodeLower0100_024E[$01CF]:=$01D0;
438   UnicodeLower0100_024E[$01D1]:=$01D2;
439   UnicodeLower0100_024E[$01D3]:=$01D4;
440   UnicodeLower0100_024E[$01D5]:=$01D6;
441   UnicodeLower0100_024E[$01D7]:=$01D8;
442   UnicodeLower0100_024E[$01D9]:=$01DA;
443   UnicodeLower0100_024E[$01DB]:=$01DC;
444   UnicodeLower0100_024E[$01DE]:=$01DF;
445   UnicodeLower0100_024E[$01E0]:=$01E1;
446   UnicodeLower0100_024E[$01E2]:=$01E3;
447   UnicodeLower0100_024E[$01E4]:=$01E5;
448   UnicodeLower0100_024E[$01E6]:=$01E7;
449   UnicodeLower0100_024E[$01E8]:=$01E9;
450   UnicodeLower0100_024E[$01EA]:=$01EB;
451   UnicodeLower0100_024E[$01EC]:=$01ED;
452   UnicodeLower0100_024E[$01EE]:=$01EF;
453   UnicodeLower0100_024E[$01F1]:=$01F3;
454   UnicodeLower0100_024E[$01F2]:=$01F3;
455   UnicodeLower0100_024E[$01F4]:=$01F5;
456   UnicodeLower0100_024E[$01F6]:=$0195;
457   UnicodeLower0100_024E[$01F7]:=$01BF;
458   UnicodeLower0100_024E[$01F8]:=$01F9;
459   UnicodeLower0100_024E[$01FA]:=$01FB;
460   UnicodeLower0100_024E[$01FC]:=$01FD;
461   UnicodeLower0100_024E[$01FE]:=$01FF;
462   UnicodeLower0100_024E[$0200]:=$0201;
463   UnicodeLower0100_024E[$0202]:=$0203;
464   UnicodeLower0100_024E[$0204]:=$0205;
465   UnicodeLower0100_024E[$0206]:=$0207;
466   UnicodeLower0100_024E[$0208]:=$0209;
467   UnicodeLower0100_024E[$020A]:=$020B;
468   UnicodeLower0100_024E[$020C]:=$020D;
469   UnicodeLower0100_024E[$020E]:=$020F;
470   UnicodeLower0100_024E[$0210]:=$0211;
471   UnicodeLower0100_024E[$0212]:=$0213;
472   UnicodeLower0100_024E[$0214]:=$0215;
473   UnicodeLower0100_024E[$0216]:=$0217;
474   UnicodeLower0100_024E[$0218]:=$0219;
475   UnicodeLower0100_024E[$021A]:=$021B;
476   UnicodeLower0100_024E[$021C]:=$021D;
477   UnicodeLower0100_024E[$021E]:=$021F;
478   UnicodeLower0100_024E[$0220]:=$019E;
479   UnicodeLower0100_024E[$0222]:=$0223;
480   UnicodeLower0100_024E[$0224]:=$0225;
481   UnicodeLower0100_024E[$0226]:=$0227;
482   UnicodeLower0100_024E[$0228]:=$0229;
483   UnicodeLower0100_024E[$022A]:=$022B;
484   UnicodeLower0100_024E[$022C]:=$022D;
485   UnicodeLower0100_024E[$022E]:=$022F;
486   UnicodeLower0100_024E[$0230]:=$0231;
487   UnicodeLower0100_024E[$0232]:=$0233;
488   UnicodeLower0100_024E[$023A]:=$2C65;
489   UnicodeLower0100_024E[$023B]:=$023C;
490   UnicodeLower0100_024E[$023D]:=$019A;
491   UnicodeLower0100_024E[$023E]:=$2C66;
492   UnicodeLower0100_024E[$0241]:=$0242;
493   UnicodeLower0100_024E[$0243]:=$0180;
494   UnicodeLower0100_024E[$0244]:=$0289;
495   UnicodeLower0100_024E[$0245]:=$028C;
496   UnicodeLower0100_024E[$0246]:=$0247;
497   UnicodeLower0100_024E[$0248]:=$0249;
498   UnicodeLower0100_024E[$024A]:=$024B;
499   UnicodeLower0100_024E[$024C]:=$024D;
500   UnicodeLower0100_024E[$024E]:=$024F;
501 
502   for i:=Low(UnicodeLower0386_03AB) to High(UnicodeLower0386_03AB) do
503     UnicodeLower0386_03AB[i]:=i;
504   UnicodeLower0386_03AB[$0386]:=$03AC;
505   UnicodeLower0386_03AB[$0388]:=$03AD;
506   UnicodeLower0386_03AB[$0389]:=$03AE;
507   UnicodeLower0386_03AB[$038A]:=$03AF;
508   UnicodeLower0386_03AB[$038C]:=$03CC;
509   UnicodeLower0386_03AB[$038E]:=$03CD;
510   UnicodeLower0386_03AB[$038F]:=$03CE;
511   UnicodeLower0386_03AB[$0391]:=$03B1;
512   UnicodeLower0386_03AB[$0392]:=$03B2;
513   UnicodeLower0386_03AB[$0393]:=$03B3;
514   UnicodeLower0386_03AB[$0394]:=$03B4;
515   UnicodeLower0386_03AB[$0395]:=$03B5;
516   UnicodeLower0386_03AB[$0396]:=$03B6;
517   UnicodeLower0386_03AB[$0397]:=$03B7;
518   UnicodeLower0386_03AB[$0398]:=$03B8;
519   UnicodeLower0386_03AB[$0399]:=$03B9;
520   UnicodeLower0386_03AB[$039A]:=$03BA;
521   UnicodeLower0386_03AB[$039B]:=$03BB;
522   UnicodeLower0386_03AB[$039C]:=$03BC;
523   UnicodeLower0386_03AB[$039D]:=$03BD;
524   UnicodeLower0386_03AB[$039E]:=$03BE;
525   UnicodeLower0386_03AB[$039F]:=$03BF;
526   UnicodeLower0386_03AB[$03A0]:=$03C0;
527   UnicodeLower0386_03AB[$03A1]:=$03C1;
528   UnicodeLower0386_03AB[$03A3]:=$03C3;
529   UnicodeLower0386_03AB[$03A4]:=$03C4;
530   UnicodeLower0386_03AB[$03A5]:=$03C5;
531   UnicodeLower0386_03AB[$03A6]:=$03C6;
532   UnicodeLower0386_03AB[$03A7]:=$03C7;
533   UnicodeLower0386_03AB[$03A8]:=$03C8;
534   UnicodeLower0386_03AB[$03A9]:=$03C9;
535   UnicodeLower0386_03AB[$03AA]:=$03CA;
536   UnicodeLower0386_03AB[$03AB]:=$03CB;
537 
538   for i:=Low(UnicodeLower03D8_042F) to High(UnicodeLower03D8_042F) do
539     UnicodeLower03D8_042F[i]:=i;
540   UnicodeLower03D8_042F[$03D8]:=$03D9;
541   UnicodeLower03D8_042F[$03DA]:=$03DB;
542   UnicodeLower03D8_042F[$03DC]:=$03DD;
543   UnicodeLower03D8_042F[$03DE]:=$03DF;
544   UnicodeLower03D8_042F[$03E0]:=$03E1;
545   UnicodeLower03D8_042F[$03E2]:=$03E3;
546   UnicodeLower03D8_042F[$03E4]:=$03E5;
547   UnicodeLower03D8_042F[$03E6]:=$03E7;
548   UnicodeLower03D8_042F[$03E8]:=$03E9;
549   UnicodeLower03D8_042F[$03EA]:=$03EB;
550   UnicodeLower03D8_042F[$03EC]:=$03ED;
551   UnicodeLower03D8_042F[$03EE]:=$03EF;
552   UnicodeLower03D8_042F[$03F4]:=$03B8;
553   UnicodeLower03D8_042F[$03F7]:=$03F8;
554   UnicodeLower03D8_042F[$03F9]:=$03F2;
555   UnicodeLower03D8_042F[$03FA]:=$03FB;
556   UnicodeLower03D8_042F[$03FD]:=$037B;
557   UnicodeLower03D8_042F[$03FE]:=$037C;
558   UnicodeLower03D8_042F[$03FF]:=$037D;
559   UnicodeLower03D8_042F[$0400]:=$0450;
560   UnicodeLower03D8_042F[$0401]:=$0451;
561   UnicodeLower03D8_042F[$0402]:=$0452;
562   UnicodeLower03D8_042F[$0403]:=$0453;
563   UnicodeLower03D8_042F[$0404]:=$0454;
564   UnicodeLower03D8_042F[$0405]:=$0455;
565   UnicodeLower03D8_042F[$0406]:=$0456;
566   UnicodeLower03D8_042F[$0407]:=$0457;
567   UnicodeLower03D8_042F[$0408]:=$0458;
568   UnicodeLower03D8_042F[$0409]:=$0459;
569   UnicodeLower03D8_042F[$040A]:=$045A;
570   UnicodeLower03D8_042F[$040B]:=$045B;
571   UnicodeLower03D8_042F[$040C]:=$045C;
572   UnicodeLower03D8_042F[$040D]:=$045D;
573   UnicodeLower03D8_042F[$040E]:=$045E;
574   UnicodeLower03D8_042F[$040F]:=$045F;
575   UnicodeLower03D8_042F[$0410]:=$0430;
576   UnicodeLower03D8_042F[$0411]:=$0431;
577   UnicodeLower03D8_042F[$0412]:=$0432;
578   UnicodeLower03D8_042F[$0413]:=$0433;
579   UnicodeLower03D8_042F[$0414]:=$0434;
580   UnicodeLower03D8_042F[$0415]:=$0435;
581   UnicodeLower03D8_042F[$0416]:=$0436;
582   UnicodeLower03D8_042F[$0417]:=$0437;
583   UnicodeLower03D8_042F[$0418]:=$0438;
584   UnicodeLower03D8_042F[$0419]:=$0439;
585   UnicodeLower03D8_042F[$041A]:=$043A;
586   UnicodeLower03D8_042F[$041B]:=$043B;
587   UnicodeLower03D8_042F[$041C]:=$043C;
588   UnicodeLower03D8_042F[$041D]:=$043D;
589   UnicodeLower03D8_042F[$041E]:=$043E;
590   UnicodeLower03D8_042F[$041F]:=$043F;
591   UnicodeLower03D8_042F[$0420]:=$0440;
592   UnicodeLower03D8_042F[$0421]:=$0441;
593   UnicodeLower03D8_042F[$0422]:=$0442;
594   UnicodeLower03D8_042F[$0423]:=$0443;
595   UnicodeLower03D8_042F[$0424]:=$0444;
596   UnicodeLower03D8_042F[$0425]:=$0445;
597   UnicodeLower03D8_042F[$0426]:=$0446;
598   UnicodeLower03D8_042F[$0427]:=$0447;
599   UnicodeLower03D8_042F[$0428]:=$0448;
600   UnicodeLower03D8_042F[$0429]:=$0449;
601   UnicodeLower03D8_042F[$042A]:=$044A;
602   UnicodeLower03D8_042F[$042B]:=$044B;
603   UnicodeLower03D8_042F[$042C]:=$044C;
604   UnicodeLower03D8_042F[$042D]:=$044D;
605   UnicodeLower03D8_042F[$042E]:=$044E;
606   UnicodeLower03D8_042F[$042F]:=$044F;
607 
608   for i:=Low(UnicodeLower0460_0512) to High(UnicodeLower0460_0512) do
609     UnicodeLower0460_0512[i]:=i;
610   UnicodeLower0460_0512[$0460]:=$0461;
611   UnicodeLower0460_0512[$0462]:=$0463;
612   UnicodeLower0460_0512[$0464]:=$0465;
613   UnicodeLower0460_0512[$0466]:=$0467;
614   UnicodeLower0460_0512[$0468]:=$0469;
615   UnicodeLower0460_0512[$046A]:=$046B;
616   UnicodeLower0460_0512[$046C]:=$046D;
617   UnicodeLower0460_0512[$046E]:=$046F;
618   UnicodeLower0460_0512[$0470]:=$0471;
619   UnicodeLower0460_0512[$0472]:=$0473;
620   UnicodeLower0460_0512[$0474]:=$0475;
621   UnicodeLower0460_0512[$0476]:=$0477;
622   UnicodeLower0460_0512[$0478]:=$0479;
623   UnicodeLower0460_0512[$047A]:=$047B;
624   UnicodeLower0460_0512[$047C]:=$047D;
625   UnicodeLower0460_0512[$047E]:=$047F;
626   UnicodeLower0460_0512[$0480]:=$0481;
627   UnicodeLower0460_0512[$048A]:=$048B;
628   UnicodeLower0460_0512[$048C]:=$048D;
629   UnicodeLower0460_0512[$048E]:=$048F;
630   UnicodeLower0460_0512[$0490]:=$0491;
631   UnicodeLower0460_0512[$0492]:=$0493;
632   UnicodeLower0460_0512[$0494]:=$0495;
633   UnicodeLower0460_0512[$0496]:=$0497;
634   UnicodeLower0460_0512[$0498]:=$0499;
635   UnicodeLower0460_0512[$049A]:=$049B;
636   UnicodeLower0460_0512[$049C]:=$049D;
637   UnicodeLower0460_0512[$049E]:=$049F;
638   UnicodeLower0460_0512[$04A0]:=$04A1;
639   UnicodeLower0460_0512[$04A2]:=$04A3;
640   UnicodeLower0460_0512[$04A4]:=$04A5;
641   UnicodeLower0460_0512[$04A6]:=$04A7;
642   UnicodeLower0460_0512[$04A8]:=$04A9;
643   UnicodeLower0460_0512[$04AA]:=$04AB;
644   UnicodeLower0460_0512[$04AC]:=$04AD;
645   UnicodeLower0460_0512[$04AE]:=$04AF;
646   UnicodeLower0460_0512[$04B0]:=$04B1;
647   UnicodeLower0460_0512[$04B2]:=$04B3;
648   UnicodeLower0460_0512[$04B4]:=$04B5;
649   UnicodeLower0460_0512[$04B6]:=$04B7;
650   UnicodeLower0460_0512[$04B8]:=$04B9;
651   UnicodeLower0460_0512[$04BA]:=$04BB;
652   UnicodeLower0460_0512[$04BC]:=$04BD;
653   UnicodeLower0460_0512[$04BE]:=$04BF;
654   UnicodeLower0460_0512[$04C0]:=$04CF;
655   UnicodeLower0460_0512[$04C1]:=$04C2;
656   UnicodeLower0460_0512[$04C3]:=$04C4;
657   UnicodeLower0460_0512[$04C5]:=$04C6;
658   UnicodeLower0460_0512[$04C7]:=$04C8;
659   UnicodeLower0460_0512[$04C9]:=$04CA;
660   UnicodeLower0460_0512[$04CB]:=$04CC;
661   UnicodeLower0460_0512[$04CD]:=$04CE;
662   UnicodeLower0460_0512[$04D0]:=$04D1;
663   UnicodeLower0460_0512[$04D2]:=$04D3;
664   UnicodeLower0460_0512[$04D4]:=$04D5;
665   UnicodeLower0460_0512[$04D6]:=$04D7;
666   UnicodeLower0460_0512[$04D8]:=$04D9;
667   UnicodeLower0460_0512[$04DA]:=$04DB;
668   UnicodeLower0460_0512[$04DC]:=$04DD;
669   UnicodeLower0460_0512[$04DE]:=$04DF;
670   UnicodeLower0460_0512[$04E0]:=$04E1;
671   UnicodeLower0460_0512[$04E2]:=$04E3;
672   UnicodeLower0460_0512[$04E4]:=$04E5;
673   UnicodeLower0460_0512[$04E6]:=$04E7;
674   UnicodeLower0460_0512[$04E8]:=$04E9;
675   UnicodeLower0460_0512[$04EA]:=$04EB;
676   UnicodeLower0460_0512[$04EC]:=$04ED;
677   UnicodeLower0460_0512[$04EE]:=$04EF;
678   UnicodeLower0460_0512[$04F0]:=$04F1;
679   UnicodeLower0460_0512[$04F2]:=$04F3;
680   UnicodeLower0460_0512[$04F4]:=$04F5;
681   UnicodeLower0460_0512[$04F6]:=$04F7;
682   UnicodeLower0460_0512[$04F8]:=$04F9;
683   UnicodeLower0460_0512[$04FA]:=$04FB;
684   UnicodeLower0460_0512[$04FC]:=$04FD;
685   UnicodeLower0460_0512[$04FE]:=$04FF;
686   UnicodeLower0460_0512[$0500]:=$0501;
687   UnicodeLower0460_0512[$0502]:=$0503;
688   UnicodeLower0460_0512[$0504]:=$0505;
689   UnicodeLower0460_0512[$0506]:=$0507;
690   UnicodeLower0460_0512[$0508]:=$0509;
691   UnicodeLower0460_0512[$050A]:=$050B;
692   UnicodeLower0460_0512[$050C]:=$050D;
693   UnicodeLower0460_0512[$050E]:=$050F;
694   UnicodeLower0460_0512[$0510]:=$0511;
695   UnicodeLower0460_0512[$0512]:=$0513;
696 
697   for i:=Low(UnicodeLower1E00_1FFC) to High(UnicodeLower1E00_1FFC) do
698     UnicodeLower1E00_1FFC[i]:=i;
699   UnicodeLower1E00_1FFC[$1E00]:=$1E01;
700   UnicodeLower1E00_1FFC[$1E02]:=$1E03;
701   UnicodeLower1E00_1FFC[$1E04]:=$1E05;
702   UnicodeLower1E00_1FFC[$1E06]:=$1E07;
703   UnicodeLower1E00_1FFC[$1E08]:=$1E09;
704   UnicodeLower1E00_1FFC[$1E0A]:=$1E0B;
705   UnicodeLower1E00_1FFC[$1E0C]:=$1E0D;
706   UnicodeLower1E00_1FFC[$1E0E]:=$1E0F;
707   UnicodeLower1E00_1FFC[$1E10]:=$1E11;
708   UnicodeLower1E00_1FFC[$1E12]:=$1E13;
709   UnicodeLower1E00_1FFC[$1E14]:=$1E15;
710   UnicodeLower1E00_1FFC[$1E16]:=$1E17;
711   UnicodeLower1E00_1FFC[$1E18]:=$1E19;
712   UnicodeLower1E00_1FFC[$1E1A]:=$1E1B;
713   UnicodeLower1E00_1FFC[$1E1C]:=$1E1D;
714   UnicodeLower1E00_1FFC[$1E1E]:=$1E1F;
715   UnicodeLower1E00_1FFC[$1E20]:=$1E21;
716   UnicodeLower1E00_1FFC[$1E22]:=$1E23;
717   UnicodeLower1E00_1FFC[$1E24]:=$1E25;
718   UnicodeLower1E00_1FFC[$1E26]:=$1E27;
719   UnicodeLower1E00_1FFC[$1E28]:=$1E29;
720   UnicodeLower1E00_1FFC[$1E2A]:=$1E2B;
721   UnicodeLower1E00_1FFC[$1E2C]:=$1E2D;
722   UnicodeLower1E00_1FFC[$1E2E]:=$1E2F;
723   UnicodeLower1E00_1FFC[$1E30]:=$1E31;
724   UnicodeLower1E00_1FFC[$1E32]:=$1E33;
725   UnicodeLower1E00_1FFC[$1E34]:=$1E35;
726   UnicodeLower1E00_1FFC[$1E36]:=$1E37;
727   UnicodeLower1E00_1FFC[$1E38]:=$1E39;
728   UnicodeLower1E00_1FFC[$1E3A]:=$1E3B;
729   UnicodeLower1E00_1FFC[$1E3C]:=$1E3D;
730   UnicodeLower1E00_1FFC[$1E3E]:=$1E3F;
731   UnicodeLower1E00_1FFC[$1E40]:=$1E41;
732   UnicodeLower1E00_1FFC[$1E42]:=$1E43;
733   UnicodeLower1E00_1FFC[$1E44]:=$1E45;
734   UnicodeLower1E00_1FFC[$1E46]:=$1E47;
735   UnicodeLower1E00_1FFC[$1E48]:=$1E49;
736   UnicodeLower1E00_1FFC[$1E4A]:=$1E4B;
737   UnicodeLower1E00_1FFC[$1E4C]:=$1E4D;
738   UnicodeLower1E00_1FFC[$1E4E]:=$1E4F;
739   UnicodeLower1E00_1FFC[$1E50]:=$1E51;
740   UnicodeLower1E00_1FFC[$1E52]:=$1E53;
741   UnicodeLower1E00_1FFC[$1E54]:=$1E55;
742   UnicodeLower1E00_1FFC[$1E56]:=$1E57;
743   UnicodeLower1E00_1FFC[$1E58]:=$1E59;
744   UnicodeLower1E00_1FFC[$1E5A]:=$1E5B;
745   UnicodeLower1E00_1FFC[$1E5C]:=$1E5D;
746   UnicodeLower1E00_1FFC[$1E5E]:=$1E5F;
747   UnicodeLower1E00_1FFC[$1E60]:=$1E61;
748   UnicodeLower1E00_1FFC[$1E62]:=$1E63;
749   UnicodeLower1E00_1FFC[$1E64]:=$1E65;
750   UnicodeLower1E00_1FFC[$1E66]:=$1E67;
751   UnicodeLower1E00_1FFC[$1E68]:=$1E69;
752   UnicodeLower1E00_1FFC[$1E6A]:=$1E6B;
753   UnicodeLower1E00_1FFC[$1E6C]:=$1E6D;
754   UnicodeLower1E00_1FFC[$1E6E]:=$1E6F;
755   UnicodeLower1E00_1FFC[$1E70]:=$1E71;
756   UnicodeLower1E00_1FFC[$1E72]:=$1E73;
757   UnicodeLower1E00_1FFC[$1E74]:=$1E75;
758   UnicodeLower1E00_1FFC[$1E76]:=$1E77;
759   UnicodeLower1E00_1FFC[$1E78]:=$1E79;
760   UnicodeLower1E00_1FFC[$1E7A]:=$1E7B;
761   UnicodeLower1E00_1FFC[$1E7C]:=$1E7D;
762   UnicodeLower1E00_1FFC[$1E7E]:=$1E7F;
763   UnicodeLower1E00_1FFC[$1E80]:=$1E81;
764   UnicodeLower1E00_1FFC[$1E82]:=$1E83;
765   UnicodeLower1E00_1FFC[$1E84]:=$1E85;
766   UnicodeLower1E00_1FFC[$1E86]:=$1E87;
767   UnicodeLower1E00_1FFC[$1E88]:=$1E89;
768   UnicodeLower1E00_1FFC[$1E8A]:=$1E8B;
769   UnicodeLower1E00_1FFC[$1E8C]:=$1E8D;
770   UnicodeLower1E00_1FFC[$1E8E]:=$1E8F;
771   UnicodeLower1E00_1FFC[$1E90]:=$1E91;
772   UnicodeLower1E00_1FFC[$1E92]:=$1E93;
773   UnicodeLower1E00_1FFC[$1E94]:=$1E95;
774   UnicodeLower1E00_1FFC[$1EA0]:=$1EA1;
775   UnicodeLower1E00_1FFC[$1EA2]:=$1EA3;
776   UnicodeLower1E00_1FFC[$1EA4]:=$1EA5;
777   UnicodeLower1E00_1FFC[$1EA6]:=$1EA7;
778   UnicodeLower1E00_1FFC[$1EA8]:=$1EA9;
779   UnicodeLower1E00_1FFC[$1EAA]:=$1EAB;
780   UnicodeLower1E00_1FFC[$1EAC]:=$1EAD;
781   UnicodeLower1E00_1FFC[$1EAE]:=$1EAF;
782   UnicodeLower1E00_1FFC[$1EB0]:=$1EB1;
783   UnicodeLower1E00_1FFC[$1EB2]:=$1EB3;
784   UnicodeLower1E00_1FFC[$1EB4]:=$1EB5;
785   UnicodeLower1E00_1FFC[$1EB6]:=$1EB7;
786   UnicodeLower1E00_1FFC[$1EB8]:=$1EB9;
787   UnicodeLower1E00_1FFC[$1EBA]:=$1EBB;
788   UnicodeLower1E00_1FFC[$1EBC]:=$1EBD;
789   UnicodeLower1E00_1FFC[$1EBE]:=$1EBF;
790   UnicodeLower1E00_1FFC[$1EC0]:=$1EC1;
791   UnicodeLower1E00_1FFC[$1EC2]:=$1EC3;
792   UnicodeLower1E00_1FFC[$1EC4]:=$1EC5;
793   UnicodeLower1E00_1FFC[$1EC6]:=$1EC7;
794   UnicodeLower1E00_1FFC[$1EC8]:=$1EC9;
795   UnicodeLower1E00_1FFC[$1ECA]:=$1ECB;
796   UnicodeLower1E00_1FFC[$1ECC]:=$1ECD;
797   UnicodeLower1E00_1FFC[$1ECE]:=$1ECF;
798   UnicodeLower1E00_1FFC[$1ED0]:=$1ED1;
799   UnicodeLower1E00_1FFC[$1ED2]:=$1ED3;
800   UnicodeLower1E00_1FFC[$1ED4]:=$1ED5;
801   UnicodeLower1E00_1FFC[$1ED6]:=$1ED7;
802   UnicodeLower1E00_1FFC[$1ED8]:=$1ED9;
803   UnicodeLower1E00_1FFC[$1EDA]:=$1EDB;
804   UnicodeLower1E00_1FFC[$1EDC]:=$1EDD;
805   UnicodeLower1E00_1FFC[$1EDE]:=$1EDF;
806   UnicodeLower1E00_1FFC[$1EE0]:=$1EE1;
807   UnicodeLower1E00_1FFC[$1EE2]:=$1EE3;
808   UnicodeLower1E00_1FFC[$1EE4]:=$1EE5;
809   UnicodeLower1E00_1FFC[$1EE6]:=$1EE7;
810   UnicodeLower1E00_1FFC[$1EE8]:=$1EE9;
811   UnicodeLower1E00_1FFC[$1EEA]:=$1EEB;
812   UnicodeLower1E00_1FFC[$1EEC]:=$1EED;
813   UnicodeLower1E00_1FFC[$1EEE]:=$1EEF;
814   UnicodeLower1E00_1FFC[$1EF0]:=$1EF1;
815   UnicodeLower1E00_1FFC[$1EF2]:=$1EF3;
816   UnicodeLower1E00_1FFC[$1EF4]:=$1EF5;
817   UnicodeLower1E00_1FFC[$1EF6]:=$1EF7;
818   UnicodeLower1E00_1FFC[$1EF8]:=$1EF9;
819   UnicodeLower1E00_1FFC[$1F08]:=$1F00;
820   UnicodeLower1E00_1FFC[$1F09]:=$1F01;
821   UnicodeLower1E00_1FFC[$1F0A]:=$1F02;
822   UnicodeLower1E00_1FFC[$1F0B]:=$1F03;
823   UnicodeLower1E00_1FFC[$1F0C]:=$1F04;
824   UnicodeLower1E00_1FFC[$1F0D]:=$1F05;
825   UnicodeLower1E00_1FFC[$1F0E]:=$1F06;
826   UnicodeLower1E00_1FFC[$1F0F]:=$1F07;
827   UnicodeLower1E00_1FFC[$1F18]:=$1F10;
828   UnicodeLower1E00_1FFC[$1F19]:=$1F11;
829   UnicodeLower1E00_1FFC[$1F1A]:=$1F12;
830   UnicodeLower1E00_1FFC[$1F1B]:=$1F13;
831   UnicodeLower1E00_1FFC[$1F1C]:=$1F14;
832   UnicodeLower1E00_1FFC[$1F1D]:=$1F15;
833   UnicodeLower1E00_1FFC[$1F28]:=$1F20;
834   UnicodeLower1E00_1FFC[$1F29]:=$1F21;
835   UnicodeLower1E00_1FFC[$1F2A]:=$1F22;
836   UnicodeLower1E00_1FFC[$1F2B]:=$1F23;
837   UnicodeLower1E00_1FFC[$1F2C]:=$1F24;
838   UnicodeLower1E00_1FFC[$1F2D]:=$1F25;
839   UnicodeLower1E00_1FFC[$1F2E]:=$1F26;
840   UnicodeLower1E00_1FFC[$1F2F]:=$1F27;
841   UnicodeLower1E00_1FFC[$1F38]:=$1F30;
842   UnicodeLower1E00_1FFC[$1F39]:=$1F31;
843   UnicodeLower1E00_1FFC[$1F3A]:=$1F32;
844   UnicodeLower1E00_1FFC[$1F3B]:=$1F33;
845   UnicodeLower1E00_1FFC[$1F3C]:=$1F34;
846   UnicodeLower1E00_1FFC[$1F3D]:=$1F35;
847   UnicodeLower1E00_1FFC[$1F3E]:=$1F36;
848   UnicodeLower1E00_1FFC[$1F3F]:=$1F37;
849   UnicodeLower1E00_1FFC[$1F48]:=$1F40;
850   UnicodeLower1E00_1FFC[$1F49]:=$1F41;
851   UnicodeLower1E00_1FFC[$1F4A]:=$1F42;
852   UnicodeLower1E00_1FFC[$1F4B]:=$1F43;
853   UnicodeLower1E00_1FFC[$1F4C]:=$1F44;
854   UnicodeLower1E00_1FFC[$1F4D]:=$1F45;
855   UnicodeLower1E00_1FFC[$1F59]:=$1F51;
856   UnicodeLower1E00_1FFC[$1F5B]:=$1F53;
857   UnicodeLower1E00_1FFC[$1F5D]:=$1F55;
858   UnicodeLower1E00_1FFC[$1F5F]:=$1F57;
859   UnicodeLower1E00_1FFC[$1F68]:=$1F60;
860   UnicodeLower1E00_1FFC[$1F69]:=$1F61;
861   UnicodeLower1E00_1FFC[$1F6A]:=$1F62;
862   UnicodeLower1E00_1FFC[$1F6B]:=$1F63;
863   UnicodeLower1E00_1FFC[$1F6C]:=$1F64;
864   UnicodeLower1E00_1FFC[$1F6D]:=$1F65;
865   UnicodeLower1E00_1FFC[$1F6E]:=$1F66;
866   UnicodeLower1E00_1FFC[$1F6F]:=$1F67;
867   UnicodeLower1E00_1FFC[$1F88]:=$1F80;
868   UnicodeLower1E00_1FFC[$1F89]:=$1F81;
869   UnicodeLower1E00_1FFC[$1F8A]:=$1F82;
870   UnicodeLower1E00_1FFC[$1F8B]:=$1F83;
871   UnicodeLower1E00_1FFC[$1F8C]:=$1F84;
872   UnicodeLower1E00_1FFC[$1F8D]:=$1F85;
873   UnicodeLower1E00_1FFC[$1F8E]:=$1F86;
874   UnicodeLower1E00_1FFC[$1F8F]:=$1F87;
875   UnicodeLower1E00_1FFC[$1F98]:=$1F90;
876   UnicodeLower1E00_1FFC[$1F99]:=$1F91;
877   UnicodeLower1E00_1FFC[$1F9A]:=$1F92;
878   UnicodeLower1E00_1FFC[$1F9B]:=$1F93;
879   UnicodeLower1E00_1FFC[$1F9C]:=$1F94;
880   UnicodeLower1E00_1FFC[$1F9D]:=$1F95;
881   UnicodeLower1E00_1FFC[$1F9E]:=$1F96;
882   UnicodeLower1E00_1FFC[$1F9F]:=$1F97;
883   UnicodeLower1E00_1FFC[$1FA8]:=$1FA0;
884   UnicodeLower1E00_1FFC[$1FA9]:=$1FA1;
885   UnicodeLower1E00_1FFC[$1FAA]:=$1FA2;
886   UnicodeLower1E00_1FFC[$1FAB]:=$1FA3;
887   UnicodeLower1E00_1FFC[$1FAC]:=$1FA4;
888   UnicodeLower1E00_1FFC[$1FAD]:=$1FA5;
889   UnicodeLower1E00_1FFC[$1FAE]:=$1FA6;
890   UnicodeLower1E00_1FFC[$1FAF]:=$1FA7;
891   UnicodeLower1E00_1FFC[$1FB8]:=$1FB0;
892   UnicodeLower1E00_1FFC[$1FB9]:=$1FB1;
893   UnicodeLower1E00_1FFC[$1FBA]:=$1F70;
894   UnicodeLower1E00_1FFC[$1FBB]:=$1F71;
895   UnicodeLower1E00_1FFC[$1FBC]:=$1FB3;
896   UnicodeLower1E00_1FFC[$1FC8]:=$1F72;
897   UnicodeLower1E00_1FFC[$1FC9]:=$1F73;
898   UnicodeLower1E00_1FFC[$1FCA]:=$1F74;
899   UnicodeLower1E00_1FFC[$1FCB]:=$1F75;
900   UnicodeLower1E00_1FFC[$1FCC]:=$1FC3;
901   UnicodeLower1E00_1FFC[$1FD8]:=$1FD0;
902   UnicodeLower1E00_1FFC[$1FD9]:=$1FD1;
903   UnicodeLower1E00_1FFC[$1FDA]:=$1F76;
904   UnicodeLower1E00_1FFC[$1FDB]:=$1F77;
905   UnicodeLower1E00_1FFC[$1FE8]:=$1FE0;
906   UnicodeLower1E00_1FFC[$1FE9]:=$1FE1;
907   UnicodeLower1E00_1FFC[$1FEA]:=$1F7A;
908   UnicodeLower1E00_1FFC[$1FEB]:=$1F7B;
909   UnicodeLower1E00_1FFC[$1FEC]:=$1FE5;
910   UnicodeLower1E00_1FFC[$1FF8]:=$1F78;
911   UnicodeLower1E00_1FFC[$1FF9]:=$1F79;
912   UnicodeLower1E00_1FFC[$1FFA]:=$1F7C;
913   UnicodeLower1E00_1FFC[$1FFB]:=$1F7D;
914   UnicodeLower1E00_1FFC[$1FFC]:=$1FF3;
915 
916   for i:=Low(UnicodeLower2126_2183) to High(UnicodeLower2126_2183) do
917     UnicodeLower2126_2183[i]:=i;
918   UnicodeLower2126_2183[$2126]:=$03C9;
919   UnicodeLower2126_2183[$212A]:=$006B;
920   UnicodeLower2126_2183[$212B]:=$00E5;
921   UnicodeLower2126_2183[$2132]:=$214E;
922   UnicodeLower2126_2183[$2160]:=$2170;
923   UnicodeLower2126_2183[$2161]:=$2171;
924   UnicodeLower2126_2183[$2162]:=$2172;
925   UnicodeLower2126_2183[$2163]:=$2173;
926   UnicodeLower2126_2183[$2164]:=$2174;
927   UnicodeLower2126_2183[$2165]:=$2175;
928   UnicodeLower2126_2183[$2166]:=$2176;
929   UnicodeLower2126_2183[$2167]:=$2177;
930   UnicodeLower2126_2183[$2168]:=$2178;
931   UnicodeLower2126_2183[$2169]:=$2179;
932   UnicodeLower2126_2183[$216A]:=$217A;
933   UnicodeLower2126_2183[$216B]:=$217B;
934   UnicodeLower2126_2183[$216C]:=$217C;
935   UnicodeLower2126_2183[$216D]:=$217D;
936   UnicodeLower2126_2183[$216E]:=$217E;
937   UnicodeLower2126_2183[$216F]:=$217F;
938   UnicodeLower2126_2183[$2183]:=$2184;
939 
940   for i:=Low(UnicodeLower2C60_2CE2) to High(UnicodeLower2C60_2CE2) do
941     UnicodeLower2C60_2CE2[i]:=i;
942   UnicodeLower2C60_2CE2[$2C60]:=$2C61;
943   UnicodeLower2C60_2CE2[$2C62]:=$026B;
944   UnicodeLower2C60_2CE2[$2C63]:=$1D7D;
945   UnicodeLower2C60_2CE2[$2C64]:=$027D;
946   UnicodeLower2C60_2CE2[$2C67]:=$2C68;
947   UnicodeLower2C60_2CE2[$2C69]:=$2C6A;
948   UnicodeLower2C60_2CE2[$2C6B]:=$2C6C;
949   UnicodeLower2C60_2CE2[$2C75]:=$2C76;
950   UnicodeLower2C60_2CE2[$2C80]:=$2C81;
951   UnicodeLower2C60_2CE2[$2C82]:=$2C83;
952   UnicodeLower2C60_2CE2[$2C84]:=$2C85;
953   UnicodeLower2C60_2CE2[$2C86]:=$2C87;
954   UnicodeLower2C60_2CE2[$2C88]:=$2C89;
955   UnicodeLower2C60_2CE2[$2C8A]:=$2C8B;
956   UnicodeLower2C60_2CE2[$2C8C]:=$2C8D;
957   UnicodeLower2C60_2CE2[$2C8E]:=$2C8F;
958   UnicodeLower2C60_2CE2[$2C90]:=$2C91;
959   UnicodeLower2C60_2CE2[$2C92]:=$2C93;
960   UnicodeLower2C60_2CE2[$2C94]:=$2C95;
961   UnicodeLower2C60_2CE2[$2C96]:=$2C97;
962   UnicodeLower2C60_2CE2[$2C98]:=$2C99;
963   UnicodeLower2C60_2CE2[$2C9A]:=$2C9B;
964   UnicodeLower2C60_2CE2[$2C9C]:=$2C9D;
965   UnicodeLower2C60_2CE2[$2C9E]:=$2C9F;
966   UnicodeLower2C60_2CE2[$2CA0]:=$2CA1;
967   UnicodeLower2C60_2CE2[$2CA2]:=$2CA3;
968   UnicodeLower2C60_2CE2[$2CA4]:=$2CA5;
969   UnicodeLower2C60_2CE2[$2CA6]:=$2CA7;
970   UnicodeLower2C60_2CE2[$2CA8]:=$2CA9;
971   UnicodeLower2C60_2CE2[$2CAA]:=$2CAB;
972   UnicodeLower2C60_2CE2[$2CAC]:=$2CAD;
973   UnicodeLower2C60_2CE2[$2CAE]:=$2CAF;
974   UnicodeLower2C60_2CE2[$2CB0]:=$2CB1;
975   UnicodeLower2C60_2CE2[$2CB2]:=$2CB3;
976   UnicodeLower2C60_2CE2[$2CB4]:=$2CB5;
977   UnicodeLower2C60_2CE2[$2CB6]:=$2CB7;
978   UnicodeLower2C60_2CE2[$2CB8]:=$2CB9;
979   UnicodeLower2C60_2CE2[$2CBA]:=$2CBB;
980   UnicodeLower2C60_2CE2[$2CBC]:=$2CBD;
981   UnicodeLower2C60_2CE2[$2CBE]:=$2CBF;
982   UnicodeLower2C60_2CE2[$2CC0]:=$2CC1;
983   UnicodeLower2C60_2CE2[$2CC2]:=$2CC3;
984   UnicodeLower2C60_2CE2[$2CC4]:=$2CC5;
985   UnicodeLower2C60_2CE2[$2CC6]:=$2CC7;
986   UnicodeLower2C60_2CE2[$2CC8]:=$2CC9;
987   UnicodeLower2C60_2CE2[$2CCA]:=$2CCB;
988   UnicodeLower2C60_2CE2[$2CCC]:=$2CCD;
989   UnicodeLower2C60_2CE2[$2CCE]:=$2CCF;
990   UnicodeLower2C60_2CE2[$2CD0]:=$2CD1;
991   UnicodeLower2C60_2CE2[$2CD2]:=$2CD3;
992   UnicodeLower2C60_2CE2[$2CD4]:=$2CD5;
993   UnicodeLower2C60_2CE2[$2CD6]:=$2CD7;
994   UnicodeLower2C60_2CE2[$2CD8]:=$2CD9;
995   UnicodeLower2C60_2CE2[$2CDA]:=$2CDB;
996   UnicodeLower2C60_2CE2[$2CDC]:=$2CDD;
997   UnicodeLower2C60_2CE2[$2CDE]:=$2CDF;
998   UnicodeLower2C60_2CE2[$2CE0]:=$2CE1;
999   UnicodeLower2C60_2CE2[$2CE2]:=$2CE3;
1000 end;
1001 
1002 function UnicodeLowercase(u: cardinal): cardinal;
1003 begin
1004   if u<$00C0 then begin
1005     // most common
1006     if (u>=$0041) and (u<=$0061) then
1007       Result:=u+32
1008     else
1009       Result:=u;
1010   end else
1011     case u of
1012     $00C0..$00DE: Result:=UnicodeLower00C0_00DE[u];
1013     $0100..$024E: Result:=UnicodeLower0100_024E[u];
1014     $0386..$03AB: Result:=UnicodeLower0386_03AB[u];
1015     $03D8..$042F: Result:=UnicodeLower03D8_042F[u];
1016     $0460..$0512: Result:=UnicodeLower0460_0512[u];
1017     $0531..$0556: Result:=u+48;
1018     $10A0..$10C5: Result:=u+7264;
1019     $1E00..$1FFC: Result:=UnicodeLower1E00_1FFC[u];
1020     $2126..$2183: Result:=UnicodeLower2126_2183[u];
1021     $24B6..$24CF: Result:=u+26;
1022     $2C00..$2C2E: Result:=u+48;
1023     $2C60..$2CE2: Result:=UnicodeLower2C60_2CE2[u];
1024     $FF21..$FF3A: Result:=u+32;
1025     else          Result:=u;
1026   end;
1027 end;
1028 
1029 {$IFDEF FPC}
1030 function UTF8LowercaseDynLength(const s: string): string;
1031 var
1032   Buf: shortstring;
1033   SrcPos: PtrInt;
1034   DstPos: PtrInt;
1035   CharLen: integer;
1036   OldCode: LongWord;
1037   NewCode: LongWord;
1038 begin
1039   // first compute needed length
1040   SrcPos:=1;
1041   DstPos:=1;
1042   while SrcPos<=length(s) do begin
1043     case s[SrcPos] of
1044     #192..#240:
1045       begin
1046         OldCode:=UTF8CodepointToUnicode(@s[SrcPos],CharLen);
1047         NewCode:=UnicodeLowercase(OldCode);
1048         if NewCode=OldCode then begin
1049           inc(DstPos,CharLen);
1050         end else begin
1051           inc(DstPos,UnicodeToUTF8(NewCode,@Buf[1]));
1052         end;
1053         inc(SrcPos,CharLen);
1054       end;
1055     else
1056       inc(SrcPos);
1057       inc(DstPos);
1058     end;
1059   end;
1060   SetLength(Result{%H-},DstPos-1);
1061   if Result='' then exit;
1062   // create the new string
1063   SrcPos:=1;
1064   DstPos:=1;
1065   while SrcPos<=length(s) do begin
1066     case s[SrcPos] of
1067     #192..#240:
1068       begin
1069         OldCode:=UTF8CodepointToUnicode(@s[SrcPos],CharLen);
1070         NewCode:=UnicodeLowercase(OldCode);
1071         if NewCode=OldCode then begin
1072           System.Move(s[SrcPos],Result[DstPos],CharLen);
1073           inc(DstPos,CharLen);
1074         end else begin
1075           inc(DstPos,UnicodeToUTF8(NewCode,@Result[DstPos]));
1076         end;
1077         inc(SrcPos,CharLen);
1078       end;
1079     else
1080       Result[DstPos]:=s[SrcPos];
1081       inc(SrcPos);
1082       inc(DstPos);
1083     end;
1084   end;
1085 end;
1086 
1087 function UTF8LowerCaseViaTables(const s: string): string;
1088 var
1089   i: PtrInt;
1090   CharLen: integer;
1091   OldCode: LongWord;
1092   NewCode: LongWord;
1093   NewCharLen: integer;
1094   Changed: Boolean;
1095   p: PChar;
1096 begin
1097   Result:=s;
1098   if Result='' then exit;
1099   Changed:=false;
1100   p:=PChar(Result);
1101   repeat
1102     case p^ of
1103     #0:
1104       if p-PChar(Result)=length(Result) then
1105         exit
1106       else
1107         inc(p);
1108     'A'..'Z': // First ASCII chars
1109       begin
1110         if not Changed then begin
1111           i:=p-PChar(Result)+1;
1112           UniqueString(Result);
1113           Changed:=true;
1114           p:=@Result[i];
1115         end;
1116         p^:=chr(ord(p^)+32);
1117         inc(p);
1118       end;
1119 
1120     #192..#240: // Now chars with multiple bytes
1121       begin
1122         OldCode:=UTF8CodepointToUnicode(p,CharLen);
1123         NewCode:=UnicodeLowercase(OldCode);
1124         if NewCode<>OldCode then begin
1125           if not Changed then begin
1126             i:=p-PChar(Result)+1;
1127             UniqueString(Result);
1128             Changed:=true;
1129             p:=@Result[i];
1130           end;
1131           NewCharLen:=UnicodeToUTF8(NewCode,p);
1132           if CharLen<>NewCharLen then begin
1133             // string size changed => use slower function
1134             Result:=UTF8LowercaseDynLength(s);
1135             exit;
1136           end;
1137         end;
1138         inc(p,CharLen);
1139       end;
1140     else
1141       inc(p);
1142     end;
1143   until false;
1144 end;
1145 {$ENDIF}
1146 
1147 initialization
1148   InitUnicodeTables;
1149 end.
1150 
1151