Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | introducing new TSynAnsiConvert and TSynAnsiFixedWidth classes, able to process Unicode to/from Ansi conversion in all possible code pages, with generic access methods and optimized handling of fixed width encodings |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
fef5fd8640150ad594e7a8ce824bcc8c |
User & Date: | G018869 2012-02-08 16:22:44 |
2012-02-08
| ||
18:35 | fix minor compiler warnings check-in: 647d27132b user: User tags: trunk | |
16:22 | introducing new TSynAnsiConvert and TSynAnsiFixedWidth classes, able to process Unicode to/from Ansi conversion in all possible code pages, with generic access methods and optimized handling of fixed width encodings check-in: fef5fd8640 user: G018869 tags: trunk | |
2012-02-07
| ||
07:49 | updated documentation about new Service callbacks signature check-in: 2317e013f4 user: G018869 tags: trunk | |
Changes to SQLite3/SQLite3Commons.pas.
463 464 465 466 467 468 469 470 471 472 473 474 475 476 ... 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 ... 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 ... 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 .... 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 .... 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 .... 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 .... 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 |
- associated TSQLRestServer.CacheFlush service for flushing the Server cache, and remote TSQLRestClientURI.ServerCacheFlush() method for the client - fixed issue in TSQLRecord.FillPrepare when the table has less columns that the filling TSQLTable (can occur e.g. when using aCustomFieldsCSV parameter in FillPrepare method) - EngineList methods (including TSQLRestServerStaticInMemory class) now handles an optional integer pointer, to return the count of row data - fix issue about missing last item in JSONToObject() function - fix issue when handling null JSON objects in GetJSONObjectAsSQL() function - fix issue about record locking in TSQLRestClientURI.Retrieve method - fix issue about abusive session timeout: TSQLRestServer.SessionGet is now renamed SessionAccess and refreshes the session access timestamp each time a session is retrieved - fix issue in SetInt64Prop() procedure which failed the update of a property ................................................................................ /// set of available SQL field property types TSQLFieldTypes = set of TSQLFieldType; //// a fixed array of SQL field property types TSQLFieldTypeArray = array[0..MAX_SQLFIELDS] of TSQLFieldType; /// used to define how to convert some UTF-8 encoded text into the current // generic string type // - return either code page + AnsiString for Delphi 2 to 2007, // either UnicodeString for Delphi 2009/2010/XE // - this event will mostly point to the Language.UTF8ToString method // of the SQlite3i18n unit TUTF8ToStringEvent = function(const Text: RawUTF8): string of object; /// contains the parameters used for sorting // - FieldCount is 0 if was never sorted // - used to sort data again after a successfull data update with TSQLTableJSON.FillFrom() TSQLTableSortParams = record FieldCount, FieldIndex: integer; FieldType: TSQLFieldType; Asc: boolean; ................................................................................ {{ read-only access to a particular field value, as a Variant - will try to use the most approriate Variant type for conversion (will use e.g. TDateTime for sftDateTime or sftTimeLog, Extended for sftFloat...) - will handle any necessary conversion to VCL string text, ready to be displayed - the global UTF8ToString() function will be used for the conversion: for proper i18n handling before Delphi 2009, you should use the overloaded method with aUTF8ToString=Language.UTF8ToString } function GetVariant(Row,Field: integer; Client: TObject; aUTF8ToString: TUTF8ToStringEvent=nil): variant; {$endif} {{ read-only access to a particular field value, as VCL string text - the global UTF8ToString() function will be used for the conversion: for proper i18n handling before Delphi 2009, you should use the overloaded method with aUTF8ToString=Language.UTF8ToString } function GetString(Row,Field: integer): string; overload; {{ read-only access to a particular field value, as VCL string text - aUTF8ToString parameter should be set to Language.UTF8ToString for proper i18n handling with Delphi versions prior to Delphi 2009; the default global UTF8ToString() function will be used if no event is defined } function GetString(Row,Field: integer; aUTF8ToString: TUTF8ToStringEvent): string; overload; {{ fill a unicode buffer with a particular field value - return number of wide characters written in Dest^ } function GetWP(Row,Field: integer; Dest: PWideChar; MaxDestChars: cardinal): integer; {{ read-only access to a particular field value, as UTF-16 Unicode text - Raw Unicode is WideChar(zero) terminated - its content is allocated to contain all WideChars (not trimed to 255, like GetWP() above } ................................................................................ ready to be displayed to the VCL, for sftEnumerate, sftTimeLog and sftRecord/sftID - returns '' as string Text, if text can by displayed directly with Get*() methods above - returns '' for other properties kind, if UTF8ToString is nil, or the ready to be displayed value if UTF8ToString event is set (to be used mostly with Language.UTF8ToString) } function ExpandAsString(Row,Field: integer; Client: TObject; out Text: string; aUTF8ToString: TUTF8ToStringEvent=nil): TSQLFieldType; {{ read-only access to a particular DateTime field value - expect SQLite3 TEXT field in ISO 8601 'YYYYMMDD hhmmss' or 'YYYY-MM-DD hh:mm:ss' format } function GetDateTime(Row,Field: integer): TDateTime; {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif} {{ read-only access to a particular TTimeLog field value - return the result as Iso8601.Text() Iso-8601 encoded text } ................................................................................ end; function TSQLTable.GetS(Row, Field: integer): shortstring; begin UTF8ToShortString(result,Get(Row,Field)); end; function TSQLTable.GetString(Row, Field: integer; aUTF8ToString: TUTF8ToStringEvent): string; {$ifndef UNICODE} var P: PUTF8Char; {$endif} begin if Assigned(aUTF8ToString) then result := aUTF8ToString(GetU(Row,Field)) else {$ifdef UNICODE} result := UTF8DecodeToUnicodeString(Get(Row,Field),0); {$else} begin P := Get(Row,Field); result := UTF8DecodeToString(P,StrLen(P)); end; {$endif} end; function TSQLTable.GetString(Row, Field: integer): string; begin {$ifdef UNICODE} result := UTF8DecodeToUnicodeString(Get(Row,Field),0); // L=0 -> use StrLen {$else} result := UTF8ToString(GetU(Row,Field)); {$endif} end; function TSQLTable.GetCaption(Row, Field: integer): string; begin GetCaptionFromPCharLen(Get(Row,Field),result); end; ................................................................................ for result := 1 to RowCount do if UTF8IComp(Get(result,FieldIndex),pointer(aValue))=0 then exit; result := 0; end; {$ifdef USEVARIANTS} function TSQLTable.GetVariant(Row, Field: integer; Client: TObject; aUTF8ToString: TUTF8ToStringEvent=nil): Variant; var FT: TSQLFieldType; EnumType: PEnumType; err: integer; Value64: Int64; ValueRef: RecordRef absolute Value64; Value8601: Iso8601 absolute Value64; ValueCurrency: Currency absolute Value64; ................................................................................ end; // err<>0 -> not an integer -> will be displayed with GetString() end; end; // sftBlob and sftMany are not handled // sftBlobRecord, sftBlobDynArray as binary string // sftObject as JSON serialization result := GetString(Row,Field,aUTF8ToString); end; {$endif} function TSQLTable.ExpandAsString(Row, Field: integer; Client: TObject; out Text: string; aUTF8ToString: TUTF8ToStringEvent=nil): TSQLFieldType; var EnumType: PEnumType; err: integer; Value: Int64; Ref: RecordRef absolute Value; begin // Text was already forced to '' because was defined as "out" parameter if Row=0 then begin // Field Name result := sftUnknown; ................................................................................ Text := {$ifdef UNICODE}Ansi7ToString{$endif}(Ref.Text(TSQLRest(Client).Model)) else result := sftUTF8Text; // display ID number if no table model end; end; end; if Text='' then // returns the value as text by default Text := GetString(Row,Field,aUTF8ToString); end; function TSQLTable.GetTimeLog(Row, Field: integer; Expanded: boolean; FirstTimeChar: AnsiChar): RawUTF8; var Value: Iso8601; begin Value.Value := GetInt64(Get(Row,Field)); |
> > > > < < < < < < < < | < | < < < < < | < | < | < < < < < < | < < < < < < < | < > | < | | | |
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 ... 751 752 753 754 755 756 757 758 759 760 761 762 763 764 ... 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 ... 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 .... 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 .... 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 .... 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 .... 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 |
- associated TSQLRestServer.CacheFlush service for flushing the Server cache, and remote TSQLRestClientURI.ServerCacheFlush() method for the client - fixed issue in TSQLRecord.FillPrepare when the table has less columns that the filling TSQLTable (can occur e.g. when using aCustomFieldsCSV parameter in FillPrepare method) - EngineList methods (including TSQLRestServerStaticInMemory class) now handles an optional integer pointer, to return the count of row data - uses new generic TSynAnsiConvert classes for code page process: that is, SQLite3i18n S2U() and U2S() match the SynCommons StringToUTF8() and UTF8ToString() functions - therefore, the TUTF8ToStringEvent parameter is not useful any more - fix issue about missing last item in JSONToObject() function - fix issue when handling null JSON objects in GetJSONObjectAsSQL() function - fix issue about record locking in TSQLRestClientURI.Retrieve method - fix issue about abusive session timeout: TSQLRestServer.SessionGet is now renamed SessionAccess and refreshes the session access timestamp each time a session is retrieved - fix issue in SetInt64Prop() procedure which failed the update of a property ................................................................................ /// set of available SQL field property types TSQLFieldTypes = set of TSQLFieldType; //// a fixed array of SQL field property types TSQLFieldTypeArray = array[0..MAX_SQLFIELDS] of TSQLFieldType; /// contains the parameters used for sorting // - FieldCount is 0 if was never sorted // - used to sort data again after a successfull data update with TSQLTableJSON.FillFrom() TSQLTableSortParams = record FieldCount, FieldIndex: integer; FieldType: TSQLFieldType; Asc: boolean; ................................................................................ {{ read-only access to a particular field value, as a Variant - will try to use the most approriate Variant type for conversion (will use e.g. TDateTime for sftDateTime or sftTimeLog, Extended for sftFloat...) - will handle any necessary conversion to VCL string text, ready to be displayed - the global UTF8ToString() function will be used for the conversion: for proper i18n handling before Delphi 2009, you should use the overloaded method with aUTF8ToString=Language.UTF8ToString } function GetVariant(Row,Field: integer; Client: TObject): variant; {$endif} {{ read-only access to a particular field value, as VCL string text - the global UTF8ToString() function will be used for the conversion: for proper i18n handling before Delphi 2009, you should use the overloaded method with aUTF8ToString=Language.UTF8ToString } function GetString(Row,Field: integer): string; {{ fill a unicode buffer with a particular field value - return number of wide characters written in Dest^ } function GetWP(Row,Field: integer; Dest: PWideChar; MaxDestChars: cardinal): integer; {{ read-only access to a particular field value, as UTF-16 Unicode text - Raw Unicode is WideChar(zero) terminated - its content is allocated to contain all WideChars (not trimed to 255, like GetWP() above } ................................................................................ ready to be displayed to the VCL, for sftEnumerate, sftTimeLog and sftRecord/sftID - returns '' as string Text, if text can by displayed directly with Get*() methods above - returns '' for other properties kind, if UTF8ToString is nil, or the ready to be displayed value if UTF8ToString event is set (to be used mostly with Language.UTF8ToString) } function ExpandAsString(Row,Field: integer; Client: TObject; out Text: string): TSQLFieldType; {{ read-only access to a particular DateTime field value - expect SQLite3 TEXT field in ISO 8601 'YYYYMMDD hhmmss' or 'YYYY-MM-DD hh:mm:ss' format } function GetDateTime(Row,Field: integer): TDateTime; {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif} {{ read-only access to a particular TTimeLog field value - return the result as Iso8601.Text() Iso-8601 encoded text } ................................................................................ end; function TSQLTable.GetS(Row, Field: integer): shortstring; begin UTF8ToShortString(result,Get(Row,Field)); end; function TSQLTable.GetString(Row, Field: integer): string; var U: PUTF8Char; begin U := Get(Row,Field); {$ifdef UNICODE} result := UTF8DecodeToUnicodeString(U,0); // L=0 -> use StrLen {$else} result := CurrentAnsiConvert.UTF8BufferToAnsi(U,StrLen(U)); {$endif} end; function TSQLTable.GetCaption(Row, Field: integer): string; begin GetCaptionFromPCharLen(Get(Row,Field),result); end; ................................................................................ for result := 1 to RowCount do if UTF8IComp(Get(result,FieldIndex),pointer(aValue))=0 then exit; result := 0; end; {$ifdef USEVARIANTS} function TSQLTable.GetVariant(Row, Field: integer; Client: TObject): Variant; var FT: TSQLFieldType; EnumType: PEnumType; err: integer; Value64: Int64; ValueRef: RecordRef absolute Value64; Value8601: Iso8601 absolute Value64; ValueCurrency: Currency absolute Value64; ................................................................................ end; // err<>0 -> not an integer -> will be displayed with GetString() end; end; // sftBlob and sftMany are not handled // sftBlobRecord, sftBlobDynArray as binary string // sftObject as JSON serialization result := GetString(Row,Field); end; {$endif} function TSQLTable.ExpandAsString(Row, Field: integer; Client: TObject; out Text: string): TSQLFieldType; var EnumType: PEnumType; err: integer; Value: Int64; Ref: RecordRef absolute Value; begin // Text was already forced to '' because was defined as "out" parameter if Row=0 then begin // Field Name result := sftUnknown; ................................................................................ Text := {$ifdef UNICODE}Ansi7ToString{$endif}(Ref.Text(TSQLRest(Client).Model)) else result := sftUTF8Text; // display ID number if no table model end; end; end; if Text='' then // returns the value as text by default Text := GetString(Row,Field); end; function TSQLTable.GetTimeLog(Row, Field: integer; Expanded: boolean; FirstTimeChar: AnsiChar): RawUTF8; var Value: Iso8601; begin Value.Value := GetInt64(Get(Row,Field)); |
Changes to SQLite3/SQLite3Pages.pas.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 ... 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 .... 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 |
Version 1.15 - fixed an endless loop in TGDIPages.DrawTextAcrossCols when wrapping text - fixed an issue in TGDIPages.DrawTextAcrossCols when test is exported to pdf (wrong clipping region set) - if TGDIPages.WordWrapLeftCols=TRUE, won't wrap column headers Version 1.16 - some minor fixes (e.g. preview landscape or keys for popup menu) - enhanced the print preview screen with a left-sided button bar *) interface ................................................................................ {$ifndef USEPDFPRINTER} {$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER {$endif} uses {$ifndef USEPDFPRINTER} SynCommons, SynPdf, {$endif} Windows, Messages, SysUtils, Classes, Contnrs, {$ifdef GDIPLUSDRAW} SynGdiPlus, {$endif} Graphics, Controls, Dialogs, Forms, StdCtrls, ................................................................................ end; if PreviewForm<>nil then SetFocus; end; procedure TGDIPages.InternalUnicodeString(const s: string; var PW: PWideChar; var PWLen: integer; size: PSize); {$ifndef UNICODE} {$ifndef USEPDFPRINTER} var i: integer; {$endif} {$endif} begin if Assigned(OnStringToUnicode) then begin fInternalUnicodeString := OnStringToUnicode(s); PW := pointer(fInternalUnicodeString); PWLen := length(fInternalUnicodeString) shr 1; end else begin {$ifdef UNICODE} PW := pointer(s); // under Delphi 2009/2010/XE, no conversion necessary :) PWLen := length(s); {$else} fInternalUnicodeString := ''; // so Setlength() below won't do any move() PWLen := length(S); SetLength(fInternalUnicodeString,PWLen*2+1); // +1 for last wide #0 PW := Pointer(fInternalUnicodeString); {$ifndef USEPDFPRINTER} if GetACP<>1252 then begin {$endif} // low-level MBCS RTL function including last widechar #0 PWLen := MultiByteToWideChar(GetACP, 0, Pointer(s), Length(s), PW, PWLen); PW[PWLen] := #0; {$ifndef USEPDFPRINTER} end else // fast WinAnsi conversion using a fixed table from SynCommons for i := 0 to PWLen do // includes S[length(s)+1]=#0 -> last widechar #0 PWordArray(PW)[i] := WinAnsiTable[PByteArray(s)[i]]; {$endif} {$endif} end; if size<>nil then GetTextExtentPoint32W(fCanvas.Handle,PW,PWLen,size^); end; procedure TGDIPages.PopupMenuPopup(Sender: TObject); |
> | < < < < < > < < < | | < < < | < < | < < < > > |
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 ... 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 .... 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 |
Version 1.15 - fixed an endless loop in TGDIPages.DrawTextAcrossCols when wrapping text - fixed an issue in TGDIPages.DrawTextAcrossCols when test is exported to pdf (wrong clipping region set) - if TGDIPages.WordWrapLeftCols=TRUE, won't wrap column headers Version 1.16 - includes new TSynAnsiConvert classes for handling Ansi charsets - some minor fixes (e.g. preview landscape or keys for popup menu) - enhanced the print preview screen with a left-sided button bar *) interface ................................................................................ {$ifndef USEPDFPRINTER} {$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER {$endif} uses {$ifndef USEPDFPRINTER} SynCommons, SynPdf, {$endif} Windows, Messages, SysUtils, Classes, Contnrs, {$ifdef GDIPLUSDRAW} SynGdiPlus, {$endif} Graphics, Controls, Dialogs, Forms, StdCtrls, ................................................................................ end; if PreviewForm<>nil then SetFocus; end; procedure TGDIPages.InternalUnicodeString(const s: string; var PW: PWideChar; var PWLen: integer; size: PSize); begin if Assigned(OnStringToUnicode) then begin fInternalUnicodeString := OnStringToUnicode(s); PW := pointer(fInternalUnicodeString); PWLen := length(fInternalUnicodeString) shr 1; end else begin PWLen := length(s); {$ifdef UNICODE} PW := pointer(s); // under Delphi 2009/2010/XE, no conversion necessary :) {$else} SetString(fInternalUnicodeString,nil,PWLen*2+1); // +1 for last wide #0 PW := Pointer(fInternalUnicodeString); {$ifdef USEPDFPRINTER} PWLen := MultiByteToWideChar(GetACP,0,Pointer(s),Length(s),PW,PWLen); {$else} PWLen := CurrentAnsiConvert.AnsiBufferToUnicode(PW,Pointer(s),length(s))-PW; {$endif} PW[PWLen] := #0; {$endif} end; if size<>nil then GetTextExtentPoint32W(fCanvas.Handle,PW,PWLen,size^); end; procedure TGDIPages.PopupMenuPopup(Sender: TObject); |
Changes to SQLite3/SQLite3ToolBar.pas.
1 2 3 4 5 6 7 8 9 10 .. 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 .... 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 .... 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 .... 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 .... 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 |
/// Database-driven Office 2007 Toolbar // - this unit is a part of the freeware Synopse SQLite3 database framework, // licensed under a MPL/GPL/LGPL tri-license; version 1.15 unit SQLite3ToolBar; interface { This file is part of Synopse SQLite3 database framework. ................................................................................ - now uses TSQLRecord.RecordProps instead of lowest level RTTI calls - by default, will use only VCL components to create the Ribbon; can use proprietary TMS component pack if USETMSPACK global conditional is defined Version 1.15 - TSQLRibbon.AddToReport method can work with self=nil } uses Windows, Consts, Dialogs, ShellAPI, SysUtils, Forms, Classes, Messages, Graphics, ImgList, Controls, Grids, ExtCtrls, Menus, {$ifdef USETMSPACK} AdvOfficePager, AdvToolBar, AdvGlowButton, AdvMenus, AdvShapeButton, AdvPreviewMenu, AdvToolBarStylers, AdvPreviewMenuStylers, AdvOfficePagerStylers, AdvOfficeStatusBarStylers, AdvPanel, TaskDialog, TaskDialogEx, GDIPicture, {$else} StdCtrls, ComCtrls, SynTaskDialog, Buttons, CommCtrl, ................................................................................ exit; PHint := nil; RibbonParams := GetParameter(aRecord.RecordClass); if RibbonParams<>nil then with RibbonParams^ do if EditFieldHintsToReport and (EditFieldHints<>nil) then PHint := pointer(LoadResString(EditFieldHints)); result := Language.UTF8ToString(Client.MainFieldValue( aRecord.RecordClass,aRecord.ID,true)); if WithTitle then begin aReport.DrawTitle(aRecord.CaptionName+' : '+result,true); aReport.NewHalfLine; end; OldWordWrapLeftCols := aReport.WordWrapLeftCols; aReport.WordWrapLeftCols := true; // automatic word wrap and #13 for next line aReport.AddColumns([ColWidthName,ColWidthValue]); ................................................................................ Rep.Caption := aName; if not Rep.ExportPDF(aName,false) then exit; end else if SameText(ext,'.TXT') then begin for i := 0 to Rep.Pages.Count-1 do Content := Content+Rep.Pages[i]; // append content of every page with Language do // export as ANSI text file, in the current code page if not FileFromString(UnicodeToAnsi(StringToUnicode(Content)),aName) then exit; end else exit; // invalid extension if OpenAfterCreation then ShellExecute(Application.DialogHandle,nil,pointer(aName),nil,nil,SW_SHOWNORMAL); result := aName; // mark success finally ................................................................................ SetLength(ColWidth,Table.FieldCount); move(ColWidths[0],ColWidth[0],Table.FieldCount*4); end else Table.CalculateFieldLengthMean(ColWidth,true); // FromDisplay=true result.AddColumns(ColWidth); SetLength(ColText,Table.FieldCount); for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(0,F,Client,ColText[F],Language.UTF8ToString); result.AddColumnHeaders(ColText,true,true); // true = with gray bottom line for R := 1 to Table.RowCount do if Marked[R] then begin for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(R,F,Client,ColText[F],Language.UTF8ToString); result.DrawTextAcrossCols(ColText); end; end; end; procedure TSQLRibbon.CreateReport(aPageIndex: Integer); var P: TSQLRibbonTab; ................................................................................ SetLength(ColWidth,Table.FieldCount); move(ColWidths[0],ColWidth[0],Table.FieldCount*4); end else Table.CalculateFieldLengthMean(ColWidth,true); // FromDisplay=true aReport.AddColumns(ColWidth); SetLength(ColText,Table.FieldCount); for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(0,F,aClient,ColText[F],Language.UTF8ToString); aReport.AddColumnHeaders(ColText,true,true); // true = with gray bottom line for R := 1 to Table.RowCount do begin for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(R,F,aClient,ColText[F],Language.UTF8ToString); aReport.DrawTextAcrossCols(ColText); end; end; end. |
| > > > | < | | | > > > > > | | | | |
1 2 3 4 5 6 7 8 9 10 .. 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 .... 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 .... 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 .... 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 .... 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 |
/// Database-driven Office 2007 Toolbar // - this unit is a part of the freeware Synopse SQLite3 database framework, // licensed under a MPL/GPL/LGPL tri-license; version 1.16 unit SQLite3ToolBar; interface { This file is part of Synopse SQLite3 database framework. ................................................................................ - now uses TSQLRecord.RecordProps instead of lowest level RTTI calls - by default, will use only VCL components to create the Ribbon; can use proprietary TMS component pack if USETMSPACK global conditional is defined Version 1.15 - TSQLRibbon.AddToReport method can work with self=nil Version 1.16 - includes new TSynAnsiConvert classes for handling Ansi charsets } uses Windows, Consts, Dialogs, ShellAPI, SysUtils, Forms, Classes, Messages, Graphics, ImgList, Controls, Grids, ExtCtrls, Menus, {$ifdef USETMSPACK} AdvOfficePager, AdvToolBar, AdvGlowButton, AdvMenus, AdvShapeButton, AdvPreviewMenu, AdvToolBarStylers, AdvPreviewMenuStylers, AdvOfficePagerStylers, AdvOfficeStatusBarStylers, AdvPanel, TaskDialog, TaskDialogEx, GDIPicture, {$else} StdCtrls, ComCtrls, SynTaskDialog, Buttons, CommCtrl, ................................................................................ exit; PHint := nil; RibbonParams := GetParameter(aRecord.RecordClass); if RibbonParams<>nil then with RibbonParams^ do if EditFieldHintsToReport and (EditFieldHints<>nil) then PHint := pointer(LoadResString(EditFieldHints)); result := U2S(Client.MainFieldValue(aRecord.RecordClass,aRecord.ID,true)); if WithTitle then begin aReport.DrawTitle(aRecord.CaptionName+' : '+result,true); aReport.NewHalfLine; end; OldWordWrapLeftCols := aReport.WordWrapLeftCols; aReport.WordWrapLeftCols := true; // automatic word wrap and #13 for next line aReport.AddColumns([ColWidthName,ColWidthValue]); ................................................................................ Rep.Caption := aName; if not Rep.ExportPDF(aName,false) then exit; end else if SameText(ext,'.TXT') then begin for i := 0 to Rep.Pages.Count-1 do Content := Content+Rep.Pages[i]; // append content of every page // export as ANSI text file, in the current code page if not FileFromString( {$ifdef UNICODE} CurrentAnsiConvert.UnicodeBufferToAnsi(pointer(Content),length(Content)) {$else} Content {$endif} ,aName) then exit; end else exit; // invalid extension if OpenAfterCreation then ShellExecute(Application.DialogHandle,nil,pointer(aName),nil,nil,SW_SHOWNORMAL); result := aName; // mark success finally ................................................................................ SetLength(ColWidth,Table.FieldCount); move(ColWidths[0],ColWidth[0],Table.FieldCount*4); end else Table.CalculateFieldLengthMean(ColWidth,true); // FromDisplay=true result.AddColumns(ColWidth); SetLength(ColText,Table.FieldCount); for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(0,F,Client,ColText[F]); result.AddColumnHeaders(ColText,true,true); // true = with gray bottom line for R := 1 to Table.RowCount do if Marked[R] then begin for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(R,F,Client,ColText[F]); result.DrawTextAcrossCols(ColText); end; end; end; procedure TSQLRibbon.CreateReport(aPageIndex: Integer); var P: TSQLRibbonTab; ................................................................................ SetLength(ColWidth,Table.FieldCount); move(ColWidths[0],ColWidth[0],Table.FieldCount*4); end else Table.CalculateFieldLengthMean(ColWidth,true); // FromDisplay=true aReport.AddColumns(ColWidth); SetLength(ColText,Table.FieldCount); for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(0,F,aClient,ColText[F]); aReport.AddColumnHeaders(ColText,true,true); // true = with gray bottom line for R := 1 to Table.RowCount do begin for F := 0 to Table.FieldCount-1 do Table.ExpandAsString(R,F,aClient,ColText[F]); aReport.DrawTextAcrossCols(ColText); end; end; end. |
Changes to SQLite3/SQLite3UI.pas.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 .... 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 .... 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 |
- compatibility with Delphi XE2 - new TSQLTableToGrid.SetFieldFixedWidth method - new TSQLTableToGrid.FieldTitleTruncatedNotShownAsHint property - fixed issue on TDrawGrid events when TSQLTableToGrid is destroyed Version 1.16 - new FillStringGrid() function, ready to fill a regular TStringGrid *) interface {$I Synopse.inc} ................................................................................ end else // not first row: data if (Button=mbRight) and (ssRight in Shift) and Assigned(OnRightClickCell) then OnRightClickCell(Table,ACol,ARow,X,Y) else if (ssCtrl in Shift) or (Button<>mbLeft) then begin if not Assigned(OnHintText) or not OnHintText(Table,ACol,ARow,Hint) then Table.ExpandAsString(ARow,ACol,Client,Hint,Language.UTF8ToString); // Hint := IntToStr(SelectedID); ShowHintString(Hint,ACol,ARow,4000); end else if (Button=mbLeft) and (ACol=0) and fMarkAllowed and (X<CheckBoxWidth+4) then begin if Marked[ARow] then // on click: invert current Marked[] checkbox state fMouseDownMarkedValue := markOff else fMouseDownMarkedValue := markOn; ................................................................................ begin result := ''; if (self=nil) or (cardinal(Row)>cardinal(Table.RowCount)) or (Table.FieldCount<=0) then exit; for F := 0 to Table.FieldCount-1 do begin if (not Assigned(OnValueText)) or (not OnValueText(Table,F,Row,text)) then Table.ExpandAsString(Row,F,Client,Text,Language.UTF8ToString); i := pos(#13,Text); // trim multi-line text to first line if i>0 then SetLength(Text,i-1); if (F>0) and (text<>'') then text := ' '+text; result := result+text; end; |
> | | |
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 .... 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 .... 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 |
- compatibility with Delphi XE2 - new TSQLTableToGrid.SetFieldFixedWidth method - new TSQLTableToGrid.FieldTitleTruncatedNotShownAsHint property - fixed issue on TDrawGrid events when TSQLTableToGrid is destroyed Version 1.16 - new FillStringGrid() function, ready to fill a regular TStringGrid - includes new TSynAnsiConvert classes for handling Ansi charsets *) interface {$I Synopse.inc} ................................................................................ end else // not first row: data if (Button=mbRight) and (ssRight in Shift) and Assigned(OnRightClickCell) then OnRightClickCell(Table,ACol,ARow,X,Y) else if (ssCtrl in Shift) or (Button<>mbLeft) then begin if not Assigned(OnHintText) or not OnHintText(Table,ACol,ARow,Hint) then Table.ExpandAsString(ARow,ACol,Client,Hint); // Hint := IntToStr(SelectedID); ShowHintString(Hint,ACol,ARow,4000); end else if (Button=mbLeft) and (ACol=0) and fMarkAllowed and (X<CheckBoxWidth+4) then begin if Marked[ARow] then // on click: invert current Marked[] checkbox state fMouseDownMarkedValue := markOff else fMouseDownMarkedValue := markOn; ................................................................................ begin result := ''; if (self=nil) or (cardinal(Row)>cardinal(Table.RowCount)) or (Table.FieldCount<=0) then exit; for F := 0 to Table.FieldCount-1 do begin if (not Assigned(OnValueText)) or (not OnValueText(Table,F,Row,text)) then Table.ExpandAsString(Row,F,Client,Text); i := pos(#13,Text); // trim multi-line text to first line if i>0 then SetLength(Text,i-1); if (F>0) and (text<>'') then text := ' '+text; result := result+text; end; |
Changes to SQLite3/SQLite3i18n.pas.
1 2 3 4 5 6 7 8 9 10 .. 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 ... 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 ... 147 148 149 150 151 152 153 154 155 156 157 158 159 160 ... 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 ... 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 ... 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 ... 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 ... 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 ... 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 ... 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 ... 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 ... 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 ... 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 ... 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 .... 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 .... 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 .... 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 .... 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 .... 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 .... 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 .... 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 .... 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 .... 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 .... 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 .... 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 .... 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 .... 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 .... 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 .... 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 |
/// internationalization (i18n) routines and classes // - this unit is a part of the freeware Synopse SQLite3 database framework, // licensed under a MPL/GPL/LGPL tri-license; version 1.15 unit SQLite3i18n; (* This file is part of Synopse SQLite3 database framework. Synopse SQLite3 database framework. Copyright (C) 2012 Arnaud Bouchez Synopse Informatique - http://synopse.info ................................................................................ i18n routines for the Synopse SQLite3 database framework ********************************************************** - works internaly with the string type, that is AnsiString with code pages and charsets for compiler versions earlier than Delphi 2009, and UnicodeString since Delphi 2009/2010 -> so it's 100% VCL compatible - can load language definition files encoded in Unicode or UTF8 - auto-call SetThreadLocale() for full application i18n - update default locale settings values (date,currency..) - force english locale settings on non english system (consistent with UI) - handle multiple charsets (standard or custom VCL components compatible) - resourcestring on-the-fly translation - resourcestring access using fast cache, even without translation; use this ................................................................................ i18nCompareStr/Text() is to be used instead 0.2.1 bug found in i18nInnerCompareText() 1.0 First public release of the Synopse SQLite3 Framework Version 1.1 - 14 January 2009: - allow to get rid of our Enhanced Runtime Library dependency if not available (e.g. for FPC or on cross-platform, or on Delphi version newer than Delphi 7) - attempt to reach Delphi 2009/2010 compilation (string=UnicodeString): the UNICODE conditional will adapt the framework to these compilers (you shouldn't have to change any conditinal define below - generic string type is now used for all i18n of text: in Delphi 2009/2010, it will be an UnicodeString, but with earlier version of Delphi, string is an AnsiString with the codepage of the current selected language - attempt to reach Free Pascal Compiler 2.4.0 compatibility - LoadResStringTranslate() and resourcestring caching are defined in the SQLite3Commons unit, if our Enhanced Run Time Library (or LVCL) is not used Version 1.2 - 18 January 2010 ................................................................................ Version 1.15 - compatibility with Delphi XE2 - fix endless recursion loop in ExtractAllResources for nested classes - several changes in ExtractAllResources implementation - handle TModTime published property / sftModTime SQL field - handle TCreateTime published property / sftCreateTime SQL field *) {$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER interface {.$D-,L-} ................................................................................ {.$define EXTRACTALLRESOURCES} // must be set globally for the whole application {.$define ENHANCEDRTL} { define this if you DID install our Enhanced Runtime library: it has already hacked the "legacy" LoadResString() procedure and added a LoadResStringTranslate() - it will be unset automaticaly (see below) for Delphi 2009/2010, since no version of our Enhanced RTL exists for these compilers - this conditional must be defined in both SQLite3Commons and SQLite3i18n units, or (even better) globally in the Project options } {.$define USESHARP} // if defined, $$,$$$,$$$ are replaced with some globals in _() {$ifndef NOI18N} // with this global define, you can use the unit procs, without the UI i18n {$define USEFORMCREATEHOOK} // if defined, all forms will be auto-translated, even 3rd party ones // just before an OnCreate handler would be called {$endif} {$ifdef LVCL} // the LVCL don't have TForm.DoCreate and such // -> it's easier to explicitely change {$undef USEFORMCREATEHOOK} {$endif} uses Windows, SysUtils, Classes, {$ifdef USEFORMCREATEHOOK} {$ifndef LVCL} Menus, ................................................................................ {$endif USEFORMCREATEHOOK} StdCtrls, Forms, SynCommons, // some basic types SQLite3Commons; // need extended RTTI information {$ifdef UNICODE} {$undef ENHANCEDRTL} // no version of our Enhanced RTL exists for Delphi 2009/2010 {$endif} {$ifdef LVCL} // LVCL system.pas doesn't implement LoadResStringTranslate() and won't need it {$define ENHANCEDRTL} {$endif} ................................................................................ LCID: cardinal; /// initializes all TLanguage object fields for a specific language procedure Fill(Language: TLanguages); /// returns two-chars long language abreviation ('HE' e.g.) function Abr: RawByteString; /// returns fully qualified language name ('Hebrew' e.g.), // using current UI language // - return "string" type, i.e. UnicodeString for Delphi 2009/2010 function Name: string; end; var /// the global Language used by the User Interface, // as updated by the last SetCurrentLanguage() call CurrentLanguage: TLanguage = ( ................................................................................ // change the current language in the registry procedure i18nAddLanguageCombo(const MsgPath: TFileName; Combo: TComboBox); {$endif} /// save the default language to the registry // - language will be changed at next startup // - return a message ready to be displayed on the screen // - return "string" type, i.e. UnicodeString for Delphi 2009/2010 function i18nLanguageToRegistry(const Language: TLanguages): string; /// get the default language from the registry function i18nRegistryToLanguage: TLanguages; resourcestring /// this message will be displayed on the screen when the user change the ................................................................................ procedure SetCurrentLanguage(const value: RawUTF8); overload; {$endif} {$endif} /// Return the language text, ready to be displayed (after translation if // necessary) // - e.g. LanguageName(lngEnglish)='English' // - return "string" type, i.e. UnicodeString for Delphi 2009/2010 function LanguageName(aLanguage: TLanguages): string; /// LanguageAbrToIndex('GR')=1, e.g. // - return LANGUAGE_NONE if not found function LanguageAbrToIndex(const value: RawUTF8): TLanguages; overload; /// LanguageAbrToIndex('GR')=1, e.g. ................................................................................ sharp2: string = ''; sharp3: string = ''; {$endif} /// translate the 'Text' term into current language, with no || nor $$[$[$]] // - LoadResStringTranslate of our customized system.pas points to this procedure // - therefore, direct use of LoadResStringTranslate() is better in apps // - expect "string" type, i.e. UnicodeString for Delphi 2009/2010 procedure GetText(var Text: string); /// translate the 'English' term into current language // - you should use resourcestring instead of this function // - call interenaly GetText() procedure, i.e. LoadResStringTranslate() function _(const English: WinAnsiString): string; (* /// translate the 'English' term into current language, with || and $$[$[$]] function _(const English: AnsiString; const Name: AnsiString = ''): AnsiString; overload; /// find 'at' nth word in a list: _('A,B,C','idABC7',3) = 'C' function _(const English, Name: AnsiString; at: integer): AnsiString; overload; *) /// get text File contents (even Unicode or UTF8) and convert it into a // Charset-compatible AnsiString (for Delphi 7) or an UnicodeString (for Delphi // 2009/2010) // - by use of this function, the TLanguageFile.LoadFromFile() method is // able to display any Unicode message into the 8 bit standard Delphi VCL, // (for Delphi 2 to 2007) or with the new Unicode VCL (for Delphi 2009/2010) function AnyTextFileToString(const FileName: TFileName; Lang: TLanguages): string; { /// fast replacement of StringReplace(S, OldPattern, NewPattern,[rfReplaceAll]); function StringReplaceAll(const S, OldPattern, NewPattern: AnsiString): AnsiString; /// Faster Equivalent of Delphi 7 StrUtils.PosEx function PosEx(const SubStr, S: AnsiString; Offset: Cardinal = 1): Integer;} var /// a table used for fast conversion to lowercase, according to the current language // - can NOT be used for MBCS strings (with such code pages, you should use windows // slow but accurate API) i18nToLower, /// a table used for fast conversion to uppercase, according to the current language ................................................................................ reload the updated translations. } TLanguageFile = class protected /// the content of the .msg file, translated into generic VCL string // - [Messages] section is expanded into Messages TStringList (see below) // - for Forms translations: [FormName] sections, with Properties=UI Text pairs // - is either an AnsiString in the current code page, or an UnicodeString // (in case of Delphi 2009/2010, that is a UNICODE compiler) Text: string; /// copy of translated strings from [Messages] section // - Objects[] contain pointer(Hash32(WinAnsiEncodedMessage)) // - Strings[] contain Message text, in UnicodeString for Delphi 2009/2010 Messages: TStringList; {$ifndef LVCL} { LVCL will use always the ISO 8601 generic text format } /// format string used to convert a date value to a text // - the expected format is the one used by the FormatDateTime() function // - the current system format, depending on the current language, is used, // then overriden by a DateFmt= entry in the .msg file content DateFmt: string; ................................................................................ procedure LanguageClick(Sender: TObject); {$endif USEFORMCREATEHOOK} /// get corresponding *.msg translation text file name from current exe directory // - e.g. return 'C:\Program Files\MyApplication\FR.msg' class function FileName(aLanguageLocale: TLanguages): TFileName; /// return a translated text from a Hash32(WinAnsiString) value // - search is very fast (use binary search algorithm) // - return a generic VCL string (i.e. UnicodeString for Delphi 2009/2010) function FindMessage(Hash: cardinal): string; public /// identify the current language Language: TLanguage; /// specify a text file containing the translation messages for a language constructor Create(const aFileName: TFileName; aLanguageLocale: TLanguages); overload; /// load corresponding *.msg translation text file from the current exe directory constructor Create(aLanguageLocale: TLanguages); overload; /// free translation tables memory destructor Destroy; override; /// fill translation tables from text file containing the translation messages // - handle on the fly UTF-8 and UNICODE decode into the corresponding ANSI // CHARSET, or into UnicodeString for Delphi 2009/2010 procedure LoadFromFile(const aFileName: TFileName); /// translate an English string into a localized string // - English is case-sensitive (same as standard gettext) // - translations are stored in Messages[] and Text properties // - expect parameter as generic VCL string (i.e. UnicodeString for Delphi 2009/2010) procedure Translate(var English: string); /// translate the english captions of a TForm into the current UI language // - must be called once with english captions // - call automaticaly if conditional USEFORMCREATEHOOK is defined procedure FormTranslateOne(aForm: TComponent); {$ifndef USEFORMCREATEHOOK} procedure FormTranslate(Forms: array of TCustomForm); {$endif USEFORMCREATEHOOK} /// read a parameter, stored in the .msg file before any [Section] function ReadParam(const ParamName: RawUTF8): string; /// convert any Ansi Text into an Unicode String // - use the current Language.CodePage setting, or Win-Ansi (i.e. CODEPAGE_US=1252) // if this TLanguageFile instance is nil (i.e. no custom language set) // - internaly call MultiByteToWideChar() for accurate conversion function AnsiToUnicode(const AnsiText: AnsiString): RawUnicode; /// convert any Ansi Text into an UTF-8 encoded String // - use the current Language.CodePage setting, or Win-Ansi (i.e. CODEPAGE_US=1252) // if this TLanguageFile instance is nil (i.e. no custom language set) // - internaly call MultiByteToWideChar() for accurate conversion function AnsiToUTF8(const AnsiText: AnsiString): RawUTF8; /// convert any UTF-8 encoded text into Ansi Text // - use the current Language.CodePage setting, or Win-Ansi (i.e. CODEPAGE_US=1252) // if this TLanguageFile instance is nil (i.e. no custom language set) // - internaly call WideCharToMultiByte() for accurate conversion function UTF8ToAnsi(const Text: RawUTF8): AnsiString; /// convert any Unicode encoded text into Ansi Text // - use the current Language.CodePage setting, or Win-Ansi (i.e. CODEPAGE_US=1252) // if this TLanguageFile instance is nil (i.e. no custom language set) // - internaly call WideCharToMultiByte() for accurate conversion function UnicodeToAnsi(const Text: RawUnicode): AnsiString; /// convert any generic VCL Text into an Unicode encoded String function StringToUnicode(const Text: string): RawUnicode; {$ifdef PUREPASCAL}{$ifdef FPC}inline{$endif}{$endif} {$ifdef UNICODE}inline;{$endif} /// convert any generic VCL Text into an UTF-8 encoded String function StringToUTF8(const Text: string): RawUTF8; {$ifdef FPC}inline{$endif} {$ifdef UNICODE}inline;{$endif} {$ifdef UNICODE} /// convert any Ansi Text into the native Delphi unicode string // - convert into UnicodeString (for Delphi 2009/2010) // - not needed of course for Delphi 3 to 2007, since the AnsiText is to // be used as is in the VCL, with the appropriate code page and char set // - use the current Language.CodePage setting, or Win-Ansi (i.e. CODEPAGE_US=1252) // if this TLanguageFile instance is nil (i.e. no custom language set) // - internaly call MultiByteToWideChar() for accurate conversion if necessary function AnsiToString(const AnsiText: AnsiString): UnicodeString; {$endif} /// convert an UTF-8 encoded text into a VCL-ready string // - return a generic string, i.e. AnsiString with the proper code page // for Delphi 2 to 2007, or an UnicodeString for Delphi 2009/2010: this // result can be directly used by the VCL, regardless of the compiler version // - for AnsiString conversion (that is for Delphi 2 to 2007), this method // uses the current Language.CodePage setting, or Win-Ansi (i.e. CODEPAGE_US=1252) // if this TLanguageFile instance is nil (i.e. no custom language set) // - this method can be affected to a TUTF8ToStringEvent event, for // on the fly UTF-8 to VCL text conversion (see example in SQLite3UI unit) function UTF8ToString(const Text: RawUTF8): string; {$ifdef FPC}inline{$endif} {$ifdef UNICODE}inline;{$endif} /// convert an Unicode encoded text into a VCL-ready string // - return a generic string, i.e. AnsiString with the proper code page // for Delphi 2 to 2007, or an UnicodeString for Delphi 2009/2010: this // result can be directly used by the VCL, regardless of the compiler version // - for AnsiString conversion (that is for Delphi 2 to 2007), this method // uses the current Language.CodePage setting, or Win-Ansi (i.e. CODEPAGE_US=1252) // if this TLanguageFile instance is nil (i.e. no custom language set) function RawUnicodeToString(const Text: RawUnicode): string; {$ifdef FPC}inline{$endif} {$ifdef UNICODE}inline;{$endif} /// convert the supplied boolean constant into ready to be displayed text // - by default, returns 'No' for false, and 'Yes' for true // - returns the text as generic string type, ready to be used in the VCL function BooleanToString(Value: boolean): string; /// convert a TSQLRecord published property value into ready to be displayed text // - will convert any sftUTF8Text/sftAnsiText into ready to be displayed text // - will convert any sftInteger/sftFloat/sftCurrency into its textual value ................................................................................ function TimeToText(const DateTime: TDateTime): string; overload; {$ifdef HASINLINE}inline;{$endif} /// convert a time into a ready to be displayed text on the screen function TimeToText(const ISO: Iso8601): string; overload; {$ifdef HASINLINE}inline;{$endif} /// convert a time into a ready to be displayed text on the screen function TimeToText(const Time: TTimeLog): string; overload; {$ifdef HASINLINE}inline;{$endif} end; /// export the translation file into a .PO format // - the .PO format is used by the GNU gettext tool, and allow to use some // very usefull translation tools // (see @http://www.gnu.org/software/hello/manual/gettext/PO-Files.html // for documentation about the .PO format itself) // - the .PO is created from two .msg files, both contained in the SourceMsgPath // directory: the original EN.msg file and the specified SourceLanguage.msg // translated file; the resulting POFileName will be created for this language // - if not SourceMsgPath is supplied, the current directory is used (not // necessary the executable directory) procedure POExport(const SourceMsgPath, POFileName: TFileName; SourceLanguage: TLanguages); /// generic US/English date/time to VCL text conversion // - not to be used in your programs: it's just here to allow inlining of // TLanguageFile.DateTimeToText/DateToText/TimeToText function DateTimeToIso(const DateTime: TDateTime; DateOnly: boolean): string; var ................................................................................ {$endif} {$ifndef ENHANCEDRTL} /// our hooked procedure for reading a string resource // - the default one in System.pas unit is replaced by this one // - this function add caching and on the fly translation (if LoadResStringTranslate // is defined in SQLite3Commons unit) // - use "string" type, i.e. UnicodeString for Delphi 2009/2010 function LoadResString(ResStringRec: PResStringRec): string; {$endif} /// convert any generic VCL Text into an UTF-8 encoded String // - wrapper to Language.StringToUTF8 method function S2U(const Text: string): RawUTF8; {$ifdef UNICODE}inline;{$endif} /// convert an UTF-8 encoded text into a VCL-ready string // - wrapper to Language.UTF8ToString method function U2S(const Text: RawUTF8): string; {$ifdef UNICODE}inline;{$endif} /// convert a custom date/time into a VCL-ready string // - this function must be assigned to i18nDateText global var of SQLite3Commons unit // - wrapper to Language.DateTimeToText method function Iso2S(Iso: TTimeLog): string; implementation uses {$ifndef LVCL} ComCtrls, {$ifdef WITHUXTHEME} ................................................................................ function LanguageAbrToIndex(p: pAnsiChar): TLanguages; overload; begin result := TLanguages(IntegerScanIndex( @LanguageAbrInteger[low(TLanguages)], ord(high(TLanguages))+1, NormToLowerByte[ord(p[0])]+NormToLowerByte[ord(p[1])] shl 8)); end; (* procedure ValAtPChar(pc: pAnsiChar; index: integer; charLimit: AnsiChar; var result: AnsiString); var pdeb: pAnsiChar; // optimized asm label s; begin if pc=nil then exit; if index=0 then goto s; // goto is deprecated, but fast & easy in this code dec(index); while pc^<>#0 do begin if pc^=charLimit then begin if index=0 then begin inc(pc); if pc^=#10 then inc(pc); // ignore #10 s: pdeb := pc; while (pc^<>#0) and (pc^<>charLimit) do inc(pc); SetString(result,pdeb,pc-pdeb); exit; end; dec(index); end; inc(pc); end; end; function ValAt(const value: AnsiString; index: integer; charLimit: AnsiChar = ','): AnsiString; // ValAt('un,deux',1)='deux' begin result := ''; if index>=0 then ValAtPChar(pointer(value),index,charLimit,result); end; function PosEx(const SubStr, S: AnsiString; Offset: Cardinal = 1): Integer; // Faster Equivalent of Delphi 7 StrUtils.PosEx asm push ebx push esi push edx {@Str} test eax, eax jz @@NotFound {Exit if SubStr = ''} test edx, edx jz @@NotFound {Exit if Str = ''} mov esi, ecx mov ecx,[edx-4] {Length(Str)} mov ebx,[eax-4] {Length(SubStr)} add ecx, edx sub ecx, ebx {Max Start Pos for Full Match} lea edx,[edx+esi-1] {Set Start Position} cmp edx, ecx jg @@NotFound {StartPos > Max Start Pos} cmp ebx, 1 {Length(SubStr)} jle @@SingleChar {Length(SubStr) <= 1} push edi push ebp lea edi,[ebx-2] {Length(SubStr) - 2} mov esi, eax movzx ebx,[eax] {Search Character} @@Loop: {Compare 2 Characters per Loop} cmp bl,[edx] jne @@NotChar1 mov ebp, edi {Remainder} @@Char1Loop: movzx eax, word ptr [esi+ebp] cmp ax,[edx+ebp] jne @@NotChar1 sub ebp, 2 jnc @@Char1Loop pop ebp pop edi jmp @@SetResult @@NotChar1: cmp bl,[edx+1] jne @@NotChar2 mov ebp, edi {Remainder} @@Char2Loop: movzx eax, word ptr [esi+ebp] cmp ax,[edx+ebp+1] jne @@NotChar2 sub ebp, 2 jnc @@Char2Loop pop ebp pop edi jmp @@CheckResult @@NotChar2: lea edx,[edx+2] cmp edx, ecx {Next Start Position <= Max Start Position} jle @@Loop pop ebp pop edi jmp @@NotFound @@SingleChar: jl @@NotFound {Needed for Zero-Length Non-NIL Strings} movzx eax,[eax] {Search Character} @@CharLoop: cmp al,[edx] je @@SetResult cmp al,[edx+1] je @@CheckResult lea edx,[edx+2] cmp edx, ecx jle @@CharLoop @@NotFound: xor eax, eax pop edx pop esi pop ebx ret @@CheckResult: {Check within AnsiString} cmp edx, ecx jge @@NotFound add edx, 1 @@SetResult: pop ecx {@Str} pop esi pop ebx neg ecx lea eax,[edx+ecx+1] end; function StringReplaceAll(const S, OldPattern, NewPattern: AnsiString): AnsiString; // fast replacement of StringReplace(S, OldPattern, NewPattern,[rfReplaceAll]); procedure Process(j: integer); var i: integer; begin Result := ''; i := 1; repeat Result := Result+Copy(S,i,j-i)+NewPattern; i := j+length(OldPattern); j := PosEx(OldPattern, S, i); if j=0 then begin Result := Result+Copy(S, i, maxInt); break; end; until false; end; var j: integer; begin j := Pos(OldPattern, S); if j=0 then result := S else Process(j); end; function _(const English, Name: AnsiString; at: integer): AnsiString; overload; // find 'at' nth word in a list: _('A,B,C','sABC',3) = 'C' begin result := ValAt(_(English,Name), at, ','); end; function _(const English: AnsiString; const Name: AnsiString = ''): AnsiString; overload; // translate the 'English' term into current language (search Name in Translate) // $$->sharp $$$->sharp2 $$$$-sharp3 ||->#13#10 {$ifdef USESHARP} var i, L: integer; {$endif} function Correct(p: pAnsiChar): integer; var d: pAnsiChar; begin result := 0; // not used ifndef USESHARP if p=nil then exit; d := p-1; // so result := p-d -> index as AnsiString repeat case p^ of // we search for pairs -> read only twice a char #0: exit; {$ifdef USESHARP} '$': begin if result=0 then if p[-1]='$' then result := p-d-1 else if p[+1]='$' then result := p-d; inc(p,2); end; {$endif} '|': begin if p[-1]='|' then pWord(p-1)^ := $0a0d else if p[+1]='|' then pWord(p)^ := $0a0d; inc(p,2); end; else if p[1]<>#0 then inc(p,2) else exit; end; until false; end; begin if Language=nil then result := English else begin if Name='' then result := Language.Translate(English) else result := Language.Translate(Name); end; if result='' then exit; {$ifdef USESHARP} i := Correct(pointer(result)); if (i=0) or (sharp='') then exit; // no $$* to replace repeat // replace all $$.. delete(result,i,2); L := length(result); if (i<=L) and (result[i]='$') then // $$$ ou $$$$ if (i<L) and (result[i+1]='$') then begin // $$$$ = sharp3 delete(result,i,2); insert(sharp3,result,i); inc(i,length(sharp3)); end else begin delete(result,i,1); insert(sharp2,result,i); inc(i,length(sharp2)); end // $$$ = sharp2 else begin insert(sharp,result,i); inc(i,length(sharp)); end; i := posEx('$$',result,i); until i=0; {$else} Correct(pointer(result)); // only '||' -> #13#10 {$endif} end; *) const // default character set for a specific language (for GUI i18n) // list taken from http://www.webheadstart.org/xhtml/encoding // see also http://msdn2.microsoft.com/en-us/library/ms776260.aspx // DEFAULT_CHARSET is set if not known -> Win32 will take care as default locale // ANSI_CHARSET is iso-8859-1, windows-1252 ................................................................................ Jump: byte; Offset: PtrInt; end; {$ifndef ENHANCEDRTL} // code below is extracted from our Extended System.pas unit, and // use the generic string type (i.e. UnicodeString for Delphi 2009/2010) const LoadResStringCacheSize = 512; // cache makes it faster, even more when using on the fly translations // 512 is a reasonnable value, never reached in practice var CacheRes: array[0..LoadResStringCacheSize-1] of PResStringRec; CacheResValue: array of string; ................................................................................ PatchPositionForm: PPatchEvent = nil; PatchFrame, OriginalFrame: TPatchEvent; PatchPositionFrame: PPatchEvent = nil; procedure PatchCreate; var ov: cardinal; begin // hook TForm: PatchPositionForm := PPatchEvent(@THookedForm.DoCreate); OriginalForm := PatchPositionForm^; PatchForm.Jump := $E9; // Jmp opcode PatchForm.Offset := PtrInt(@THookedForm.HookedDoCreate)-PtrInt(PatchPositionForm)-5; if not VirtualProtect(PatchPositionForm, 5, PAGE_EXECUTE_READWRITE, @ov) then RaiseLastOSError; PatchPositionForm^ := PatchForm; // enable Hook // hook TFrame: PatchPositionFrame := PPatchEvent(@TCustomFrame.Create); OriginalFrame := PatchPositionFrame^; PatchFrame.Jump := $E9; // Jmp opcode PatchFrame.Offset := PtrInt(@THookedFrame.Create)-PtrInt(PatchPositionFrame)-5; if not VirtualProtect(PatchPositionFrame, 5, PAGE_EXECUTE_READWRITE, @ov) then RaiseLastOSError; PatchPositionFrame^ := PatchFrame; // enable Hook ................................................................................ if GetThreadLocale<>LCID then // force locale settings if different if SetThreadLocale(LCID) then GetFormatSettings; // resets all locale-specific variables {$ifdef UNICODE} SetMultiByteConversionCodePage(CodePage); // for default AnsiString handling {$endif} {$endif} for c := #0 to #255 do begin i18nToUpper[c] := c; i18nToLower[c] := c; end; CharUpperBuffA(i18nToUpper,256); // get values from current user locale CharLowerBuffA(i18nToLower,256); if not(CharSet in [GB2312_CHARSET,SHIFTJIS_CHARSET,HANGEUL_CHARSET,ARABIC_CHARSET]) and ................................................................................ SetCurrentLanguage(LanguageAbrToIndex(value)); end; function ProgramName: AnsiString; var i: integer; begin result := AnsiString(ExtractFileName(paramstr(0))); i := pos(RawUTF8('.'),RawUTF8(result)); if i>0 then Setlength(result,i-1); end; {$ifdef USEFORMCREATEHOOK} function i18nAddLanguageItems(MsgPath: TFileName; List: TStrings): integer; ................................................................................ {$ifdef USEFORMCREATEHOOK} if Language<>nil then PatchCreate; // only patch TForm and TFrame if not english {$endif USEFORMCREATEHOOK} end; {$endif} { TLanguageFile } function AnyTextFileToString(const FileName: TFileName; Lang: TLanguages): string; // get text File contents (even Unicode or UTF8) // - into Charset-compatible AnsiString for Delphi 2 to 2007 // - into a UnicodeString for Delphi 2009/2010 var CodePage: cardinal; unicode: RawUnicode; Len, L, LU: integer; {$ifdef UNICODE} Tmp: RawByteString; {$endif} begin result := ''; if FileExists(FileName) then with THeapMemoryStream.Create do try LoadFromFile(FileName); Len := Size; if Len<4 then exit; if Lang=LANGUAGE_NONE then CodePage := CODEPAGE_US else CodePage := CharSetToCodePage(LanguageCharSet[Lang]); if PWord(Memory)^=$FEFF then begin // Unicode text file: L := (integer(Len)-2) shr 1; // calculate WideChar count {$ifdef UNICODE} SetString(result,PWideChar(PtrInt(Memory)+2),L); {$else} SetLength(result,L*4); // enough place SetLength(result, WideCharToMultiByte(CodePage, 0, pointer(PtrInt(Memory)+2), L, pointer(result), L*4, nil, nil)); {$endif} end else if (PWord(Memory)^=$BBEF) and (PAnsiChar(Memory)[2]=#$BF) then begin // UTF8 text file: LU := integer(Len)-3; SetLength(unicode,LU*2); // enough place for UTF-8 into Unicode conversion L := UTF8ToWideChar(pointer(unicode), pointer(PtrInt(Memory)+3), LU); if L>0 then begin {$ifdef UNICODE} SetString(result,PWideChar(pointer(unicode)),L); {$else} SetLength(result,L*4); // enough place SetLength(result, WideCharToMultiByte(CodePage, 0, pointer(unicode), L, pointer(result), L*4, nil, nil)); {$endif} end else result := ''; end else begin // WinAnsi text file: assume file encoding is CodePage {$ifdef UNICODE} SetString(tmp,PAnsiChar(Memory),Len); PWord(PtrInt(tmp)-12)^ := CodePage; // faster than SetCodePage() result := string(tmp); // lets the RTL do the conversion for us {$else} SetString(result,PAnsiChar(Memory),Len); (* if CodePage<>CODEPAGE_US then begin // non ansi codepage: correct ambiguous chars // -> non CP-1252 languages should use UTF-8 or Unicode text encoding SetLength(unicode,Len*2); WinAnsiToUnicodeBuffer(result,pointer(unicode)); // convert '�'->'c' e.g. SetLength(result, WideCharToMultiByte(CodePage, 0, pointer(unicode), Len, pointer(result), length(result), nil, nil)); end; *) {$endif} end; finally Free; // THeapMemoryStream end; end; constructor TLanguageFile.Create(aLanguageLocale: TLanguages); // FR.msg, DE.msg, JP.msg files must be in the .exe directory begin Create(FileName(aLanguageLocale),aLanguageLocale); end; constructor TLanguageFile.Create(const aFileName: TFileName; aLanguageLocale: TLanguages); begin Language.Fill(aLanguageLocale); LoadFromFile(aFileName); end; destructor TLanguageFile.Destroy; begin FreeAndNil(Messages); inherited; end; class function TLanguageFile.FileName(aLanguageLocale: TLanguages): TFileName; begin if aLanguageLocale<>LANGUAGE_NONE then result := ExtractFilePath(paramstr(0))+TFileName(LanguageAbr[aLanguageLocale])+'.msg' else result := ''; end; {$ifndef USEFORMCREATEHOOK} procedure TLanguageFile.FormTranslate(Forms: array of TCustomForm); var f: integer; begin ................................................................................ H := Messages.Count - 1; while L <= H do begin // use fast binary search algorithm I := (L + H) shr 1; V := cardinal(Messages.Objects[I]); // our custom Classes.pas unit is fast enough if V<Hash then L := I+1 else if V=Hash then begin result := Messages.Strings[I]; // UnicodeString on Delphi 2009/2010 exit; end else H := I-1; end; end; result := ''; end; ................................................................................ FreeAndNil(Messages); fBooleanToString[false] := B2SS[false]; fBooleanToString[true] := B2SS[true]; Text := ''; if not FileExists(aFileName) then exit; // 1. read .msg file with appropriate UTF8 or Unicode conversion Text := AnyTextFileToString(aFileName,Language.Index); // appropriate conversion // 2. fill Translation[] and Messages[] Messages := TStringList.Create; P := pointer(Text); {$ifdef UNICODE} if FindSectionFirstLineW(P,'MESSAGES]') then while (P<>nil) and (P^<>'[') do begin H := GetNextItemCardinalW(P,'='); ................................................................................ procedure TLanguageFile.Translate(var English: string); // case-sensitive (same as standard gettext) var result: string; begin result := FindMessage(Hash32( // resourcestring are expected to be in English, that is WinAnsi encoded // before being hashed {$ifdef UNICODE}RawUnicodeToWinAnsi(pointer(English),length(English)) {$else}English{$endif})); if result<>'' then English := result; end; procedure GetText(var Text: string); // used for System.LoadResStringTranslate case-sensitive (same as standard gettext) var Translated: string; begin if Language<>nil then begin Translated := Language.FindMessage(Hash32( // resourcestring are expected to be in English, that is WinAnsi encoded // before being hashed {$ifdef UNICODE}RawUnicodeToWinAnsi(pointer(Text),length(Text)) {$else}Text{$endif})); if Translated<>'' then Text := Translated; end; end; function _(const English: WinAnsiString): string; begin if Language<>nil then begin result := Language.FindMessage(Hash32(English)); if result='' then result := string(English); end else result := string(English); end; {$ifdef UNICODE} function TLanguageFile.AnsiToString(const AnsiText: AnsiString): UnicodeString; var tmp: RawUnicode; begin tmp := AnsiToUnicode(AnsiText); SetString(result,PWideChar(pointer(tmp)),length(tmp)shr 1); end; {$endif} {$ifdef UNICODE} function TLanguageFile.StringToUnicode(const Text: string): RawUnicode; begin SetString(result,PAnsiChar(pointer(Text)),length(Text)*2+1); {$else} {$ifdef PUREPASCAL} function TLanguageFile.StringToUnicode(const Text: string): RawUnicode; begin result := AnsiToUnicode(Text); {$else} function TLanguageFile.StringToUnicode(const Text: string): RawUnicode; asm jmp AnsiToUnicode {$endif} {$endif} end; function TLanguageFile.StringToUTF8(const Text: string): RawUTF8; begin {$ifdef UNICODE} result := RawUnicodeToUtf8(PWideChar(pointer(Text)),length(Text)); {$else} result := RawUnicodeToUtf8(AnsiToUnicode(Text)); {$endif} end; function S2U(const Text: string): RawUTF8; begin {$ifdef UNICODE} result := RawUnicodeToUtf8(PWideChar(pointer(Text)),length(Text)); {$else} result := RawUnicodeToUtf8(Language.AnsiToUnicode(Text)); {$endif} end; function U2S(const Text: RawUTF8): string; begin {$ifdef UNICODE} result := UTF8DecodeToUnicodeString(pointer(Text),length(Text)); {$else} // fast conversion using a temp RawUnicode result := Language.RawUnicodeToString(Utf8DecodeToRawUnicode(pointer(Text),length(Text))); {$endif} end; function Iso2S(Iso: TTimeLog): string; begin if Iso=0 then result := '' else result := Language.DateTimeToText(Iso); end; function TLanguageFile.AnsiToUnicode(const AnsiText: AnsiString): RawUnicode; var CodePage: Cardinal; InputLength, OutputLength: Integer; begin if self=nil then CodePage := CODEPAGE_US else CodePage := Language.CodePage; if CodePage=CP_UTF8 then // CP_UTF8 not supported on Windows 95 Result := Utf8DecodeToRawUnicode(RawUTF8(AnsiText)) else if CodePage=CODEPAGE_US then // fast integrated CODEPAGE_US conversion Result := WinAnsiToRawUnicode(WinAnsiString(AnsiText)) else begin // compute result length InputLength := Length(AnsiText); OutputLength := MultiByteToWideChar(CodePage, 0, pointer(AnsiText), InputLength, nil, 0); // make conversion Result := ''; // somewhat faster if memory is released before SetLength() SetLength(Result, OutputLength*2+1); // +1 = space for last WideChar(#0) MultiByteToWideChar(CodePage, 0, pointer(AnsiText), InputLength, pointer(Result), OutputLength); PWordArray(pointer(Result))^[OutputLength] := 0; // set last WideChar to #0 {$ifdef UNICODE} PWord(PtrInt(result)-12)^ := 1200; // use only SetLength() -> force set code page {$endif} end; end; function TLanguageFile.AnsiToUTF8(const AnsiText: AnsiString): RawUTF8; begin // fast conversion using a temp RawUnicode result := RawUnicodeToUtf8(AnsiToUnicode(AnsiText)); end; function TLanguageFile.UTF8ToString(const Text: RawUTF8): string; {$ifdef UNICODE} begin result := UTF8DecodeToUnicodeString(pointer(Text),length(Text)); end; {$else} begin // fast conversion using a temp RawUnicode result := RawUnicodeToString(Utf8DecodeToRawUnicode(pointer(Text),length(Text))); end; {$endif} {$ifdef UNICODE} function TLanguageFile.RawUnicodeToString(const Text: RawUnicode): string; begin SetString(result,PWideChar(pointer(Text)),length(Text)shr 1); end; {$else} function TLanguageFile.RawUnicodeToString(const Text: RawUnicode): string; begin result := UnicodeToAnsi(Text); end; {$endif} function TLanguageFile.UTF8ToAnsi(const Text: RawUTF8): AnsiString; begin result := UnicodeToAnsi(Utf8DecodeToRawUnicode(pointer(Text),length(Text))); end; function TLanguageFile.BooleanToString(Value: boolean): string; begin if self=nil then result := B2SS[Value] else begin result := fBooleanToString[Value]; if result='' then result := B2SS[Value]; ................................................................................ begin {$ifndef LVCL}if Self=nil then{$endif} result := DateTimeToIso(Iso8601(Time).ToTime,false) {$ifndef LVCL} else DateTimeToString(Result, TimeFmt, Iso8601(Time).ToTime); {$endif} end; function TLanguageFile.UnicodeToAnsi(const Text: RawUnicode): AnsiString; var WideCharCount: integer; CodePage: cardinal; begin result := ''; if Text='' then exit; if self=nil then CodePage := CODEPAGE_US else CodePage := Language.CodePage; WideCharCount := length(Text) shr 1; if CodePage=CODEPAGE_US then begin // fast internal conversion SetLength(result,WideCharCount); RawUnicodeToWinPChar(pointer(result),pointer(Text),WideCharCount); end else begin SetLength(result,WideCharCount*4); // enough place for MBCS result SetLength(result,WideCharToMultiByte(CodePage, 0, pointer(Text), WideCharCount, pointer(result), length(result), nil, nil)); // perform conversion end; {$ifdef UNICODE} PWord(PtrInt(result)-12)^ := CodePage; // use only SetLength() -> force set code page {$endif} end; procedure POExport(const SourceMsgPath, POFileName: TFileName; SourceLanguage: TLanguages); var English, Source: TLanguageFile; SourceDir: TFileName; Dest: TFileStream; W: TTextWriter; i: integer; E: string; ................................................................................ try W.AddLine('"Content-Type: text/plain; charset=UTF-8\n"'#13#10+ '"Content-Transfer-Encoding: 8bit\n"'#13#10); for i := 0 to English.Messages.Count - 1 do begin E := English.Messages[i]; Source.Translate(E); W.Add('msgid "%"'#13'msgstr"'#13#13, // #13 will be written as #13#10 [English.StringToUTF8(StringReplace(English.Messages[i],#13#10,'"'#13#10'"',[rfReplaceAll])), Source.StringToUTF8(StringReplace(E,#13#10,'"'#13#10'"',[rfReplaceAll]))]); end; finally W.Free; Source.Free; English.Free; Dest.Free; end; end; { TLanguage } function TLanguage.Abr: RawByteString; begin if Index=LANGUAGE_NONE then result := '' else ................................................................................ begin result := LanguageName(Index); end; {$ifdef EXTRACTALLRESOURCES} var // expect english text, converted into WinAnsi before Hash32() // - Delphi 2009/2010 will do the implicit codepage conversion // (usefull for chars with unicode value >255, e.g. '�') CB_EnumStrings: TWinAnsiDynArray; /// number of items in CB_EnumStrings[] CB_EnumStringsCount: integer; // store the curently identified Hash32() of each english text CB_Enum: TDynArrayHashed; ................................................................................ // code below use the string generic type, which is prefered for the RTTI var F: ^Text absolute lparam; Reader: TReader; procedure ConvertObject(const ParentName, ObjectName: string); procedure ConvertValue(const PropName, LastPropName: string); procedure WriteProperty(const Value: WinAnsiString); // for Delphi 2009/2010, Value: string was converted into a WinAnsiString begin // ignore components which names begin with '_' if (PropName<>LastPropName) and (PropName<>'') then // PropName=Label1.Caption if PropName[1]='_' then // ignore _Copyright.Caption exit; // write value if (LastPropName='Caption') or (LastPropName='EditLabel.Caption') or ................................................................................ ClassList: TList; procedure AddEnum(T: PEnumType); var index: integer; begin for index := T^.MinValue to T^.MaxValue do AddOnceDynArray(StringToWinAnsi(T^.GetCaption(index))); // for Delphi 2009/2010/XE: CaptionName converted into a WinAnsiString end; procedure AddClass(C: TClass); var i: integer; P: PPropInfo; CP: PClassProp; begin if (C=nil) or (ClassList.IndexOf(C)>=0) then ................................................................................ AddClass(C.ClassParent); // add parent properties first CP := InternalClassProp(C); if CP=nil then exit; P := @CP^.PropList; for i := 1 to CP^.PropCount do begin // add all field names AddOnceDynArray(StringToWinAnsi(TSQLRecord.CaptionName(@P^.ShortName))); // for Delphi 2009/2010/XE: CaptionName converted into a WinAnsiString with P^.PropType^^ do case Kind of tkClass: // add contained objects AddClass(ClassType^.ClassType); tkEnumeration: // add enumeration values AddEnum(EnumBaseType); tkSet: ................................................................................ if Objects[i].InheritsFrom(TSQLModel) then begin AddOnceDynArray('ID'); // ID property is never published, but always here // add custom captions for all tables of a database model with TSQLModel(Objects[i]) do for index := 0 to high(Tables) do with Tables[index] do begin // TSQLRecord.CaptionName() may be overriden AddOnceDynArray(StringToWinAnsi(CaptionName(nil))); // add table name // for Delphi 2009/2010, CaptionName(): string will be converted into a WinAnsiString with InternalClassProp(Tables[index])^ do begin P := @PropList; for j := 1 to PropCount do begin // add all field names AddOnceDynArray(StringToWinAnsi(CaptionName(@P^.ShortName))); P := P^.Next; end; end; |
| | | | > > > > > > | | | | | | | | | | | | | < < < < < < < < < > | > | < < < < < < | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > | | | > < > | > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > | < < < | < < < < | < < < > | < < < < | | > > > | > | < | < | > | < < < < < < < < < < < < < < < < < < < < < > > > > > < < < < < < < < < > > > > > | | | | < | < < < < < < | | < | < < < > | < | < < < < < < > > | < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | > > | | | | | |
1 2 3 4 5 6 7 8 9 10 .. 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 ... 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 ... 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 ... 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 ... 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 ... 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 ... 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 ... 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 ... 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 ... 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 ... 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 ... 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 ... 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 ... 745 746 747 748 749 750 751 752 753 754 755 756 757 758 ... 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 ... 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 .... 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 .... 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 .... 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 .... 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 .... 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 .... 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 .... 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 .... 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 .... 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 .... 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 .... 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 .... 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 .... 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 |
/// internationalization (i18n) routines and classes // - this unit is a part of the freeware Synopse SQLite3 database framework, // licensed under a MPL/GPL/LGPL tri-license; version 1.16 unit SQLite3i18n; (* This file is part of Synopse SQLite3 database framework. Synopse SQLite3 database framework. Copyright (C) 2012 Arnaud Bouchez Synopse Informatique - http://synopse.info ................................................................................ i18n routines for the Synopse SQLite3 database framework ********************************************************** - works internaly with the string type, that is AnsiString with code pages and charsets for compiler versions earlier than Delphi 2009, and UnicodeString since Delphi 2009 and up -> so it's 100% VCL compatible - can load language definition files encoded in Unicode or UTF8 - auto-call SetThreadLocale() for full application i18n - update default locale settings values (date,currency..) - force english locale settings on non english system (consistent with UI) - handle multiple charsets (standard or custom VCL components compatible) - resourcestring on-the-fly translation - resourcestring access using fast cache, even without translation; use this ................................................................................ i18nCompareStr/Text() is to be used instead 0.2.1 bug found in i18nInnerCompareText() 1.0 First public release of the Synopse SQLite3 Framework Version 1.1 - 14 January 2009: - allow to get rid of our Enhanced Runtime Library dependency if not available (e.g. for FPC or on cross-platform, or on Delphi version newer than Delphi 7) - attempt to reach Delphi 2009 and up compilation (string=UnicodeString): the UNICODE conditional will adapt the framework to these compilers (you shouldn't have to change any conditinal define below - generic string type is now used for all i18n of text: in Delphi 2009 and up, it will be an UnicodeString, but with earlier version of Delphi, string is an AnsiString with the codepage of the current selected language - attempt to reach Free Pascal Compiler 2.4.0 compatibility - LoadResStringTranslate() and resourcestring caching are defined in the SQLite3Commons unit, if our Enhanced Run Time Library (or LVCL) is not used Version 1.2 - 18 January 2010 ................................................................................ Version 1.15 - compatibility with Delphi XE2 - fix endless recursion loop in ExtractAllResources for nested classes - several changes in ExtractAllResources implementation - handle TModTime published property / sftModTime SQL field - handle TCreateTime published property / sftCreateTime SQL field Version 1.16 - uses new generic TSynAnsiConvert classes for code page process: since TLanguageFile.Create() will set CurrentAnsiConvert global instance, applications should use CurrentAnsiConvert instead of previous TLanguageFile methods *) {$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER interface {.$D-,L-} ................................................................................ {.$define EXTRACTALLRESOURCES} // must be set globally for the whole application {.$define ENHANCEDRTL} { define this if you DID install our Enhanced Runtime library: it has already hacked the "legacy" LoadResString() procedure and added a LoadResStringTranslate() - it will be unset automaticaly (see below) for Delphi 2009 and up, since no version of our Enhanced RTL exists for these compilers - this conditional must be defined in both SQLite3Commons and SQLite3i18n units, or (even better) globally in the Project options } {.$define USESHARP} // if defined, $$,$$$,$$$ are replaced with some globals in _() {$ifndef NOI18N} // with this global define, you can use the unit procs, without the UI i18n {$define USEFORMCREATEHOOK} // if defined, all forms will be auto-translated, even 3rd party ones // just before an OnCreate handler would be called {$endif} {$ifdef LVCL} // the LVCL don't have TForm.DoCreate and such // -> it's easier to explicitely change the captions from code in LVCL {$undef USEFORMCREATEHOOK} {$endif} uses Windows, SysUtils, Classes, {$ifdef USEFORMCREATEHOOK} {$ifndef LVCL} Menus, ................................................................................ {$endif USEFORMCREATEHOOK} StdCtrls, Forms, SynCommons, // some basic types SQLite3Commons; // need extended RTTI information {$ifdef UNICODE} {$undef ENHANCEDRTL} // no version of our Enhanced RTL exists for Delphi 2009 and up {$endif} {$ifdef LVCL} // LVCL system.pas doesn't implement LoadResStringTranslate() and won't need it {$define ENHANCEDRTL} {$endif} ................................................................................ LCID: cardinal; /// initializes all TLanguage object fields for a specific language procedure Fill(Language: TLanguages); /// returns two-chars long language abreviation ('HE' e.g.) function Abr: RawByteString; /// returns fully qualified language name ('Hebrew' e.g.), // using current UI language // - return "string" type, i.e. UnicodeString for Delphi 2009 and up function Name: string; end; var /// the global Language used by the User Interface, // as updated by the last SetCurrentLanguage() call CurrentLanguage: TLanguage = ( ................................................................................ // change the current language in the registry procedure i18nAddLanguageCombo(const MsgPath: TFileName; Combo: TComboBox); {$endif} /// save the default language to the registry // - language will be changed at next startup // - return a message ready to be displayed on the screen // - return "string" type, i.e. UnicodeString for Delphi 2009 and up function i18nLanguageToRegistry(const Language: TLanguages): string; /// get the default language from the registry function i18nRegistryToLanguage: TLanguages; resourcestring /// this message will be displayed on the screen when the user change the ................................................................................ procedure SetCurrentLanguage(const value: RawUTF8); overload; {$endif} {$endif} /// Return the language text, ready to be displayed (after translation if // necessary) // - e.g. LanguageName(lngEnglish)='English' // - return "string" type, i.e. UnicodeString for Delphi 2009 and up function LanguageName(aLanguage: TLanguages): string; /// LanguageAbrToIndex('GR')=1, e.g. // - return LANGUAGE_NONE if not found function LanguageAbrToIndex(const value: RawUTF8): TLanguages; overload; /// LanguageAbrToIndex('GR')=1, e.g. ................................................................................ sharp2: string = ''; sharp3: string = ''; {$endif} /// translate the 'Text' term into current language, with no || nor $$[$[$]] // - LoadResStringTranslate of our customized system.pas points to this procedure // - therefore, direct use of LoadResStringTranslate() is better in apps // - expect "string" type, i.e. UnicodeString for Delphi 2009 and up procedure GetText(var Text: string); /// translate the 'English' term into current language // - you should use resourcestring instead of this function // - call interenaly GetText() procedure, i.e. LoadResStringTranslate() function _(const English: WinAnsiString): string; /// get text File contents (even Unicode or UTF8) and convert it into a // Charset-compatible AnsiString (for Delphi 7) or an UnicodeString (for Delphi // 2009 and up) according to any BOM marker at the beginning of the file // - by use of this function, the TLanguageFile.LoadFromFile() method is // able to display any Unicode message into the 8 bit standard Delphi VCL, // (for Delphi 2 to 2007) or with the new Unicode VCL (for Delphi 2009 and up) // - before Delphi 2009, the current string code page is used (i.e. CurrentAnsiConvert) function AnyTextFileToString(const FileName: TFileName): string; var /// a table used for fast conversion to lowercase, according to the current language // - can NOT be used for MBCS strings (with such code pages, you should use windows // slow but accurate API) i18nToLower, /// a table used for fast conversion to uppercase, according to the current language ................................................................................ reload the updated translations. } TLanguageFile = class protected /// the content of the .msg file, translated into generic VCL string // - [Messages] section is expanded into Messages TStringList (see below) // - for Forms translations: [FormName] sections, with Properties=UI Text pairs // - is either an AnsiString in the current code page, or an UnicodeString // (in case of Delphi 2009 and up, that is a UNICODE compiler) Text: string; /// copy of translated strings from [Messages] section // - Objects[] contain pointer(Hash32(WinAnsiEncodedMessage)) // - Strings[] contain Message text, in UnicodeString for Delphi 2009 and up Messages: TStringList; {$ifndef LVCL} { LVCL will use always the ISO 8601 generic text format } /// format string used to convert a date value to a text // - the expected format is the one used by the FormatDateTime() function // - the current system format, depending on the current language, is used, // then overriden by a DateFmt= entry in the .msg file content DateFmt: string; ................................................................................ procedure LanguageClick(Sender: TObject); {$endif USEFORMCREATEHOOK} /// get corresponding *.msg translation text file name from current exe directory // - e.g. return 'C:\Program Files\MyApplication\FR.msg' class function FileName(aLanguageLocale: TLanguages): TFileName; /// return a translated text from a Hash32(WinAnsiString) value // - search is very fast (use binary search algorithm) // - return a generic VCL string (i.e. UnicodeString for Delphi 2009 and up) function FindMessage(Hash: cardinal): string; public /// identify the current language Language: TLanguage; /// specify a text file containing the translation messages for a language constructor Create(const aFileName: TFileName; aLanguageLocale: TLanguages); overload; /// load corresponding *.msg translation text file from the current exe directory constructor Create(aLanguageLocale: TLanguages); overload; /// free translation tables memory destructor Destroy; override; /// fill translation tables from text file containing the translation messages // - handle on the fly UTF-8 and UNICODE decode into the corresponding ANSI // CHARSET, or into UnicodeString for Delphi 2009 and up procedure LoadFromFile(const aFileName: TFileName); /// translate an English string into a localized string // - English is case-sensitive (same as standard gettext) // - translations are stored in Messages[] and Text properties // - expect parameter as generic VCL string (i.e. UnicodeString for Delphi 2009 and up) procedure Translate(var English: string); /// translate the english captions of a TForm into the current UI language // - must be called once with english captions // - call automaticaly if conditional USEFORMCREATEHOOK is defined procedure FormTranslateOne(aForm: TComponent); {$ifndef USEFORMCREATEHOOK} procedure FormTranslate(Forms: array of TCustomForm); {$endif USEFORMCREATEHOOK} /// read a parameter, stored in the .msg file before any [Section] function ReadParam(const ParamName: RawUTF8): string; /// convert the supplied boolean constant into ready to be displayed text // - by default, returns 'No' for false, and 'Yes' for true // - returns the text as generic string type, ready to be used in the VCL function BooleanToString(Value: boolean): string; /// convert a TSQLRecord published property value into ready to be displayed text // - will convert any sftUTF8Text/sftAnsiText into ready to be displayed text // - will convert any sftInteger/sftFloat/sftCurrency into its textual value ................................................................................ function TimeToText(const DateTime: TDateTime): string; overload; {$ifdef HASINLINE}inline;{$endif} /// convert a time into a ready to be displayed text on the screen function TimeToText(const ISO: Iso8601): string; overload; {$ifdef HASINLINE}inline;{$endif} /// convert a time into a ready to be displayed text on the screen function TimeToText(const Time: TTimeLog): string; overload; {$ifdef HASINLINE}inline;{$endif} end; (* /// export the translation file into a .PO format // - the .PO format is used by the GNU gettext tool, and allow to use some // very usefull translation tools // (see @http://www.gnu.org/software/hello/manual/gettext/PO-Files.html // for documentation about the .PO format itself) // - the .PO is created from two .msg files, both contained in the SourceMsgPath // directory: the original EN.msg file and the specified SourceLanguage.msg // translated file; the resulting POFileName will be created for this language // - if not SourceMsgPath is supplied, the current directory is used (not // necessary the executable directory) procedure POExport(const SourceMsgPath, POFileName: TFileName; SourceLanguage: TLanguages); *) /// generic US/English date/time to VCL text conversion // - not to be used in your programs: it's just here to allow inlining of // TLanguageFile.DateTimeToText/DateToText/TimeToText function DateTimeToIso(const DateTime: TDateTime; DateOnly: boolean): string; var ................................................................................ {$endif} {$ifndef ENHANCEDRTL} /// our hooked procedure for reading a string resource // - the default one in System.pas unit is replaced by this one // - this function add caching and on the fly translation (if LoadResStringTranslate // is defined in SQLite3Commons unit) // - use "string" type, i.e. UnicodeString for Delphi 2009 and up function LoadResString(ResStringRec: PResStringRec): string; {$endif} /// convert any generic VCL Text into an UTF-8 encoded String // - same as SynCommons.StringToUTF8() function S2U(const Text: string): RawUTF8; {$ifdef HASINLINE}inline;{$endif} /// convert an UTF-8 encoded text into a VCL-ready string // - same as SynCommons.UTF8ToString() function U2S(const Text: RawUTF8): string; {$ifdef HASINLINE}inline;{$endif} /// convert a custom date/time into a VCL-ready string // - this function must be assigned to i18nDateText global var of SQLite3Commons unit // - wrapper to Language.DateTimeToText method function Iso2S(Iso: TTimeLog): string; implementation uses {$ifndef LVCL} ComCtrls, {$ifdef WITHUXTHEME} ................................................................................ function LanguageAbrToIndex(p: pAnsiChar): TLanguages; overload; begin result := TLanguages(IntegerScanIndex( @LanguageAbrInteger[low(TLanguages)], ord(high(TLanguages))+1, NormToLowerByte[ord(p[0])]+NormToLowerByte[ord(p[1])] shl 8)); end; const // default character set for a specific language (for GUI i18n) // list taken from http://www.webheadstart.org/xhtml/encoding // see also http://msdn2.microsoft.com/en-us/library/ms776260.aspx // DEFAULT_CHARSET is set if not known -> Win32 will take care as default locale // ANSI_CHARSET is iso-8859-1, windows-1252 ................................................................................ Jump: byte; Offset: PtrInt; end; {$ifndef ENHANCEDRTL} // code below is extracted from our Extended System.pas unit, and // use the generic string type (i.e. UnicodeString for Delphi 2009 and up) const LoadResStringCacheSize = 512; // cache makes it faster, even more when using on the fly translations // 512 is a reasonnable value, never reached in practice var CacheRes: array[0..LoadResStringCacheSize-1] of PResStringRec; CacheResValue: array of string; ................................................................................ PatchPositionForm: PPatchEvent = nil; PatchFrame, OriginalFrame: TPatchEvent; PatchPositionFrame: PPatchEvent = nil; procedure PatchCreate; var ov: cardinal; begin // hook TForm PatchPositionForm := PPatchEvent(@THookedForm.DoCreate); OriginalForm := PatchPositionForm^; PatchForm.Jump := $E9; // Jmp opcode PatchForm.Offset := PtrInt(@THookedForm.HookedDoCreate)-PtrInt(PatchPositionForm)-5; if not VirtualProtect(PatchPositionForm, 5, PAGE_EXECUTE_READWRITE, @ov) then RaiseLastOSError; PatchPositionForm^ := PatchForm; // enable Hook // hook TFrame PatchPositionFrame := PPatchEvent(@TCustomFrame.Create); OriginalFrame := PatchPositionFrame^; PatchFrame.Jump := $E9; // Jmp opcode PatchFrame.Offset := PtrInt(@THookedFrame.Create)-PtrInt(PatchPositionFrame)-5; if not VirtualProtect(PatchPositionFrame, 5, PAGE_EXECUTE_READWRITE, @ov) then RaiseLastOSError; PatchPositionFrame^ := PatchFrame; // enable Hook ................................................................................ if GetThreadLocale<>LCID then // force locale settings if different if SetThreadLocale(LCID) then GetFormatSettings; // resets all locale-specific variables {$ifdef UNICODE} SetMultiByteConversionCodePage(CodePage); // for default AnsiString handling {$endif} {$endif} CurrentAnsiConvert := TSynAnsiConvert.Engine(CodePage); // redefine from GetACP for c := #0 to #255 do begin i18nToUpper[c] := c; i18nToLower[c] := c; end; CharUpperBuffA(i18nToUpper,256); // get values from current user locale CharLowerBuffA(i18nToLower,256); if not(CharSet in [GB2312_CHARSET,SHIFTJIS_CHARSET,HANGEUL_CHARSET,ARABIC_CHARSET]) and ................................................................................ SetCurrentLanguage(LanguageAbrToIndex(value)); end; function ProgramName: AnsiString; var i: integer; begin result := AnsiString(ExtractFileName(paramstr(0))); i := Pos(RawUTF8('.'),RawUTF8(result)); if i>0 then Setlength(result,i-1); end; {$ifdef USEFORMCREATEHOOK} function i18nAddLanguageItems(MsgPath: TFileName; List: TStrings): integer; ................................................................................ {$ifdef USEFORMCREATEHOOK} if Language<>nil then PatchCreate; // only patch TForm and TFrame if not english {$endif USEFORMCREATEHOOK} end; {$endif} function AnyTextFileToString(const FileName: TFileName): string; // get text File contents (even Unicode or UTF8) var Len: integer; Kind: (isUnicode, isUTF8, isAnsi); begin result := ''; if FileExists(FileName) then with TSynMemoryStreamMapped.Create(FileName) do try Len := Size; if Len<4 then exit; if PWord(Memory)^=$FEFF then Kind := isUnicode else if (PWord(Memory)^=$BBEF) and (PByteArray(Memory)[2]=$BF) then Kind := isUTF8 else Kind := isAnsi; case Kind of {$ifdef UNICODE} isUnicode: SetString(result,PWideChar(PtrInt(Memory)+2),(Len-2) shr 1); isUTF8: result := UTF8DecodeToString(pointer(PtrInt(Memory)+3),Len-3); isAnsi: result := CurrentAnsiConvert.AnsiToUnicodeString(Memory,Len); {$else} isUnicode: result := CurrentAnsiConvert.UnicodeBufferToAnsi(PWideChar(PtrInt(Memory)+2),(Len-2) shr 1); isUTF8: result := CurrentAnsiConvert.UTF8BufferToAnsi(pointer(PtrInt(Memory)+3),Len-3); isAnsi: SetString(result,PAnsiChar(Memory),Len); {$endif} end; finally Free; // THeapMemoryStream end; end; { TLanguageFile } constructor TLanguageFile.Create(aLanguageLocale: TLanguages); // FR.msg, DE.msg, JP.msg files must be in the .exe directory begin Create(FileName(aLanguageLocale),aLanguageLocale); end; constructor TLanguageFile.Create(const aFileName: TFileName; aLanguageLocale: TLanguages); begin Language.Fill(aLanguageLocale); CurrentAnsiConvert := TSynAnsiConvert.Engine(Language.CodePage); LoadFromFile(aFileName); end; destructor TLanguageFile.Destroy; begin FreeAndNil(Messages); inherited; end; class function TLanguageFile.FileName(aLanguageLocale: TLanguages): TFileName; begin if aLanguageLocale<>LANGUAGE_NONE then result := ExtractFilePath(paramstr(0))+ Ansi7ToString(LanguageAbr[aLanguageLocale])+'.msg' else result := ''; end; {$ifndef USEFORMCREATEHOOK} procedure TLanguageFile.FormTranslate(Forms: array of TCustomForm); var f: integer; begin ................................................................................ H := Messages.Count - 1; while L <= H do begin // use fast binary search algorithm I := (L + H) shr 1; V := cardinal(Messages.Objects[I]); // our custom Classes.pas unit is fast enough if V<Hash then L := I+1 else if V=Hash then begin result := Messages.Strings[I]; // UnicodeString on Delphi 2009 and up exit; end else H := I-1; end; end; result := ''; end; ................................................................................ FreeAndNil(Messages); fBooleanToString[false] := B2SS[false]; fBooleanToString[true] := B2SS[true]; Text := ''; if not FileExists(aFileName) then exit; // 1. read .msg file with appropriate UTF8 or Unicode conversion Text := AnyTextFileToString(aFileName); // appropriate conversion // 2. fill Translation[] and Messages[] Messages := TStringList.Create; P := pointer(Text); {$ifdef UNICODE} if FindSectionFirstLineW(P,'MESSAGES]') then while (P<>nil) and (P^<>'[') do begin H := GetNextItemCardinalW(P,'='); ................................................................................ procedure TLanguageFile.Translate(var English: string); // case-sensitive (same as standard gettext) var result: string; begin result := FindMessage(Hash32( // resourcestring are expected to be in English, that is WinAnsi encoded // before being hashed {$ifdef UNICODE}WinAnsiConvert.UnicodeBufferToAnsi(pointer(English),length(English)) {$else}English{$endif})); if result<>'' then English := result; end; procedure GetText(var Text: string); // used for System.LoadResStringTranslate case-sensitive (same as standard gettext) begin if Language<>nil then Language.Translate(Text); end; function _(const English: WinAnsiString): string; begin if Language<>nil then begin result := Language.FindMessage(Hash32(English)); if result<>'' then exit; end; {$ifdef UNICODE} result := WinAnsiToUnicodeString(pointer(English),length(English)); {$else} result := CurrentAnsiConvert.AnsiToAnsi(WinAnsiConvert,pointer(English),length(English)); {$endif} end; function S2U(const Text: string): RawUTF8; begin {$ifdef UNICODE} result := RawUnicodeToUtf8(PWideChar(pointer(Text)),length(Text)); {$else} result := CurrentAnsiConvert.AnsiBufferToRawUTF8(pointer(Text),length(Text)); {$endif} end; function U2S(const Text: RawUTF8): string; begin {$ifdef UNICODE} result := UTF8DecodeToUnicodeString(pointer(Text),length(Text)); {$else} result := CurrentAnsiConvert.UTF8BufferToAnsi(pointer(Text),length(Text)); {$endif} end; function Iso2S(Iso: TTimeLog): string; begin if Iso=0 then result := '' else result := Language.DateTimeToText(Iso); end; function TLanguageFile.BooleanToString(Value: boolean): string; begin if self=nil then result := B2SS[Value] else begin result := fBooleanToString[Value]; if result='' then result := B2SS[Value]; ................................................................................ begin {$ifndef LVCL}if Self=nil then{$endif} result := DateTimeToIso(Iso8601(Time).ToTime,false) {$ifndef LVCL} else DateTimeToString(Result, TimeFmt, Iso8601(Time).ToTime); {$endif} end; (* procedure POExport(const SourceMsgPath, POFileName: TFileName; SourceLanguage: TLanguages); var English, Source: TLanguageFile; SourceDir: TFileName; Dest: TFileStream; W: TTextWriter; i: integer; E: string; ................................................................................ try W.AddLine('"Content-Type: text/plain; charset=UTF-8\n"'#13#10+ '"Content-Transfer-Encoding: 8bit\n"'#13#10); for i := 0 to English.Messages.Count - 1 do begin E := English.Messages[i]; Source.Translate(E); W.Add('msgid "%"'#13'msgstr"'#13#13, // #13 will be written as #13#10 [WinAnsiConvert.StringToUTF8(StringReplace(English.Messages[i],#13#10,'"'#13#10'"',[rfReplaceAll])), Source.StringToUTF8(StringReplace(E,#13#10,'"'#13#10'"',[rfReplaceAll]))]); end; finally W.Free; Source.Free; English.Free; Dest.Free; end; end; *) { TLanguage } function TLanguage.Abr: RawByteString; begin if Index=LANGUAGE_NONE then result := '' else ................................................................................ begin result := LanguageName(Index); end; {$ifdef EXTRACTALLRESOURCES} var // expect english text, converted into WinAnsi before Hash32() // - Delphi 2009 and up will do the implicit codepage conversion // (usefull for chars with unicode value >255, e.g. '�') CB_EnumStrings: TWinAnsiDynArray; /// number of items in CB_EnumStrings[] CB_EnumStringsCount: integer; // store the curently identified Hash32() of each english text CB_Enum: TDynArrayHashed; ................................................................................ // code below use the string generic type, which is prefered for the RTTI var F: ^Text absolute lparam; Reader: TReader; procedure ConvertObject(const ParentName, ObjectName: string); procedure ConvertValue(const PropName, LastPropName: string); procedure WriteProperty(const Value: WinAnsiString); // for Delphi 2009 and up, Value: string was converted into a WinAnsiString begin // ignore components which names begin with '_' if (PropName<>LastPropName) and (PropName<>'') then // PropName=Label1.Caption if PropName[1]='_' then // ignore _Copyright.Caption exit; // write value if (LastPropName='Caption') or (LastPropName='EditLabel.Caption') or ................................................................................ ClassList: TList; procedure AddEnum(T: PEnumType); var index: integer; begin for index := T^.MinValue to T^.MaxValue do AddOnceDynArray(StringToWinAnsi(T^.GetCaption(index))); // for Delphi 2009 and up/XE: CaptionName converted into a WinAnsiString end; procedure AddClass(C: TClass); var i: integer; P: PPropInfo; CP: PClassProp; begin if (C=nil) or (ClassList.IndexOf(C)>=0) then ................................................................................ AddClass(C.ClassParent); // add parent properties first CP := InternalClassProp(C); if CP=nil then exit; P := @CP^.PropList; for i := 1 to CP^.PropCount do begin // add all field names AddOnceDynArray(StringToWinAnsi(TSQLRecord.CaptionName(@P^.ShortName))); // for Delphi 2009 and up/XE: CaptionName converted into a WinAnsiString with P^.PropType^^ do case Kind of tkClass: // add contained objects AddClass(ClassType^.ClassType); tkEnumeration: // add enumeration values AddEnum(EnumBaseType); tkSet: ................................................................................ if Objects[i].InheritsFrom(TSQLModel) then begin AddOnceDynArray('ID'); // ID property is never published, but always here // add custom captions for all tables of a database model with TSQLModel(Objects[i]) do for index := 0 to high(Tables) do with Tables[index] do begin // TSQLRecord.CaptionName() may be overriden AddOnceDynArray(StringToWinAnsi(CaptionName(nil))); // add table name // for Delphi 2009 and up, CaptionName(): string will be converted into a WinAnsiString with InternalClassProp(Tables[index])^ do begin P := @PropList; for j := 1 to PropCount do begin // add all field names AddOnceDynArray(StringToWinAnsi(CaptionName(@P^.ShortName))); P := P^.Next; end; end; |
Changes to SQLite3/Samples/06 - Remote JSON REST Service/Project06Server.dpr.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
SysUtils; type // you'll have warning about abstract classes, because TSQLRestServer doesn't // implement any database engine, but it doesn't matter here TServiceServer = class(TSQLRestServer) published function Sum(aSession: Cardinal; aRecord: TSQLRecord; aParameters: PUTF8Char; const aSentData: RawUTF8; out aResp, aHead: RawUTF8): Integer; end; { TServiceServer } function TServiceServer.Sum(aSession: Cardinal; aRecord: TSQLRecord; aParameters: PUTF8Char; const aSentData: RawUTF8; out aResp, aHead: RawUTF8): Integer; var a,b: Extended; begin if not UrlDecodeNeedParameters(aParameters,'A,B') then begin result := 404; // invalid Request exit; end; while aParameters<>nil do begin UrlDecodeExtended(aParameters,'A=',a); UrlDecodeExtended(aParameters,'B=',b,@aParameters); end; aResp := JSONEncodeResult([a+b]); // same as : aResp := JSONEncode(['result',a+b],TempMemoryStream); result := 200; // success end; var aModel: TSQLModel; begin |
| < | < | | | | | |
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
SysUtils; type // you'll have warning about abstract classes, because TSQLRestServer doesn't // implement any database engine, but it doesn't matter here TServiceServer = class(TSQLRestServer) published function Sum(var aParams: TSQLRestServerCallBackParams): Integer; end; { TServiceServer } function TServiceServer.Sum(var aParams: TSQLRestServerCallBackParams): Integer; var a,b: Extended; begin if not UrlDecodeNeedParameters(aParams.Parameters,'A,B') then begin result := 404; // invalid Request exit; end; while aParams.Parameters<>nil do begin UrlDecodeExtended(aParams.Parameters,'A=',a); UrlDecodeExtended(aParams.Parameters,'B=',b,@aParams.Parameters); end; aParams.Resp := JSONEncodeResult([a+b]); // same as : aResp := JSONEncode(['result',a+b],TempMemoryStream); result := 200; // success end; var aModel: TSQLModel; begin |
Changes to SQLite3/Samples/MainDemo/FileMain.pas.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
if Tab.Retrieve(Client,Tab.List.Row,false) then
CopyObject(Tab.CurrentRecord,Rec) else
exit;
faImport:
if isMemo and Open then
with TSQLSafeMemo(Rec) do begin
fName := S2U(ExtractFileName(FN));
fData := S2U(AnyTextFileToString(FN,CurrentLanguage.Index));
if (RecordClass=TSQLSafeMemo) and not Cypher(CaptionName,fData,true) then
exit;
end else
exit;
faCreate:
if ((RecordClass=TSQLData) or (RecordClass=TSQLSafeData)) then
if Open then begin
|
| |
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
if Tab.Retrieve(Client,Tab.List.Row,false) then CopyObject(Tab.CurrentRecord,Rec) else exit; faImport: if isMemo and Open then with TSQLSafeMemo(Rec) do begin fName := S2U(ExtractFileName(FN)); fData := S2U(AnyTextFileToString(FN)); if (RecordClass=TSQLSafeMemo) and not Cypher(CaptionName,fData,true) then exit; end else exit; faCreate: if ((RecordClass=TSQLData) or (RecordClass=TSQLSafeData)) then if Open then begin |
Changes to SynCommons.pas.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 ... 479 480 481 482 483 484 485 486 487 488 489 490 491 492 ... 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 ... 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 ... 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 ... 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 ... 767 768 769 770 771 772 773 774 775 776 777 778 779 780 .... 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 .... 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 .... 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 .... 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 .... 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 .... 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 .... 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 .... 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 .... 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 .... 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 .... 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 .... 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 .... 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 .... 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 .... 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 .... 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 .... 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 ..... 14381 14382 14383 14384 14385 14386 14387 14388 14389 14390 14391 14392 14393 14394 14395 ..... 18634 18635 18636 18637 18638 18639 18640 18641 18642 18643 18644 18645 18646 18647 18648 18649 18650 18651 18652 18653 18654 18655 18656 18657 18658 18659 18660 18661 ..... 19277 19278 19279 19280 19281 19282 19283 19284 19285 19286 19287 19288 19289 19290 19291 ..... 21458 21459 21460 21461 21462 21463 21464 21465 21466 21467 21468 21469 21470 21471 21472 ..... 24491 24492 24493 24494 24495 24496 24497 24498 24499 24500 24501 24502 24503 24504 24505 ..... 24523 24524 24525 24526 24527 24528 24529 24530 24531 24532 24533 24534 24535 24536 24537 24538 24539 24540 24541 24542 24543 24544 24545 24546 24547 24548 24549 24550 24551 24552 24553 24554 24555 24556 24557 24558 24559 24560 24561 ..... 24595 24596 24597 24598 24599 24600 24601 24602 24603 24604 24605 24606 24607 24608 24609 ..... 24964 24965 24966 24967 24968 24969 24970 24971 24972 24973 24974 24975 24976 24977 24978 24979 24980 ..... 27624 27625 27626 27627 27628 27629 27630 27631 27632 27633 27634 27635 27636 27637 ..... 28398 28399 28400 28401 28402 28403 28404 28405 28406 28407 |
- JSONEncode*() global functions will use an internal TRawByteStringStream instead of a supplied TMemoryStream - new FormatUTF8() overloaded function, handling both '%' and '?' parameters (inserting '?' as inlined :(...): parameters, with proper string quote) - with associated regression tests Version 1.16 - TSynLog allows read sharing of the .log created file - TSynCache now handle an integer ResultTag: PtrInt value parameter (used e.g. to store the row counts of a SQL result cache) - TMemoryMapText class (and therefore TSynLogFile) is now able to map/open an existing file: it will allow e.g. the SynLogViewer to browse a .log file which is actually still opened and working by the main application - faster RawUnicodeToUtf8(), WinAnsiBufferToUtf8(), UTF8ToWideChar(), UTF8ToWinPChar() functions, thanks to very clever speed-up proposals by Sha - new FileSize() and RawByteStringArrayConcat() functions - new TPrecisionTimer Pause and Resume methods - new TSynLogFamily.IncludeComputerNameInFileName property - new ToVarInt64() and FromVarInt64() functions to encode and decode variable length signed Int64 values - new tftVarInt64 kind of variable Length column in TSynTableFieldType - fixed issue in TDynArrayHashed if you do not use the external Count ................................................................................ TDateTimeDynArray = array of TDateTime; /// a dynamic array of WideString values TWideStringDynArray = array of WideString; /// a dynamic array of SynUnicode values TSynUnicodeDynArray = array of SynUnicode; TWordArray = array[0..MaxInt div SizeOf(word)-1] of word; PWordArray = ^TWordArray; TInt64Array = array[0..MaxInt div SizeOf(Int64)-1] of Int64; PInt64Array = ^TInt64Array; ................................................................................ PInt64Rec = ^Int64Rec; { ************ fast UTF-8 / Unicode / Ansi types and conversion routines } type /// defines a table containing all the unicode characters corresponding to // a given code page (non MBCS) TAnsiTable = packed array[byte] of Word; var /// this table contain all the unicode characters corresponding to // the Ansi Code page 1252 (i.e. what we call WinAnsi within the framework) WinAnsiTable: TAnsiTable; const /// MIME content type used for JSON communication (as used by the Microsoft // WCF framework and the YUI framework) JSON_CONTENT_TYPE = 'application/json; charset=UTF-8'; /// MIME content type used for plain UTF-8 text ................................................................................ /// MIME content type used for UTF-8 encoded HTML HTML_CONTENT_TYPE = 'text/html; charset="UTF-8"'; /// US English Windows Code Page, i.e. WinAnsi standard character encoding CODEPAGE_US = 1252; /// used for fast WinAnsi to Unicode conversion // - this table contain all the unicode characters corresponding to // the Ansi Code page 1252 (i.e. WinAnsi), which unicode value are > 255 // - values taken from MultiByteToWideChar(1252,0,@Tmp,256,@WinAnsiTable,256) // so these values are available outside the Windows platforms (e.g. Linux/BSD) WinAnsiUnicodeChars: packed array[128..159] of cardinal = (8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 157, 382, 376); /// used for fast Unicode to WinAnsi conversion //- this table contain all the unicode values corresponding to // the Ansi code page 1252 (i.e. WinAnsi), which unicode value are > 255, // sorted by increasing order (you can use a fast binary search to lookup // for the corresponding Ansi char, with WinAnsiTableSortedAnsi[] below) WinAnsiTableSortedWide: array[0..26] of integer = (338, 339, 352, 353, 376, 381, 382, 402, 710, 732, 8211, 8212, 8216, 8217, 8218, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8249, 8250, 8364, 8482); /// used for fast Unicode to WinAnsi (Ansi code page 1252) conversion // - lookup table for every WinAnsi char corresponding to a WinAnsiTableSortedWide[] WinAnsiTableSortedAnsi: array[0..26] of byte = (140, 156, 138, 154, 159, 142, 158, 131, 136, 152, 150, 151, 145, 146, 130, 147, 148, 132, 134, 135, 149, 133, 137, 139, 155, 128, 153); /// conversion of a wide char into a WinAnsi (CodePage 1252) char // - return ' ' for an unknown WideChar in code page 1252 function WideCharToWinAnsiChar(wc: cardinal): AnsiChar; {$ifdef HASINLINE}inline;{$endif} /// conversion of a wide char into a WinAnsi (CodePage 1252) char index // - return -1 for an unknown WideChar in code page 1252 function WideCharToWinAnsi(wc: cardinal): integer; {$ifdef HASINLINE}inline;{$endif} /// internal function called by WideCharToWinAnsi() // - should not be used in your code, only here to allow inlining of the // WideCharToWinAnsi function // - returns -1 if the supplied widechar doesn't belong to the code page 1252 function WinAnsiTableSortedFind(wc: cardinal): Integer; /// return TRUE if the supplied buffer only contains 7-bits Ansi characters function IsAnsiCompatible(PC: PAnsiChar): boolean; overload; /// return TRUE if the supplied buffer only contains 7-bits Ansi characters function IsAnsiCompatible(PW: PWideChar): boolean; overload; /// return TRUE if the supplied buffer only contains 7-bits Ansi characters function IsAnsiCompatible(PW: PWideChar; Len: integer): boolean; overload; /// return TRUE if the supplied unicode buffer only contains WinAnsi characters // - i.e. if the text can be displayed using ANSI_CHARSET function IsWinAnsi(WideText: PWideChar): boolean; overload; /// return TRUE if the supplied unicode buffer only contains WinAnsi characters // - i.e. if the text can be displayed using ANSI_CHARSET function IsWinAnsi(WideText: PWideChar; Length: integer): boolean; overload; /// return TRUE if the supplied UTF-8 buffer only contains WinAnsi characters // - i.e. if the text can be displayed using ANSI_CHARSET function IsWinAnsiU(UTF8Text: PUTF8Char): boolean; /// return TRUE if the supplied UTF-8 buffer only contains WinAnsi 8 bit characters // - i.e. if the text can be displayed using ANSI_CHARSET with only 8 bit unicode // characters (e.g. no "tm" or such) function IsWinAnsiU8Bit(UTF8Text: PUTF8Char): boolean; /// UTF-8 encode one Unicode character into Dest // - return the number of bytes written into Dest (i.e. 1,2 or 3) function UnicodeCharToUtf8(Dest: PUTF8Char; aWideChar: PtrUInt): integer; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a WinAnsi (CodePage 1252) string into a UTF-8 encoded String ................................................................................ // - faster than SysUtils: don't use Utf8Encode(WideString) -> no Windows.Global(), // and use a fixed pre-calculated array for individual chars conversion function WinAnsiToUtf8(WinAnsi: PAnsiChar; WinAnsiLen: integer): RawUTF8; overload; /// direct conversion of a WinAnsi PAnsiChar buffer into a UTF-8 encoded buffer // - Dest^ buffer must be reserved with at least SourceChars*3 function WinAnsiBufferToUtf8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; /// direct conversion of a WinAnsi (CodePage 1252) string into a Unicode encoded String // - very fast, by using a fixed pre-calculated array for individual chars conversion function WinAnsiToRawUnicode(const S: WinAnsiString): RawUnicode; /// direct conversion of a WinAnsi (CodePage 1252) string into a Unicode buffer // - very fast, by using a fixed pre-calculated array for individual chars conversion procedure WinAnsiToUnicodeBuffer(const S: WinAnsiString; Dest: PWordArray; DestLen: integer); /// direct conversion of a UTF-8 encoded string into a WinAnsi String function Utf8ToWinAnsi(const S: RawUTF8): WinAnsiString; overload; /// direct conversion of a UTF-8 encoded zero terminated buffer into a WinAnsi String function Utf8ToWinAnsi(P: PUTF8Char): WinAnsiString; overload; /// direct conversion of a UTF-8 encoded zero terminated buffer into a RawUTF8 String procedure Utf8ToRawUTF8(P: PUTF8Char; var result: RawUTF8); {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a UTF-8 encoded buffer into a WinAnsi PAnsiChar buffer function UTF8ToWinPChar(dest: PAnsiChar; source: PUTF8Char; count: integer): integer; /// direct conversion of a UTF-8 encoded buffer into a WinAnsi shortstring buffer procedure UTF8ToShortString(var dest: shortstring; source: PUTF8Char); /// convert an UTF-8 encoded text into a WideChar array // - faster than System.UTF8ToUnicode // - sourceBytes can by 0, therefore length is computed from zero terminated source // - enough place must be available in dest // - a WideChar(#0) is added at the end (if something is written) // - returns the byte count written in dest, excluding the ending WideChar(#0) function UTF8ToWideChar(dest: pWideChar; source: PUTF8Char; sourceBytes: PtrInt=0): PtrInt; overload; /// convert an UTF-8 encoded text into a WideChar array // - faster than System.UTF8ToUnicode // - this overloaded function expect a MaxDestChars parameter // - sourceBytes can not be 0 for this function // - enough place must be available in dest ................................................................................ /// convert a WideString into a UTF-8 string function WideStringToUTF8(const aText: WideString): RawUTF8; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a Unicode encoded buffer into a WinAnsi PAnsiChar buffer procedure RawUnicodeToWinPChar(dest: PAnsiChar; source: PWideChar; WideCharCount: integer); /// convert a RawUnicode PWideChar into a WinAnsi (code page 1252) string function RawUnicodeToWinAnsi(P: PWideChar; WideCharCount: integer): WinAnsiString; overload; /// convert a RawUnicode string into a WinAnsi (code page 1252) string function RawUnicodeToWinAnsi(const Unicode: RawUnicode): WinAnsiString; overload; /// convert a WideString into a WinAnsi (code page 1252) string function WideStringToWinAnsi(const Wide: WideString): WinAnsiString; /// convert an AnsiChar buffer (of a given code page) into a UTF-8 string procedure AnsiCharToUTF8(P: PAnsiChar; L: Integer; var result: RawUTF8; ACP: integer); /// convert any Raw Unicode encoded String into a generic SynUnicode Text function RawUnicodeToSynUnicode(const Unicode: RawUnicode): SynUnicode; overload; {$ifdef HASINLINE}inline;{$endif} ................................................................................ // - it's prefered to use TLanguageFile.StringToUTF8() method in SQLite3i18n, // which will handle full i18n of your application // - it will work as is with Delphi 2009/2010/XE (direct unicode conversion) // - under older version of Delphi (no unicode), it will use the // current RTL codepage, as with WideString conversion (but without slow // WideString usage) function StringToUTF8(const Text: string): RawUTF8; /// convert any generic VCL Text buffer into an UTF-8 encoded buffer // - Dest must be able to receive at least SourceChars*3 bytes // - it will work as is with Delphi 2009/2010/XE (direct unicode conversion) // - under older version of Delphi (no unicode), it will use the // current RTL codepage, as with WideString conversion (but without slow // WideString usage) ................................................................................ const FileName: TFileName=''): RawUTF8; /// retrieve if some content is compressed, from a supplied binary buffer // - returns TRUE, if the header in binary buffer "may" be compressed (this method // can trigger false positives), e.g. begin with zip/gz/gif/wma/png/jpeg markers function IsContentCompressed(Content: Pointer; Len: integer): boolean; type PIntegerDynArray = ^TIntegerDynArray; TIntegerDynArray = array of integer; PCardinalDynArray = ^TCardinalDynArray; TCardinalDynArray = array of cardinal; PInt64DynArray = ^TInt64DynArray; TInt64DynArray = array of Int64; PDoubleDynArray = ^TDoubleDynArray; TDoubleDynArray = array of double; PCurrencyDynArray = ^TCurrencyDynArray; TCurrencyDynArray = array of Currency; TWordDynArray = array of word; PWordDynArray = ^TWordDynArray; TByteDynArray = array of byte; PByteDynArray = ^TByteDynArray; /// retrieve the index where to insert a PUTF8Char in a sorted PUTF8Char array // - R is the last index of available entries in P^ (i.e. Count-1) // - string comparison is case-sensitive (so will work with any PAnsiChar) // - returns -1 if the specified Value was found (i.e. adding will duplicate a value) function FastLocatePUTF8CharSorted(P: PPUTF8CharArray; R: PtrInt; Value: PUTF8Char): PtrInt; overload; /// retrieve the index where to insert a PUTF8Char in a sorted PUTF8Char array ................................................................................ /// the main executable name, without any path nor extension ProgramName: RawUTF8; /// the main executable details, as used e.g. by TSynLog // - e.g. 'C:\Dev\lib\SQLite3\exe\TestSQL3.exe 0.0.0.0 (2011-03-29)' ProgramFullSpec: RawUTF8; /// same as paramstr(0) ProgramFileName: TFileName; /// the current executable version Version: TFileVersion; /// the current computer host name Host: RawUTF8; /// the current computer user name User: RawUTF8; end; ................................................................................ /// a TSynLogArchiveEvent handler which will delete older .log files function EventArchiveDelete(const aOldLogFileName, aDestinationPath: TFileName): boolean; /// a TSynLogArchiveEvent handler which will compress older .log files // using our proprietary SynLZ format // - resulting file will have the .synlz extension and will be located // in the aDestinationPath directory, i.e. TSynLogFamily.ArchivePath+'\log\YYYYMM\' // - use UnSynLZ.dpr tool to uncompress it into .log textual file // - SynLZ is much faster than zip for compression content, but proprietary function EventArchiveSynLZ(const aOldLogFileName, aDestinationPath: TFileName): boolean; resourcestring sInvalidIPAddress = '"%s" is an invalid IP v4 address'; ................................................................................ sValidationFieldDuplicate = 'Value already used for this unique key field'; implementation { ************ some fast UTF-8 / Unicode / Ansi conversion routines } function UnicodeCharToUtf8(Dest: PUTF8Char; aWideChar: PtrUInt): integer; begin if aWideChar<=$7F then begin Dest^ := AnsiChar(aWideChar); result := 1; end else ................................................................................ Dest[0] := AnsiChar($C0 or (aWideChar shr 6)); Dest[1] := AnsiChar($80 or (aWideChar and $3F)); result := 2; end; end; function WinAnsiBufferToUtf8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; // length(Dest) must be reserved with at least SourceChars*3 // a #0 char is appened at the end var EndSource: PAnsiChar; c: Cardinal; begin if Dest=nil then begin Result := nil; Exit; end else if (Source<>nil) and (SourceChars>0) then begin // first handle 7 bit ASCII WideChars, by quads (Sha optimization) EndSource := Source+SourceChars-4; if Source<=EndSource then repeat c := pCardinal(Source)^; if c and $80808080<>0 then break; // break on first non ASCII quad inc(Source,4); pCardinal(Dest)^ := c; inc(Dest,4); until Source>EndSource; // generic loop, handling one WideChar per iteration inc(EndSource,4); if Source<EndSource then repeat c := byte(Source^); inc(Source); if c <= $7F then begin Dest^ := AnsiChar(c); // 0..127 don't need any translation Inc(Dest); if Source<EndSource then continue else break; end else begin c := WinAnsiTable[c]; // convert WinAnsi char into Unicode char if c > $7ff then begin Dest[0] := AnsiChar($E0 or (c shr 12)); Dest[1] := AnsiChar($80 or ((c shr 6) and $3F)); Dest[2] := AnsiChar($80 or (c and $3F)); Inc(Dest,3); if Source<EndSource then continue else break; end else begin Dest[0] := AnsiChar($C0 or (c shr 6)); Dest[1] := AnsiChar($80 or (c and $3F)); Inc(Dest,2); if Source<EndSource then continue else break; end; end; until false; end; Dest^ := #0; Result := Dest; end; var WinAnsiTableA: packed array[AnsiChar] of Word absolute WinAnsiTable; procedure WinAnsiToUnicodeBuffer(const S: WinAnsiString; Dest: PWordArray; DestLen: integer); var i, L: PtrInt; begin L := PtrInt(S); if L<>0 then begin L := PPtrInt(L-4)^; if L>=DestLen then L := DestLen-1; for i := 0 to L do // include S[L+1] = last #0 Dest^[i] := WinAnsiTableA[S[i+1]]; // very fast conversion end else Dest^[0] := 0; end; function WinAnsiToRawUnicode(const S: WinAnsiString): RawUnicode; var i, L: PtrInt; PW: PWordArray; begin result := ''; if S='' then exit; L := PPtrInt(PtrInt(S)-4)^; SetLength(result,L*2+1); // +1 to include last WideChar(#0) {$ifdef UNICODE2} // not needed: SetLength() did already set the codepage PWord(PtrInt(result)-12)^ := 1200; // Codepage for an UnicodeString {$endif} PW := pointer(result); for i := 0 to L do // include S[L+1] = last #0 PW^[i] := WinAnsiTableA[S[i+1]]; // very fast conversion end; function WinAnsiToUtf8(const S: WinAnsiString): RawUTF8; // faster than SysUtils: don't use Utf8Encode(WideString) -> no Windows.Global() var L: integer; tmp: RawByteString; // don't use result as temporary buffer begin result := ''; if S='' then exit; L := PPtrInt(PtrInt(S)-4)^; SetLength(tmp,L*3); // enough place SetString(result,PAnsiChar(pointer(tmp)), WinAnsiBufferToUtf8(pointer(tmp),pointer(S),L)-pointer(tmp)); end; function WinAnsiToUtf8(WinAnsi: PAnsiChar; WinAnsiLen: integer): RawUTF8; overload; var tmp: RawByteString; // don't use result as temporary buffer begin result := ''; if WinAnsi='' then exit; SetLength(tmp,WinAnsiLen*3); // enough place SetString(result,PAnsiChar(pointer(tmp)), WinAnsiBufferToUtf8(pointer(tmp),WinAnsi,WinAnsiLen)-pointer(tmp)); end; function WideCharToWinAnsiChar(wc: cardinal): AnsiChar; begin // code generated for this function is very fast if wc<256 then if WinAnsiTable[wc]<256 then begin result := AnsiChar(wc); exit; end else begin result := ' '; // invalid ansi char for this code page (e.g. #128) exit; end else begin // wc>255: wc := WinAnsiTableSortedFind(wc); if integer(wc)>=0 then result := AnsiChar(byte(wc)) else result := ' '; // space for invalid wide char exit; end; end; function WinAnsiTableSortedFind(wc: cardinal): Integer; var L,R: PtrInt; cmp: integer; begin L := 0; R := high(WinAnsiTableSortedWide); repeat // very fast binary search result := (L + R) shr 1; cmp := WinAnsiTableSortedWide[result]-integer(wc); if cmp<0 then begin L := result + 1; if L<=R then Continue else Break; end else if cmp>0 then begin R := result - 1; if L<=R then Continue else Break; end else begin result := WinAnsiTableSortedAnsi[result]; exit; end; until false; result := -1; // invalid wide char end; function WideCharToWinAnsi(wc: cardinal): integer; begin if wc<256 then if WinAnsiTable[wc]<256 then result := wc else result := -1 else // invalid ansi char for this code page (e.g. #128) result := WinAnsiTableSortedFind(wc); end; function IsWinAnsi(WideText: PWideChar; Length: integer): boolean; var i: integer; begin result := false; if WideText<>nil then for i := 1 to Length do if WideCharToWinAnsi(cardinal(WideText^))<0 then exit else inc(WideText); result := true; end; function IsAnsiCompatible(PC: PAnsiChar): boolean; begin result := false; if PC<>nil then while true do ................................................................................ if PC^=#0 then break else if PC^<=#127 then inc(PC) else // 7 bits chars are always OK, whatever codepage/charset is used exit; result := true; end; function IsAnsiCompatible(PW: PWideChar): boolean; overload; begin result := false; if PW<>nil then while true do if Ord(PW^)=0 then break else if Ord(PW^)<=127 then inc(PW) else // 7 bits chars are always OK, whatever codepage/charset is used exit; result := true; end; function IsAnsiCompatible(PW: PWideChar; Len: integer): boolean; overload; var i: integer; ................................................................................ for i := 0 to Len-1 do if ord(PW[i])>127 then exit; result := true; end; function IsWinAnsi(WideText: PWideChar): boolean; var L,R,pivot: PtrInt; cmp: integer; wc: integer; begin result := false; if WideText<>nil then repeat wc := cardinal(WideText^); inc(WideText); if wc=0 then Break else if wc<256 then if WinAnsiTable[wc]<256 then continue else exit else begin // wc>=256 -> fast binary search of the Unicode value in WinAnsiTable L := 0; R := high(WinAnsiTableSortedWide); repeat // very fast binary search pivot := (L + R) shr 1; cmp := WinAnsiTableSortedWide[pivot]-wc; if cmp=0 then break else // found if cmp<0 then begin L := pivot + 1; if L<=R then Continue else exit; end else begin R := pivot - 1; if L<=R then Continue else exit; end; until false; end; until false; result := true; end; function IsWinAnsiU(UTF8Text: PUTF8Char): boolean; var c: Cardinal; begin result := false; if UTF8Text<>nil then repeat c := byte(UTF8Text^); inc(UTF8Text); if c=0 then break else if c and $80=0 then continue else begin if UTF8Text^=#0 then break; if c and $20=0 then begin c := c shl 6+byte(UTF8Text^)-$00003080; inc(UTF8Text); end else begin c := c shl 6+byte(UTF8Text^); inc(UTF8Text); if UTF8Text^=#0 then break; c := c shl 6+byte(UTF8Text^)-$000E2080; inc(UTF8Text); end; if c>255 then begin if WinAnsiTableSortedFind(c)<0 then exit; // invalid char in the WinAnsi code page end else if WinAnsiTable[c]>255 then exit; // invalid char in the WinAnsi code page end; until false; result := true; end; function IsWinAnsiU8Bit(UTF8Text: PUTF8Char): boolean; var c: Cardinal; begin result := false; if UTF8Text<>nil then repeat c := byte(UTF8Text^); inc(UTF8Text); if c=0 then break else if c and $80=0 then continue else begin if UTF8Text^=#0 then break; if c and $20=0 then begin c := c shl 6+byte(UTF8Text^)-$00003080; inc(UTF8Text); end else begin c := c shl 6+byte(UTF8Text^); inc(UTF8Text); if UTF8Text^=#0 then break; c := c shl 6+byte(UTF8Text^)-$000E2080; inc(UTF8Text); end; if (c>255) or (WinAnsiTable[c]>255) then exit; // not 8 bit char (like "tm" or such) is marked invalid end; until false; result := true; end; function UTF8ToWinPChar(dest: PAnsiChar; source: PUTF8Char; count: integer): integer; var c: cardinal; begd: PAnsiChar; endSource: PUTF8Char; begin result := 0; if source=nil then exit; begd := dest; // first handle trailing 7 bit ASCII chars, by quad (Sha optimization) endSource := source+count-4; if source<=endSource then repeat c := pCardinal(source)^; if c and $80808080<>0 then break; // break on first non ASCII quad inc(source,4); pCardinal(Dest)^ := c; inc(dest,4); until source>endSource; // generic loop, handling one UTF-8 code per iteration inc(endSource,4); if source<endSource then repeat c := byte(source^); inc(source); if byte(c) and $80=0 then begin dest^ := AnsiChar(byte(c)); inc(dest); if source<endsource then continue else break; end else begin if source>=endsource then break; if c and $20=0 then begin c := c shl 6+byte(source^)-$00003080; inc(source); if c and $ffffff00=0 then begin if WinAnsiTable[c]>255 then dest^ := ' ' else // invalid char in the WinAnsi code page dest^ := AnsiChar(c); inc(dest); // #128..#255 -> direct copy if source<endsource then continue else break; end; end else begin c := c shl 6+byte(source^); inc(source); if source>=endsource then break; c := c shl 6+byte(source^)-$000E2080; inc(source); end; // #256.. -> slower but accurate conversion c := WinAnsiTableSortedFind(c); if integer(c)>=0 then begin dest^ := AnsiChar(Byte(c)); // don't add invalid wide char inc(dest); end; if source>=endsource then break; end; until false; result := dest-begd; end; procedure UTF8ToShortString(var dest: shortstring; source: PUTF8Char); var c: cardinal; len: integer; begin len := 0; ................................................................................ end else begin c := c shl 6+byte(source^); inc(source); if source^=#0 then break; c := c shl 6+byte(source^)-$000E2080; inc(source); end; // #256.. -> slower but accurate conversion inc(len); c := WinAnsiTableSortedFind(c); if integer(c)<0 then c := ord('?'); dest[len] := AnsiChar(byte(c)); // #128..#255 -> direct copy if len<255 then continue else break; end; until false; dest[0] := AnsiChar(len); end; function Utf8ToWinAnsi(const S: RawUTF8): WinAnsiString; var L: integer; tmp: RawByteString; // don't use result as temporary buffer begin if S='' then begin result := ''; exit; end; L := PPtrInt(PtrInt(S)-4)^; SetLength(tmp,L); SetString(result,PAnsiChar(pointer(tmp)),UTF8ToWinPChar(pointer(tmp),pointer(S),L)); end; function Utf8ToWinAnsi(P: PUTF8Char): WinAnsiString; var L: integer; tmp: RawByteString; // don't use result as temporary buffer begin L := StrLen(P); if L=0 then begin result := ''; exit; end; SetLength(tmp,L); SetString(result,PAnsiChar(pointer(tmp)),UTF8ToWinPChar(pointer(tmp),P,L)); end; procedure Utf8ToRawUTF8(P: PUTF8Char; var result: RawUTF8); begin // fast and Delphi 2009/2010/XE ready SetString(result,PAnsiChar(P),StrLen(P)); end; ................................................................................ end; result := PtrInt(Dest)-result; end; /// convert a RawUnicode PChar into a UTF-8 string function RawUnicodeToUtf8(P: PWideChar; WideCharCount: integer): RawUTF8; var L,LW: integer; tmp: array[0..511] of AnsiChar; begin if WideCharCount=0 then begin result := ''; exit; end; LW := WideCharCount*3; // maximum resulting length if LW<SizeOf(tmp) then begin // faster computation without temporary heap allocation SetString(Result,tmp,RawUnicodeToUtf8(tmp,sizeof(tmp),P,WideCharCount)); exit; end; SetString(result,nil,LW); L := RawUnicodeToUtf8(pointer(result),LW+1,P,WideCharCount); if L<=0 then result := '' else if L<>LW then ................................................................................ var LW: integer; begin result := ''; // somewhat faster if result is freed before any SetLength() if WideCharCount=0 then exit; LW := WideCharCount*3; // maximum resulting length SetLength(result,LW); {$ifdef UNICODE2} // not needed: SetLength() did already set the codepage PWord(PtrInt(result)-12)^ := CP_UTF8; // use only SetLength() -> force set code page {$endif} UTF8Length := RawUnicodeToUtf8(pointer(result),LW+1,P,WideCharCount); if UTF8Length<=0 then result := ''; end; /// convert a RawUnicode string into a UTF-8 string function RawUnicodeToUtf8(const Unicode: RawUnicode): RawUTF8; ................................................................................ function RawUnicodeToSynUnicode(P: PWideChar; WideCharCount: integer): SynUnicode; overload; begin SetString(result,P,WideCharCount); end; procedure RawUnicodeToWinPChar(dest: PAnsiChar; source: PWideChar; WideCharCount: Integer); var i: integer; wc: integer; begin for i := 0 to WideCharCount-1 do begin wc := integer(source[i]); if wc<256 then if WinAnsiTable[wc]<256 then dest[i] := AnsiChar(wc) else dest[i] := ' ' else begin wc := WinAnsiTableSortedFind(wc); if integer(wc)>=0 then dest[i] := AnsiChar(byte(wc)) else dest[i] := ' '; // space for invalid wide char end; end; end; function RawUnicodeToWinAnsi(P: PWideChar; WideCharCount: integer): WinAnsiString; overload; begin result := ''; // somewhat faster if result is freed before any SetLength() if WideCharCount=0 then exit; SetLength(result,WideCharCount); {$ifdef UNICODE2} // not needed: SetLength() did already set the codepage PWord(PtrInt(result)-12)^ := CODEPAGE_US; // use only SetLength() -> force set code page {$endif} RawUnicodeToWinPChar(pointer(result),P,WideCharCount); end; function RawUnicodeToWinAnsi(const Unicode: RawUnicode): WinAnsiString; overload; begin result := RawUnicodeToWinAnsi(pointer(Unicode),length(Unicode) shr 1); end; function WideStringToWinAnsi(const Wide: WideString): WinAnsiString; begin result := RawUnicodeToWinAnsi(pointer(Wide),length(Wide)); end; procedure UnicodeBufferToWinAnsi(source: PWideChar; out Dest: WinAnsiString); var L: integer; begin L := StrLenW(source); SetLength(Dest,L); RawUnicodeToWinPChar(pointer(Dest),source,L); end; function UnicodeBufferToString(source: PWideChar): string; begin result := RawUnicodeToString(source,StrLenW(source)); end; procedure AnsiCharToUTF8(P: PAnsiChar; L: Integer; var result: RawUTF8; ACP: integer); var tmp: array[0..256*3-1] of AnsiChar; U: PWideChar; LW: integer; RU: RawUnicode; begin if (P=nil) or (L=0) then begin result := ''; exit; end; if ACP=CODEPAGE_US then begin /// we have WinAnsiBufferToUtf8() at hand for direct conversion from WinAnsi if L<SizeOf(tmp) div 3 then SetString(result,tmp,WinAnsiBufferToUtf8(tmp,P,L)-tmp) else begin SetLength(RU,L*3); SetString(result,PAnsiChar(pointer(RU)), WinAnsiBufferToUtf8(pointer(RU),P,L)-pointer(RU)); end; end else begin // not WinAnsi encoding -> rely on Windows API if L<SizeOf(tmp) shr 1 then U := @tmp else begin SetLength(RU,L*2+1); U := pointer(RU); end; LW := MultiByteToWideChar(ACP,0,P,L,U,L); result := RawUnicodeToUtf8(U,LW); end; end; {$ifdef UNICODE} /// convert a Delphi 2009 Unicode string into our UTF-8 string function UnicodeStringToUtf8(const S: string): RawUTF8; begin result := RawUnicodeToUtf8(pointer(S),length(S)); end; function UnicodeStringToWinAnsi(const S: string): WinAnsiString; begin ................................................................................ SetLength(result,L2); end; function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString; var i: integer; begin SetString(result,nil,WinAnsiLen); for i := 0 to WinAnsiLen-1 do PWordArray(result)[i] := WinAnsiTableA[WinAnsi[i]]; // very fast conversion end; {$endif} {$ifdef UNICODE} function Ansi7ToString(const Text: RawByteString): string; var i: integer; begin ................................................................................ function StringToWinAnsi(const Text: string): WinAnsiString; begin result := RawUnicodeToWinAnsi(Pointer(Text),length(Text)); end; {$else} function StringToWinAnsi(const Text: string): WinAnsiString; begin if GetACP=CODEPAGE_US then result := Text else result := RawUnicodeToWinAnsi(StringToRawUnicode(Text)) end; {$endif} {$ifdef UNICODE} function StringBufferToUtf8(Dest: PUTF8Char; Source: PChar; SourceChars: PtrInt): PUTF8Char; begin result := Dest+RawUnicodeToUtf8(Dest,SourceChars*3,PWideChar(Source),SourceChars); end; {$else} function StringBufferToUtf8(Dest: PUTF8Char; Source: PChar; SourceChars: PtrInt): PUTF8Char; var tmp: RawUnicode; L: integer; begin if GetACP=CODEPAGE_US then begin // faster than Windows API / Delphi RTL result := WinAnsiBufferToUtf8(Dest,Source,SourceChars); exit; end; SetLength(tmp,SourceChars*2); L := MultiByteToWideChar(GetACP, 0, Source, SourceChars, pointer(tmp), SourceChars); result := Dest+RawUnicodeToUtf8(Dest,SourceChars*3,pointer(tmp),L); end; {$endif} {$ifdef UNICODE} function StringToUTF8(const Text: string): RawUTF8; begin result := RawUnicodeToUtf8(pointer(Text),length(Text)); end; {$else} function StringToUTF8(const Text: string): RawUTF8; var Dest: RawUnicode; L: integer; begin if GetACP=CODEPAGE_US then begin result := WinAnsiToUtf8(Text); // faster than Windows API / Delphi RTL exit; end; result := ''; L := length(Text); if L=0 then exit; SetLength(Dest,L*2); L := MultiByteToWideChar(GetACP, 0, Pointer(Text), L, pointer(Dest), L); result := RawUnicodeToUtf8(pointer(Dest),L); end; {$endif} {$ifdef UNICODE} function StringToRawUnicode(const S: string): RawUnicode; begin SetString(result,PAnsiChar(pointer(S)),length(S)*2+1); // +1 for last wide #0 end; {$else} function StringToRawUnicode(const S: string): RawUnicode; var i, L: integer; begin L := length(S); SetString(result,nil,L*2+1); // +1 for last wide #0 if GetACP<>CODEPAGE_US then begin // low-level MBCS RTL function L := MultiByteToWideChar(GetACP, 0, Pointer(s), L, pointer(result), L)*2+1; SetLength(result,L); result[L-1] := #0; // ensure finish with last widechar #0 end else // fast WinAnsi conversion for i := 0 to L do // includes S[L]=#0 -> last widechar #0 PWordArray(result)[i] := WinAnsiTable[PByteArray(S)[i]]; end; {$endif} {$ifdef UNICODE} function StringToRawUnicode(P: PChar; L: integer): RawUnicode; begin SetString(result,PAnsiChar(P),L*2+1); // +1 for last wide #0 end; {$else} function StringToRawUnicode(P: PChar; L: integer): RawUnicode; var i: integer; begin SetString(result,nil,L*2+1); // +1 for last wide #0 if GetACP<>CODEPAGE_US then begin // low-level MBCS RTL function L := MultiByteToWideChar(GetACP, 0, P, L, pointer(result), L)*2+1; SetLength(result,L); result[L-1] := #0; // ensure finish with last widechar #0 end else // fast WinAnsi conversion for i := 0 to L do // includes S[L]=#0 -> last widechar #0 PWordArray(result)[i] := WinAnsiTableA[P[i]]; end; {$endif} {$ifdef UNICODE} function RawUnicodeToString(P: PWideChar; L: integer): string; overload; begin SetString(result,P,L); end; {$else} function RawUnicodeToString(P: PWideChar; L: integer): string; overload; begin if (GetACP=CODEPAGE_US) or IsAnsiCompatible(P,L) then result := RawUnicodeToWinAnsi(P,L) else begin SetLength(result,WideCharToMultiByte(GetACP,0,P,L,nil,0,nil,nil)); WideCharToMultiByte(GetACP,0,P,L,pointer(result),length(result),nil,nil); end; end; {$endif} {$ifdef UNICODE} procedure RawUnicodeToString(P: PWideChar; L: integer; var result: string); overload; begin SetString(result,P,L); end; {$else} procedure RawUnicodeToString(P: PWideChar; L: integer; var result: string); overload; begin if (GetACP=CODEPAGE_US) or IsAnsiCompatible(P,L) then result := RawUnicodeToWinAnsi(P,L) else begin SetLength(result,WideCharToMultiByte(GetACP,0,P,L,nil,0,nil,nil)); WideCharToMultiByte(GetACP,0,P,L,pointer(result),length(result),nil,nil); end; end; {$endif} {$ifdef UNICODE} function RawUnicodeToString(const U: RawUnicode): string; begin // uses StrLenW() and not length(U) to handle case when was used as buffer SetString(result,PWideChar(pointer(U)),StrLenW(Pointer(U))); end; {$else} function RawUnicodeToString(const U: RawUnicode): string; var L: Integer; begin // uses StrLenW() and not length(U) to handle case when was used as buffer L := StrLenW(Pointer(U)); if (GetACP=CODEPAGE_US) or IsAnsiCompatible(Pointer(U),L) then result := RawUnicodeToWinAnsi(pointer(U),L) else begin SetLength(result,WideCharToMultiByte(GetACP,0,pointer(U),L,nil,0,nil,nil)); WideCharToMultiByte(GetACP,0,pointer(U),L,pointer(result),length(result),nil,nil); end; end; {$endif} {$ifdef UNICODE} function UTF8DecodeToString(P: PUTF8Char; L: integer): string; begin result := UTF8DecodeToUnicodeString(P,L); end; {$else} function UTF8DecodeToString(P: PUTF8Char; L: integer): string; var Dest: RawUnicode; short: array[byte] of char; begin if GetACP=CODEPAGE_US then begin if (P=nil) or (L=0) then result := '' else if L<sizeof(short) then // mostly avoid tmp memory allocation on heap SetString(result,PAnsiChar(@short),UTF8ToWinPChar(short,P,L)) else begin SetLength(Dest,L); // faster than Windows API / Delphi RTL SetString(result,PAnsiChar(pointer(Dest)),UTF8ToWinPChar(pointer(Dest),P,L)); end; exit; end; result := ''; if (P=nil) or (L=0) then exit; SetLength(Dest,L*2); L := UTF8ToWideChar(pointer(Dest),P,L) shr 1; SetLength(result,WideCharToMultiByte(GetACP,0,pointer(Dest),L,nil,0,nil,nil)); WideCharToMultiByte(GetACP,0,pointer(Dest),L,pointer(result),length(result),nil,nil); end; {$endif} function UTF8ToString(const Text: RawUTF8): string; begin result := UTF8DecodeToString(pointer(Text),length(Text)); end; function UTF8ToWideString(const Text: RawUTF8): WideString; begin UTF8ToWideString(Text,result); end; procedure UTF8ToWideString(const Text: RawUTF8; var result: WideString); ................................................................................ begin if Offset = 1 then Result := Pos(RawByteString(SubStr),RawByteString(S)) else begin I := Offset; LenSubStr := Length(SubStr); Len := Length(S)-LenSubStr+1; C := SubStr[1]; while I <= Len do begin if S[I]=C then begin X := 1; while (X<LenSubStr) and (S[I+X]=SubStr[X+1]) do Inc(X); if X=LenSubStr then begin Result := I; exit; ................................................................................ for i := 0 to 255 do begin d := NormToUpperByte[i]; if d in [ord('A')..ord('Z')] then inc(d,32); NormToLowerByte[i] := d; end; {$endif OWNNORMTOUPPER} for i := 0 to 255 do if (i>=low(WinAnsiUnicodeChars)) and (i<=high(WinAnsiUnicodeChars)) then WinAnsiTable[i] := WinAnsiUnicodeChars[i] else WinAnsiTable[i] := i; // code below is 55 bytes long, therefore shorter than a const array fillchar(ConvertHexToBin[0],sizeof(ConvertHexToBin),255); // all to 255 v := 0; for i := ord('0') to ord('9') do begin ConvertHexToBin[i] := v; inc(v); end; for i := ord('A') to ord('F') do begin ConvertHexToBin[i] := v; ConvertHexToBin[i+(ord('a')-ord('A'))] := v; inc(v); end; end; {$ifdef MSWINDOWS} const // lpMinimumApplicationAddress retrieved from Windows is very low $10000 // - i.e. maximum number of ID per table would be 65536 in TSQLRecord.GetID // - so we'll force an higher and almost "safe" value as 1,048,576 // (real value from runnning Windows is greater than $400000) MIN_PTR_VALUE = $100000; // see http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx ................................................................................ result := NormToUpperByte[result]; {$else} result := NormToUpperAnsi7Byte[result]); {$endif} exit; end else if result and $20=0 then begin result := result shl 6+byte(U[1])-$00003080; inc(U,2); if (result<=255) and (WinAnsiTable[result]<=255) then {$ifdef USENORMTOUPPER} result := NormToUpperByte[result] {$else} result := NormToUpperAnsi7Byte[result]) {$endif} else result := ord('?'); // char ignored for soundex exit; end else begin inc(U,3); ................................................................................ if c and $80=0 then begin D[result] := AnsiChar(Table[c]); inc(result); end else if c and $20=0 then begin // UTF-8 decode c := c shl 6+byte(P[0])-$00003080; inc(P); if (c<=255) and (WinAnsiTable[c]<=255) then begin c := WinAnsiTable[Table[c]]; // convert to Unicode if c<=127 then begin D[result] := AnsiChar(Table[c]); inc(result); end else goto nxt; // leave UTF-8 encoding untouched continue; end; ................................................................................ FileWrite(F,pointer(aLine)^,length(aLine)); FileClose(F); end; procedure LogToTextFile(Msg: RawUTF8); begin if Msg='' then begin Msg := RawUTF8(SysErrorMessage(GetLastError)); if Msg='' then exit; end; AppendToTextFile(Msg,ChangeFileExt(paramstr(0),'.log')); end; ................................................................................ vtInt64: Add(VInt64^); vtExtended: Add(VExtended^); end; end; procedure TTextWriter.AddJSONEscapeString(const s: string); {$ifndef UNICODE} var tmp: RawUnicode; i, L: PtrInt; {$endif} begin {$ifdef UNICODE} AddJSONEscapeW(pointer(s)); {$else} L := length(S); SetLength(tmp,L*2); if GetACP=CODEPAGE_US then for i := 0 to L-1 do PWordArray(tmp)[i] := WinAnsiTable[PByteArray(s)[i]] else L := MultiByteToWideChar(GetACP,0,pointer(s),L,pointer(tmp),L); AddJSONEscapeW(pointer(tmp),L); {$endif} end; procedure TTextWriter.AddPropName(const PropName: ShortString); begin if ord(PropName[0])=0 then exit; ................................................................................ ch: PtrInt; begin State := STATE_BEGIN; subdomains := 1; if P<>nil then repeat ch := NextUTF8Char(P,P); if (ch<=255) and (WinAnsiTable[ch]<=255) then // convert into WinAnsi char c := AnsiChar(ch) else // invalid char c := #127; case State of STATE_BEGIN: if c in atom_chars then ................................................................................ Check(IsWinAnsi(pointer(Unic),length(Unic)shr 1)=WA); Check(IsWinAnsiU(pointer(U))=WA); Check(UpperCase(LowerCase(U))=UpperCase(U)); {$ifndef ENHANCEDRTL} Check(LowerCase(U)=RawUTF8(SysUtils.LowerCase(string(U)))); Check(UpperCase(U)=RawUTF8(SysUtils.UpperCase(string(U)))); {$endif} if GetACP=CODEPAGE_US then // initial text is WinAnsiString (CP 1252) Check(StringToUTF8(UTF8ToString(U))=U); Up := UpperCaseUnicode(U); Check(Up=UpperCaseUnicode(LowerCaseUnicode(U))); Check(kr32(0,pointer(U),length(U))=kr32pas(pointer(U),length(U))); if U='' then continue; Check(UnQuoteSQLString(pointer(QuotedStr(U,'"')),res)<>nil); ................................................................................ end; end; {$ifdef USESYNTABLEVARIANT} function TSynTableFieldProperties.GetVariant(FieldBuffer: pointer): Variant; var len: integer; PB: PByte absolute FieldBuffer; PC: PAnsiChar absolute FieldBuffer; tmp: RawByteString; begin case FieldType of // fixed-sized field value tftBoolean: result := PBoolean(FieldBuffer)^; tftUInt8: ................................................................................ tftVarInt32: result := FromVarInt32(PB); tftVarUInt64: result := FromVarUInt64(PB); tftVarInt64: result := FromVarInt64(PB); // text storage - WinAnsi could use less space than UTF-8 tftWinAnsi, tftUTF8: begin len := FromVarUInt32(PB); if len>0 then begin if FieldType=tftUTF8 then SetString(tmp,PC,len) else {$ifdef UNICODE} begin result := WinAnsiToUnicodeString(PC,len); exit; end; {$else} if GetACP=CODEPAGE_US then begin SetString(tmp,PC,len); result := tmp; exit; end else tmp := WinAnsiToUtf8(PC,len); {$endif} result := UTF8ToString(tmp); end else result := ''; end; tftBlobInternal: begin len := FromVarUInt32(PB); SetString(tmp,PC,len); result := tmp; // return internal BLOB content as string end else result := ''; // tftBlobExternal fields e.g. must be directly accessed end; end; {$endif} ................................................................................ result := Int64ToUtf8(FromVarInt64(PB)); // text storage - WinAnsi could use less space than UTF-8 tftWinAnsi, tftUTF8, tftBlobInternal: begin len := FromVarUInt32(PB); if len>0 then if FieldType<>tftWinAnsi then SetString(result,PC,len) else result := WinAnsiToUtf8(PC,len) else result := ''; end; else result := ''; // tftBlobExternal fields e.g. must be directly accessed end; end; ................................................................................ SetString(result,PAnsiChar(@VD),sizeof(VD)); end; tftWinAnsi: begin VS := Value; {$ifdef UNICODE} ToSBFStr(UnicodeStringToWinAnsi(VS),result); {$else} if GetACP=CODEPAGE_US then ToSBFStr(VS,result) else ToSBFStr(Utf8ToWinAnsi(StringToUTF8(VS)),result); {$endif} end; tftUTF8: begin VS := Value; ToSBFStr(StringToUTF8(VS),result); end; else ................................................................................ var HostTmp, UserTmp: array[byte] of AnsiChar; HostSize, UserSize: cardinal; i: integer; begin with ExeVersion do if Version=nil then begin ProgramFileName := paramstr(0); Version := TFileVersion.Create(ProgramFileName,DefaultVersion); GarbageCollector.Add(Version); ProgramFullSpec := FormatUTF8(EXE_FMT, [ProgramFileName,Version.Detailed,DateToIso8601Text(Version.BuildDateTime)]); ProgramName := StringToUTF8(ExtractFileName(ProgramFileName)); i := length(ProgramName); while i>0 do ................................................................................ finalization SynLogExceptionEnabled := false; GarbageCollector.Free; // free all global instances (avoid memory leak) {$ifdef USESYNTABLEVARIANT} SynVariantTypes.Free; {$endif} end. |
> > > | | > > > > > > > > > > > > > > > | > > > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < | | > | < < < < < < > > > > > > > > > > > > > > > > > > | > > > > > < < < < < < < < < < < < < < < < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | < < < < < < < < < < < < < < < < < < < < < < < | < > < | < < > < < < < < < < < < < < < < > < < < < < < < < < < > | < < < < < < < > < < < < < | < < < < > | < < < < < < < < < < < < < | < < | < < < < < < < < < < < < < < < | < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > | | < < < < < < < | < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < > | < < < < < < < < < < < < < < < < < < < < < < < < < > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > < < < | < < < | < < < < < < < < < | < < < < | | | < < < < < < < < < < < < < < < < < > | < < < < < < < | | | | < < < < < < < > | < < < < < < < < < < < < < < < < < < < < < | < | < < < < < | < < < < < < < < < < < < < < < < < < > < < < < < < < < < < < < > < < < < < < < < < < < > | < < < < | < < < < < < < < < < < > < < < < < < < < < > | < < < < < < < < < < > | > > > > > > | < < < < > > > | | | | | | | | | < < < > | > | | > | | | | | | | | | | | | | | | | | | < < | | | < < > < < |
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 ... 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 ... 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 ... 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 ... 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 ... 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 ... 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 .... 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 .... 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 .... 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 .... 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 .... 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 .... 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 .... 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 .... 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 .... 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 .... 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 .... 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 .... 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 .... 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 .... 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 .... 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 .... 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 ..... 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 ..... 14687 14688 14689 14690 14691 14692 14693 14694 14695 14696 14697 14698 14699 14700 14701 ..... 18940 18941 18942 18943 18944 18945 18946 18947 18948 18949 18950 18951 18952 18953 18954 18955 18956 18957 18958 18959 18960 18961 18962 18963 18964 18965 ..... 19581 19582 19583 19584 19585 19586 19587 19588 19589 19590 19591 19592 19593 19594 19595 ..... 21762 21763 21764 21765 21766 21767 21768 21769 21770 21771 21772 21773 21774 21775 21776 21777 ..... 24796 24797 24798 24799 24800 24801 24802 24803 24804 24805 24806 24807 24808 24809 24810 24811 ..... 24829 24830 24831 24832 24833 24834 24835 24836 24837 24838 24839 24840 24841 24842 24843 24844 24845 24846 24847 24848 24849 24850 24851 24852 24853 24854 24855 24856 24857 24858 24859 24860 24861 24862 24863 24864 24865 ..... 24899 24900 24901 24902 24903 24904 24905 24906 24907 24908 24909 24910 24911 24912 24913 ..... 25268 25269 25270 25271 25272 25273 25274 25275 25276 25277 25278 25279 25280 25281 25282 ..... 27926 27927 27928 27929 27930 27931 27932 27933 27934 27935 27936 27937 27938 27939 27940 ..... 28701 28702 28703 28704 28705 28706 28707 28708 |
- JSONEncode*() global functions will use an internal TRawByteStringStream instead of a supplied TMemoryStream - new FormatUTF8() overloaded function, handling both '%' and '?' parameters (inserting '?' as inlined :(...): parameters, with proper string quote) - with associated regression tests Version 1.16 - introducing new TSynAnsiConvert and TSynAnsiFixedWidth classes, able to process Unicode to/from Ansi conversion in all possible code pages, with generic access methods and optimized handling of fixed width encodings - TSynLog allows read sharing of the .log created file - TSynCache now handle an integer ResultTag: PtrInt value parameter (used e.g. to store the row counts of a SQL result cache) - TMemoryMapText class (and therefore TSynLogFile) is now able to map/open an existing file: it will allow e.g. the SynLogViewer to browse a .log file which is actually still opened and working by the main application - faster RawUnicodeToUtf8() and UTF8ToWideChar() functions, thanks to very clever speed-up proposals by Sha (also included in TSynAnsi* classes) - new FileSize() and RawByteStringArrayConcat() functions - new TPrecisionTimer Pause and Resume methods - new TSynLogFamily.IncludeComputerNameInFileName property - new ToVarInt64() and FromVarInt64() functions to encode and decode variable length signed Int64 values - new tftVarInt64 kind of variable Length column in TSynTableFieldType - fixed issue in TDynArrayHashed if you do not use the external Count ................................................................................ TDateTimeDynArray = array of TDateTime; /// a dynamic array of WideString values TWideStringDynArray = array of WideString; /// a dynamic array of SynUnicode values TSynUnicodeDynArray = array of SynUnicode; PIntegerDynArray = ^TIntegerDynArray; TIntegerDynArray = array of integer; PCardinalDynArray = ^TCardinalDynArray; TCardinalDynArray = array of cardinal; PInt64DynArray = ^TInt64DynArray; TInt64DynArray = array of Int64; PDoubleDynArray = ^TDoubleDynArray; TDoubleDynArray = array of double; PCurrencyDynArray = ^TCurrencyDynArray; TCurrencyDynArray = array of Currency; TWordDynArray = array of word; PWordDynArray = ^TWordDynArray; TByteDynArray = array of byte; PByteDynArray = ^TByteDynArray; TWordArray = array[0..MaxInt div SizeOf(word)-1] of word; PWordArray = ^TWordArray; TInt64Array = array[0..MaxInt div SizeOf(Int64)-1] of Int64; PInt64Array = ^TInt64Array; ................................................................................ PInt64Rec = ^Int64Rec; { ************ fast UTF-8 / Unicode / Ansi types and conversion routines } type /// an abstract class to handle Ansi to/from Unicode translation // - implementations of this class will handle efficiently all Code Pages // - this default implementation will use the Operating System APIs // - you should not create your own class instance by yourself, but should // better retrieve an instance using TSynAnsiConvert.Engine(), which will // initialize either a TSynAnsiFixedWidth or a TSynAnsiConvert instance on need TSynAnsiConvert = class protected fCodePage: Integer; public /// initialize the internal conversion engine constructor Create(aCodePage: integer); reintroduce; virtual; /// returns the engine corresponding to a given code page // - a global list of TSynAnsiConvert instances is handled by the unit - // therefore, caller should not release the returned instance // - will return nil in case of unhandled code page class function Engine(aCodePage: integer): TSynAnsiConvert; /// direct conversion of a PAnsiChar buffer into an Unicode buffer // - Dest^ buffer must be reserved with at least SourceChars*2 bytes // - this default implementation will use the Operating System APIs function AnsiBufferToUnicode(Dest: PWideChar; Source: PAnsiChar; SourceChars: Cardinal): PWideChar; overload; virtual; /// direct conversion of a PAnsiChar buffer into a UTF-8 encoded buffer // - Dest^ buffer must be reserved with at least SourceChars*3 bytes // - a #0 char is appended at the end (and result will point to it) // - this default implementation will use the Operating System APIs function AnsiBufferToUTF8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; overload; virtual; /// convert any Ansi Text into an Unicode String // - returns a value using our RawUnicode kind of string function AnsiToRawUnicode(const AnsiText: RawByteString): RawUnicode; overload; /// convert any Ansi buffer into an Unicode String // - returns a value using our RawUnicode kind of string function AnsiToRawUnicode(Source: PAnsiChar; SourceChars: Cardinal): RawUnicode; overload; virtual; /// convert any Ansi buffer into an Unicode String // - returns a SynUnicode, i.e. Delphi 2009+ UnicodeString or a WideString function AnsiToUnicodeString(Source: PAnsiChar; SourceChars: Cardinal): SynUnicode; /// convert any Ansi Text into an UTF-8 encoded String // - internaly calls AnsiBufferToUTF8 virtual method function AnsiToUTF8(const AnsiText: RawByteString): RawUTF8; /// direct conversion of a PAnsiChar buffer into a UTF-8 encoded string // - will call AnsiBufferToUnicode() overloaded virtual method function AnsiBufferToRawUTF8(Source: PAnsiChar; SourceChars: Cardinal): RawUTF8; overload; /// direct conversion of an Unicode buffer into a PAnsiChar buffer // - Dest^ buffer must be reserved with at least SourceChars*3 bytes function UnicodeBufferToAnsi(Dest: PAnsiChar; Source: PWideChar; SourceChars: Cardinal): PAnsiChar; overload; virtual; /// direct conversion of an Unicode buffer into an Ansi Text function UnicodeBufferToAnsi(Source: PWideChar; SourceChars: Cardinal): RawByteString; overload; /// convert any Unicode-encoded String into Ansi Text // - internaly calls UnicodeBufferToAnsi virtual method function RawUnicodeToAnsi(const Source: RawUnicode): RawByteString; /// direct conversion of an UTF-8 encoded buffer into a PAnsiChar buffer // - Dest^ buffer must be reserved with at least SourceChars bytes function UTF8BufferToAnsi(Dest: PAnsiChar; Source: PUTF8Char; SourceChars: Cardinal): PAnsiChar; overload; virtual; /// convert any UTF-8 encoded buffer into Ansi Text // - internaly calls UTF8BufferToAnsi virtual method function UTF8BufferToAnsi(Source: PUTF8Char; SourceChars: Cardinal): RawByteString; overload; /// convert any UTF-8 encoded String into Ansi Text // - internaly calls UTF8BufferToAnsi virtual method function UTF8ToAnsi(const UTF8: RawUTF8): RawByteString; /// convert any Ansi Text (providing a From converted) into Ansi Text function AnsiToAnsi(From: TSynAnsiConvert; const Source: RawByteString): RawByteString; overload; /// convert any Ansi buffer (providing a From converted) into Ansi Text function AnsiToAnsi(From: TSynAnsiConvert; Source: PAnsiChar; SourceChars: cardinal): RawByteString; overload; /// corresponding code page property CodePage: Integer read fCodePage; end; /// a class to handle Ansi to/from Unicode translation of fixed width encoding // (i.e. non MBCS) // - this class will handle efficiently all Code Page availables without MBCS // encoding - like WinAnsi (1252) or Russian (1251) // - it will use internal fast look-up tables for such encodings // - this class could take some time to generate, and will consume more than // 64 KB of memory: you should not create your own class instance by yourself, // but should better retrieve an instance using TSynAnsiConvert.Engine(), which // will initialize either a TSynAnsiFixedWidth or a TSynAnsiConvert instance // on need // - this class has some additional methods (e.g. IsValid*) which take // advantage of the internal lookup tables to provide some fast process TSynAnsiFixedWidth = class(TSynAnsiConvert) protected fAnsiToWide: TWordDynArray; fWideToAnsi: TByteDynArray; public /// initialize the internal conversion engine constructor Create(aCodePage: integer); override; /// direct conversion of a PAnsiChar buffer into an Unicode buffer // - Dest^ buffer must be reserved with at least SourceChars*2 bytes function AnsiBufferToUnicode(Dest: PWideChar; Source: PAnsiChar; SourceChars: Cardinal): PWideChar; override; /// direct conversion of a PAnsiChar buffer into a UTF-8 encoded buffer // - Dest^ buffer must be reserved with at least SourceChars*3 bytes // - a #0 char is appended at the end (and result will point to it) function AnsiBufferToUTF8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; override; /// convert any Ansi buffer into an Unicode String // - returns a value using our RawUnicode kind of string function AnsiToRawUnicode(Source: PAnsiChar; SourceChars: Cardinal): RawUnicode; override; /// direct conversion of an Unicode buffer into a PAnsiChar buffer // - Dest^ buffer must be reserved with at least SourceChars*3 bytes function UnicodeBufferToAnsi(Dest: PAnsiChar; Source: PWideChar; SourceChars: Cardinal): PAnsiChar; override; /// direct conversion of an UTF-8 encoded buffer into a PAnsiChar buffer // - Dest^ buffer must be reserved with at least SourceChars bytes function UTF8BufferToAnsi(Dest: PAnsiChar; Source: PUTF8Char; SourceChars: Cardinal): PAnsiChar; override; /// conversion of a wide char into the corresponding Ansi character // - return -1 for an unknown WideChar in the current code page function WideCharToAnsiChar(wc: cardinal): integer; /// return TRUE if the supplied unicode buffer only contains characters of // the corresponding Ansi code page // - i.e. if the text can be displayed using this code page function IsValidAnsi(WideText: PWideChar; Length: integer): boolean; overload; /// return TRUE if the supplied unicode buffer only contains characters of // the corresponding Ansi code page // - i.e. if the text can be displayed using this code page function IsValidAnsi(WideText: PWideChar): boolean; overload; /// return TRUE if the supplied UTF-8 buffer only contains characters of // the corresponding Ansi code page // - i.e. if the text can be displayed using this code page function IsValidAnsiU(UTF8Text: PUTF8Char): boolean; /// return TRUE if the supplied UTF-8 buffer only contains 8 bits characters // of the corresponding Ansi code page // - i.e. if the text can be displayed with only 8 bit unicode characters // (e.g. no "tm" or such) within this code page function IsValidAnsiU8Bit(UTF8Text: PUTF8Char): boolean; /// direct access to the Ansi-To-Unicode lookup table // - use this array like AnsiToWide: array[byte] of word property AnsiToWide: TWordDynArray read fAnsiToWide; /// direct access to the Unicode-To-Ansi lookup table // - use this array like WideToAnsi: array[word] of byte // - any unhandled WideChar will return ord('?') property WideToAnsi: TByteDynArray read fWideToAnsi; end; var /// global TSynAnsiConvert instance to handle WinAnsi encoding (code page 1252) // - this instance is global and instantied during the whole program life time WinAnsiConvert: TSynAnsiFixedWidth; /// global TSynAnsiConvert instance to handle current system encoding // - this is the encoding as used by the AnsiString Delphi // - this instance is global and instantied during the whole program life time CurrentAnsiConvert: TSynAnsiConvert; const /// MIME content type used for JSON communication (as used by the Microsoft // WCF framework and the YUI framework) JSON_CONTENT_TYPE = 'application/json; charset=UTF-8'; /// MIME content type used for plain UTF-8 text ................................................................................ /// MIME content type used for UTF-8 encoded HTML HTML_CONTENT_TYPE = 'text/html; charset="UTF-8"'; /// US English Windows Code Page, i.e. WinAnsi standard character encoding CODEPAGE_US = 1252; /// conversion of a wide char into a WinAnsi (CodePage 1252) char // - return '?' for an unknown WideChar in code page 1252 function WideCharToWinAnsiChar(wc: cardinal): AnsiChar; {$ifdef HASINLINE}inline;{$endif} /// conversion of a wide char into a WinAnsi (CodePage 1252) char index // - return -1 for an unknown WideChar in code page 1252 function WideCharToWinAnsi(wc: cardinal): integer; {$ifdef HASINLINE}inline;{$endif} /// return TRUE if the supplied buffer only contains 7-bits Ansi characters function IsAnsiCompatible(PC: PAnsiChar): boolean; overload; /// return TRUE if the supplied buffer only contains 7-bits Ansi characters function IsAnsiCompatible(PC: PAnsiChar; Len: integer): boolean; overload; /// return TRUE if the supplied buffer only contains 7-bits Ansi characters function IsAnsiCompatible(PW: PWideChar): boolean; overload; /// return TRUE if the supplied text only contains 7-bits Ansi characters function IsAnsiCompatible(const Text: RawByteString): boolean; overload; {$ifdef HASINLINE}inline;{$endif} /// return TRUE if the supplied buffer only contains 7-bits Ansi characters function IsAnsiCompatible(PW: PWideChar; Len: integer): boolean; overload; /// return TRUE if the supplied unicode buffer only contains WinAnsi characters // - i.e. if the text can be displayed using ANSI_CHARSET function IsWinAnsi(WideText: PWideChar): boolean; overload; {$ifdef HASINLINE}inline;{$endif} /// return TRUE if the supplied unicode buffer only contains WinAnsi characters // - i.e. if the text can be displayed using ANSI_CHARSET function IsWinAnsi(WideText: PWideChar; Length: integer): boolean; overload; {$ifdef HASINLINE}inline;{$endif} /// return TRUE if the supplied UTF-8 buffer only contains WinAnsi characters // - i.e. if the text can be displayed using ANSI_CHARSET function IsWinAnsiU(UTF8Text: PUTF8Char): boolean; {$ifdef HASINLINE}inline;{$endif} /// return TRUE if the supplied UTF-8 buffer only contains WinAnsi 8 bit characters // - i.e. if the text can be displayed using ANSI_CHARSET with only 8 bit unicode // characters (e.g. no "tm" or such) function IsWinAnsiU8Bit(UTF8Text: PUTF8Char): boolean; {$ifdef HASINLINE}inline;{$endif} /// UTF-8 encode one Unicode character into Dest // - return the number of bytes written into Dest (i.e. 1,2 or 3) function UnicodeCharToUtf8(Dest: PUTF8Char; aWideChar: PtrUInt): integer; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a WinAnsi (CodePage 1252) string into a UTF-8 encoded String ................................................................................ // - faster than SysUtils: don't use Utf8Encode(WideString) -> no Windows.Global(), // and use a fixed pre-calculated array for individual chars conversion function WinAnsiToUtf8(WinAnsi: PAnsiChar; WinAnsiLen: integer): RawUTF8; overload; /// direct conversion of a WinAnsi PAnsiChar buffer into a UTF-8 encoded buffer // - Dest^ buffer must be reserved with at least SourceChars*3 function WinAnsiBufferToUtf8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a WinAnsi (CodePage 1252) string into a Unicode encoded String // - very fast, by using a fixed pre-calculated array for individual chars conversion function WinAnsiToRawUnicode(const S: WinAnsiString): RawUnicode; /// direct conversion of a WinAnsi (CodePage 1252) string into a Unicode buffer // - very fast, by using a fixed pre-calculated array for individual chars conversion // - text will be truncated if necessary to avoid buffer overflow in Dest[] procedure WinAnsiToUnicodeBuffer(const S: WinAnsiString; Dest: PWordArray; DestLen: integer); {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a UTF-8 encoded string into a WinAnsi String function Utf8ToWinAnsi(const S: RawUTF8): WinAnsiString; overload; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a UTF-8 encoded zero terminated buffer into a WinAnsi String function Utf8ToWinAnsi(P: PUTF8Char): WinAnsiString; overload; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a UTF-8 encoded zero terminated buffer into a RawUTF8 String procedure Utf8ToRawUTF8(P: PUTF8Char; var result: RawUTF8); {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a UTF-8 encoded buffer into a WinAnsi PAnsiChar buffer function UTF8ToWinPChar(dest: PAnsiChar; source: PUTF8Char; count: integer): integer; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a UTF-8 encoded buffer into a WinAnsi shortstring buffer procedure UTF8ToShortString(var dest: shortstring; source: PUTF8Char); /// convert an UTF-8 encoded text into a WideChar array // - faster than System.UTF8ToUnicode // - sourceBytes can by 0, therefore length is computed from zero terminated source // - enough place must be available in dest // - a WideChar(#0) is added at the end (if something is written) // - returns the BYTE count written in dest, excluding the ending WideChar(#0) function UTF8ToWideChar(dest: pWideChar; source: PUTF8Char; sourceBytes: PtrInt=0): PtrInt; overload; /// convert an UTF-8 encoded text into a WideChar array // - faster than System.UTF8ToUnicode // - this overloaded function expect a MaxDestChars parameter // - sourceBytes can not be 0 for this function // - enough place must be available in dest ................................................................................ /// convert a WideString into a UTF-8 string function WideStringToUTF8(const aText: WideString): RawUTF8; {$ifdef HASINLINE}inline;{$endif} /// direct conversion of a Unicode encoded buffer into a WinAnsi PAnsiChar buffer procedure RawUnicodeToWinPChar(dest: PAnsiChar; source: PWideChar; WideCharCount: integer); {$ifdef HASINLINE}inline;{$endif} /// convert a RawUnicode PWideChar into a WinAnsi (code page 1252) string function RawUnicodeToWinAnsi(P: PWideChar; WideCharCount: integer): WinAnsiString; overload; {$ifdef HASINLINE}inline;{$endif} /// convert a RawUnicode string into a WinAnsi (code page 1252) string function RawUnicodeToWinAnsi(const Unicode: RawUnicode): WinAnsiString; overload; {$ifdef HASINLINE}inline;{$endif} /// convert a WideString into a WinAnsi (code page 1252) string function WideStringToWinAnsi(const Wide: WideString): WinAnsiString; {$ifdef HASINLINE}inline;{$endif} /// convert an AnsiChar buffer (of a given code page) into a UTF-8 string procedure AnsiCharToUTF8(P: PAnsiChar; L: Integer; var result: RawUTF8; ACP: integer); /// convert any Raw Unicode encoded String into a generic SynUnicode Text function RawUnicodeToSynUnicode(const Unicode: RawUnicode): SynUnicode; overload; {$ifdef HASINLINE}inline;{$endif} ................................................................................ // - it's prefered to use TLanguageFile.StringToUTF8() method in SQLite3i18n, // which will handle full i18n of your application // - it will work as is with Delphi 2009/2010/XE (direct unicode conversion) // - under older version of Delphi (no unicode), it will use the // current RTL codepage, as with WideString conversion (but without slow // WideString usage) function StringToUTF8(const Text: string): RawUTF8; {$ifdef HASINLINE}inline;{$endif} /// convert any generic VCL Text buffer into an UTF-8 encoded buffer // - Dest must be able to receive at least SourceChars*3 bytes // - it will work as is with Delphi 2009/2010/XE (direct unicode conversion) // - under older version of Delphi (no unicode), it will use the // current RTL codepage, as with WideString conversion (but without slow // WideString usage) ................................................................................ const FileName: TFileName=''): RawUTF8; /// retrieve if some content is compressed, from a supplied binary buffer // - returns TRUE, if the header in binary buffer "may" be compressed (this method // can trigger false positives), e.g. begin with zip/gz/gif/wma/png/jpeg markers function IsContentCompressed(Content: Pointer; Len: integer): boolean; /// retrieve the index where to insert a PUTF8Char in a sorted PUTF8Char array // - R is the last index of available entries in P^ (i.e. Count-1) // - string comparison is case-sensitive (so will work with any PAnsiChar) // - returns -1 if the specified Value was found (i.e. adding will duplicate a value) function FastLocatePUTF8CharSorted(P: PPUTF8CharArray; R: PtrInt; Value: PUTF8Char): PtrInt; overload; /// retrieve the index where to insert a PUTF8Char in a sorted PUTF8Char array ................................................................................ /// the main executable name, without any path nor extension ProgramName: RawUTF8; /// the main executable details, as used e.g. by TSynLog // - e.g. 'C:\Dev\lib\SQLite3\exe\TestSQL3.exe 0.0.0.0 (2011-03-29)' ProgramFullSpec: RawUTF8; /// same as paramstr(0) ProgramFileName: TFileName; /// same as ExtractFilePath(paramstr(0)) ProgramFilePath: TFileName; /// the current executable version Version: TFileVersion; /// the current computer host name Host: RawUTF8; /// the current computer user name User: RawUTF8; end; ................................................................................ /// a TSynLogArchiveEvent handler which will delete older .log files function EventArchiveDelete(const aOldLogFileName, aDestinationPath: TFileName): boolean; /// a TSynLogArchiveEvent handler which will compress older .log files // using our proprietary SynLZ format // - resulting file will have the .synlz extension and will be located // in the aDestinationPath directory, i.e. TSynLogFamily.ArchivePath+'\log\YYYYMM\' // - use UnSynLZ.dpr tool to uncompress it into .log textual file // - SynLZ is much faster than zip for compression content, but proprietary function EventArchiveSynLZ(const aOldLogFileName, aDestinationPath: TFileName): boolean; resourcestring sInvalidIPAddress = '"%s" is an invalid IP v4 address'; ................................................................................ sValidationFieldDuplicate = 'Value already used for this unique key field'; implementation { ************ some fast UTF-8 / Unicode / Ansi conversion routines } var // internal list of TSynAnsiConvert instances SynAnsiConvertList: TObjectList = nil; { TSynAnsiConvert } const DefaultChar: AnsiChar = '?'; function TSynAnsiConvert.AnsiBufferToUnicode(Dest: PWideChar; Source: PAnsiChar; SourceChars: Cardinal): PWideChar; var c: cardinal; begin // first handle trailing 7 bit ASCII chars, by quad (Sha optimization) if SourceChars>=4 then repeat c := pCardinal(Source)^; if c and $80808080<>0 then break; // break on first non ASCII quad dec(SourceChars,4); inc(Source,4); pCardinal(Dest)^ := (c shl 8 or (c and $FF)) and $00ff00ff; c := c shr 16; pCardinal(Dest+2)^ := (c shl 8 or c) and $00ff00ff; inc(Dest,4); until SourceChars<4; if (SourceChars>0) and (ord(Source^)<128) then repeat dec(SourceChars); Dest^ := WideChar(ord(Source^)); inc(Source); inc(Dest); until (SourceChars=0) or (ord(Source^)>=128); // rely on the Operating System for all remaining ASCII characters if SourceChars=0 then result := Dest else result := Dest+MultiByteToWideChar(fCodePage,0,Source,SourceChars,Dest,SourceChars); result^ := #0; end; function TSynAnsiConvert.AnsiBufferToUTF8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; var tmp: array[byte] of WideChar; c: cardinal; U: PWideChar; begin // first handle trailing 7 bit ASCII chars, by quad (Sha optimization) if SourceChars>=4 then repeat c := pCardinal(Source)^; if c and $80808080<>0 then break; // break on first non ASCII quad pCardinal(Dest)^ := c; dec(SourceChars,4); inc(Source,4); inc(Dest,4); until SourceChars<4; if (SourceChars>0) and (ord(Source^)<128) then repeat Dest^ := Source^; dec(SourceChars); inc(Source); inc(Dest); until (SourceChars=0) or (ord(Source^)>=128); // rely on the Operating System for all remaining ASCII characters if SourceChars=0 then result := Dest else if SourceChars<SizeOf(tmp) shr 1 then result := Dest+RawUnicodeToUTF8(Dest,SourceChars*3,tmp, AnsiBufferToUnicode(tmp,Source,SourceChars)-tmp) else begin GetMem(U,SourceChars*2+2); result := Dest+RawUnicodeToUtf8(Dest,SourceChars*3,U, AnsiBufferToUnicode(U,Source,SourceChars)-U); FreeMem(U); end; result^ := #0; end; function TSynAnsiConvert.AnsiToRawUnicode(const AnsiText: RawByteString): RawUnicode; begin result := AnsiToRawUnicode(pointer(AnsiText),length(AnsiText)); end; function TSynAnsiConvert.AnsiToRawUnicode(Source: PAnsiChar; SourceChars: Cardinal): RawUnicode; var U: PWideChar; begin if SourceChars=0 then result := '' else begin SetString(result,nil,SourceChars*2+1); U := AnsiBufferToUnicode(pointer(result),Source,SourceChars); U^ := #0; SetLength(result,PtrUInt(U)-PtrUInt(result)+1); end; end; function TSynAnsiConvert.AnsiToUnicodeString(Source: PAnsiChar; SourceChars: Cardinal): SynUnicode; begin result := ''; if SourceChars<>0 then begin SetLength(result,SourceChars); SetLength(result,AnsiBufferToUnicode(pointer(result),Source,SourceChars)-pointer(result)); end; end; function TSynAnsiConvert.AnsiToUTF8(const AnsiText: RawByteString): RawUTF8; begin result := AnsiBufferToRawUTF8(pointer(AnsiText),length(AnsiText)); end; function TSynAnsiConvert.AnsiBufferToRawUTF8(Source: PAnsiChar; SourceChars: Cardinal): RawUTF8; var tmpU8: array[0..256*3] of AnsiChar; U8: PUTF8Char; begin if SourceChars=0 then result := '' else if SourceChars<SizeOf(tmpU8) div 3 then SetString(result,tmpU8,AnsiBufferToUTF8(tmpU8,Source,SourceChars)-tmpU8) else begin Getmem(U8,SourceChars*3); SetString(result,U8,AnsiBufferToUTF8(U8,Source,SourceChars)-U8); FreeMem(U8); end; end; constructor TSynAnsiConvert.Create(aCodePage: integer); begin fCodePage := aCodePage; end; function IsFixedWidthCodePage(aCodePage: integer): boolean; begin result := (aCodePage>=1250) and (aCodePage<=1257); end; class function TSynAnsiConvert.Engine(aCodePage: integer): TSynAnsiConvert; var i: integer; begin if SynAnsiConvertList=nil then begin SynAnsiConvertList := TObjectList.Create; GarbageCollector.Add(SynAnsiConvertList); // global list end else for i := 0 to SynAnsiConvertList.Count-1 do begin result := SynAnsiConvertList.List[i]; if result.CodePage=aCodePage then exit; end; if IsFixedWidthCodePage(aCodePage) then result := TSynAnsiFixedWidth.Create(aCodePage) else result := TSynAnsiConvert.Create(aCodePage); SynAnsiConvertList.Add(result); end; function TSynAnsiConvert.UnicodeBufferToAnsi(Dest: PAnsiChar; Source: PWideChar; SourceChars: Cardinal): PAnsiChar; var c: cardinal; begin // first handle trailing 7 bit ASCII chars, by pairs (Sha optimization) if SourceChars>=2 then repeat c := PCardinal(Source)^; if c and $ff80ff80<>0 then break; // break on first non ASCII pair dec(SourceChars,2); inc(Source,2); c := c shr 8 or c; pWord(Dest)^ := c; inc(Dest,2); until SourceChars<2; if (SourceChars>0) and (ord(Source^)<128) then repeat Dest^ := AnsiChar(ord(Source^)); dec(SourceChars); inc(Source); inc(Dest); until (SourceChars=0) or (ord(Source^)>=128); // rely on the Operating System for all remaining ASCII characters if SourceChars=0 then result := Dest else result := Dest+WideCharToMultiByte( fCodePage,0,Source,SourceChars,Dest,SourceChars*3,@DefaultChar,nil); end; function TSynAnsiConvert.UTF8BufferToAnsi(Dest: PAnsiChar; Source: PUTF8Char; SourceChars: Cardinal): PAnsiChar; var tmp: array[0..256*2-1] of WideChar; U: PWideChar; begin if SourceChars<SizeOf(tmp) shr 1 then result := UnicodeBufferToAnsi(Dest,tmp,UTF8ToWideChar(tmp,Source,SourceChars) shr 1) else begin Getmem(U,SourceChars*2+1); result := UnicodeBufferToAnsi(Dest,U,UTF8ToWideChar(U,Source,SourceChars) shr 1); Freemem(U); end; end; function TSynAnsiConvert.UTF8BufferToAnsi(Source: PUTF8Char; SourceChars: Cardinal): RawByteString; var tmpA: array[byte] of AnsiChar; A: PAnsiChar; begin if SourceChars=0 then result := '' else begin if SourceChars<SizeOf(tmpA) then SetString(result,tmpA,Utf8BufferToAnsi(tmpA,Source,SourceChars)-tmpA) else begin Getmem(A,SourceChars+1); SetString(result,A,Utf8BufferToAnsi(A,Source,SourceChars)-A); FreeMem(A); end; {$ifdef UNICODE} PWord(PtrInt(result)-12)^ := fCodePage; // force set code page {$endif} end; end; function TSynAnsiConvert.UTF8ToAnsi(const UTF8: RawUTF8): RawByteString; begin result := UTF8BufferToAnsi(pointer(UTF8),length(UTF8)); end; function TSynAnsiConvert.UnicodeBufferToAnsi(Source: PWideChar; SourceChars: Cardinal): RawByteString; var tmpA: array[byte] of AnsiChar; A: PAnsiChar; begin if SourceChars=0 then result := '' else begin if SourceChars<SizeOf(tmpA) then SetString(result,tmpA,UnicodeBufferToAnsi(tmpA,Source,SourceChars)-tmpA) else begin Getmem(A,SourceChars+1); SetString(result,A,UnicodeBufferToAnsi(A,Source,SourceChars)-A); FreeMem(A); end; {$ifdef UNICODE} PWord(PtrInt(result)-12)^ := fCodePage; // force set code page {$endif} end; end; function TSynAnsiConvert.RawUnicodeToAnsi(const Source: RawUnicode): RawByteString; begin result := UnicodeBufferToAnsi(pointer(Source),length(Source)); end; function TSynAnsiConvert.AnsiToAnsi(From: TSynAnsiConvert; const Source: RawByteString): RawByteString; begin if From=self then result := Source else result := AnsiToAnsi(From,pointer(Source),length(Source)); end; function TSynAnsiConvert.AnsiToAnsi(From: TSynAnsiConvert; Source: PAnsiChar; SourceChars: cardinal): RawByteString; var tmpU: array[byte] of WideChar; U: PWideChar; begin if From=self then SetString(result,Source,SourceChars) else if SourceChars<sizeof(tmpU) shr 1 then result := UnicodeBufferToAnsi(tmpU,From.AnsiBufferToUnicode(tmpU,Source,SourceChars)-tmpU) else begin GetMem(U,SourceChars*2+2); result := UnicodeBufferToAnsi(U,From.AnsiBufferToUnicode(U,Source,SourceChars)-U); FreeMem(U); end; end; { TSynAnsiFixedWidth } function TSynAnsiFixedWidth.AnsiBufferToUnicode(Dest: PWideChar; Source: PAnsiChar; SourceChars: Cardinal): PWideChar; var i: Integer; begin for i := 1 to SourceChars shr 2 do begin Dest[0] := WideChar(fAnsiToWide[Ord(Source[0])]); Dest[1] := WideChar(fAnsiToWide[Ord(Source[1])]); Dest[2] := WideChar(fAnsiToWide[Ord(Source[2])]); Dest[3] := WideChar(fAnsiToWide[Ord(Source[3])]); inc(Source,4); inc(Dest,4); end; for i := 1 to SourceChars and 3 do begin Dest^ := WideChar(fAnsiToWide[Ord(Source^)]); inc(Dest); inc(Source); end; result := Dest; end; function TSynAnsiFixedWidth.AnsiBufferToUTF8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; var EndSource, EndSourceBy4: PAnsiChar; c: Cardinal; label By4, By1; // ugly but faster begin if (self=nil) or (Dest=nil) then begin Result := nil; Exit; end else if (Source<>nil) and (SourceChars>0) then begin // first handle 7 bit ASCII WideChars, by quads (Sha optimization) EndSource := Source+SourceChars; EndSourceBy4 := EndSource-4; if Source<=EndSourceBy4 then repeat By4: c := pCardinal(Source)^; if c and $80808080<>0 then goto By1; // break on first non ASCII quad inc(Source,4); pCardinal(Dest)^ := c; inc(Dest,4); until Source>EndSourceBy4; // generic loop, handling one WideChar per iteration if Source<EndSource then repeat By1: c := byte(Source^); inc(Source); if c<=$7F then begin Dest^ := AnsiChar(c); // 0..127 don't need any translation Inc(Dest); if Source<EndSource then continue else break; end else begin c := fAnsiToWide[c]; // convert WinAnsi char into Unicode char if c>$7ff then begin Dest[0] := AnsiChar($E0 or (c shr 12)); Dest[1] := AnsiChar($80 or ((c shr 6) and $3F)); Dest[2] := AnsiChar($80 or (c and $3F)); Inc(Dest,3); if Source<EndSourceBy4 then goto By4 else if Source<EndSource then continue else break; end else begin Dest[0] := AnsiChar($C0 or (c shr 6)); Dest[1] := AnsiChar($80 or (c and $3F)); Inc(Dest,2); if Source<EndSourceBy4 then goto By4 else if Source<EndSource then continue else break; end; end; until false; end; Dest^ := #0; Result := Dest; end; function TSynAnsiFixedWidth.AnsiToRawUnicode(Source: PAnsiChar; SourceChars: Cardinal): RawUnicode; begin if SourceChars=0 then result := '' else begin SetString(result,nil,SourceChars*2+1); AnsiBufferToUnicode(pointer(result),Source,SourceChars)^ := #0; end; end; constructor TSynAnsiFixedWidth.Create(aCodePage: integer); var i: integer; PW: PWideChar; A256: array[0..256] of AnsiChar; U256: array[0..256] of WideChar; // AnsiBufferToUnicode() write a last #0 begin inherited; if IsFixedWidthCodePage(aCodePage) then begin // create internal look-up tables from Operating System returned values SetLength(fAnsiToWide,256); for i := 0 to 255 do A256[i] := AnsiChar(i); PW := inherited AnsiBufferToUnicode(U256,A256,256); Assert(PW-U256=256); move(U256,fAnsiToWide[0],256*2); SetLength(fWideToAnsi,65536); fillchar(fWideToAnsi[1],65535,ord('?')); // '?' for unknown char for i := 1 to 255 do if fAnsiToWide[i]<>0 then fWideToAnsi[fAnsiToWide[i]] := i; end else raise Exception.CreateFmt('%s.Create - Invalid code page %d',[ClassName,fCodePage]); end; function TSynAnsiFixedWidth.IsValidAnsi(WideText: PWideChar; Length: integer): boolean; var i: integer; wc: cardinal; begin result := false; if WideText<>nil then for i := 0 to Length-1 do begin wc := cardinal(WideText[i]); if wc=0 then break else if wc<256 then if fAnsiToWide[wc]<256 then continue else exit else if fWideToAnsi[wc]=ord('?') then exit else continue; end; result := true; end; function TSynAnsiFixedWidth.IsValidAnsi(WideText: PWideChar): boolean; var wc: cardinal; begin result := false; if WideText<>nil then repeat wc := cardinal(WideText^); inc(WideText); if wc=0 then break else if wc<256 then if fAnsiToWide[wc]<256 then continue else exit else if fWideToAnsi[wc]=ord('?') then exit else continue; until false; result := true; end; function TSynAnsiFixedWidth.IsValidAnsiU(UTF8Text: PUTF8Char): boolean; var c: Cardinal; begin result := false; if UTF8Text<>nil then repeat c := byte(UTF8Text^); inc(UTF8Text); if c=0 then break else if c and $80=0 then continue else begin if UTF8Text^=#0 then break; if c and $20=0 then begin c := c shl 6+byte(UTF8Text^)-$00003080; inc(UTF8Text); end else begin c := c shl 6+byte(UTF8Text^); inc(UTF8Text); if UTF8Text^=#0 then break; c := c shl 6+byte(UTF8Text^)-$000E2080; inc(UTF8Text); end; if fWideToAnsi[c]=ord('?') then exit; // invalid char in the WinAnsi code page end; until false; result := true; end; function TSynAnsiFixedWidth.IsValidAnsiU8Bit(UTF8Text: PUTF8Char): boolean; var c: Cardinal; begin result := false; if UTF8Text<>nil then repeat c := byte(UTF8Text^); inc(UTF8Text); if c=0 then break else if c and $80=0 then continue else begin if UTF8Text^=#0 then break; if c and $20=0 then begin c := c shl 6+byte(UTF8Text^)-$00003080; inc(UTF8Text); end else begin c := c shl 6+byte(UTF8Text^); inc(UTF8Text); if UTF8Text^=#0 then break; c := c shl 6+byte(UTF8Text^)-$000E2080; inc(UTF8Text); end; if (c>255) or (fAnsiToWide[c]>255) then exit; // not 8 bit char (like "tm" or such) is marked invalid end; until false; result := true; end; function TSynAnsiFixedWidth.UnicodeBufferToAnsi(Dest: PAnsiChar; Source: PWideChar; SourceChars: Cardinal): PAnsiChar; var i: Integer; begin for i := 1 to SourceChars shr 2 do begin Dest[0] := AnsiChar(fWideToAnsi[Ord(Source[0])]); Dest[1] := AnsiChar(fWideToAnsi[Ord(Source[1])]); Dest[2] := AnsiChar(fWideToAnsi[Ord(Source[2])]); Dest[3] := AnsiChar(fWideToAnsi[Ord(Source[3])]); inc(Source,4); inc(Dest,4); end; for i := 1 to SourceChars and 3 do begin Dest^ := AnsiChar(fWideToAnsi[Ord(Source^)]); inc(Dest); inc(Source); end; result := Dest; end; function TSynAnsiFixedWidth.UTF8BufferToAnsi(Dest: PAnsiChar; Source: PUTF8Char; SourceChars: Cardinal): PAnsiChar; var c: cardinal; begd: PAnsiChar; endSource, endSourceBy4: PUTF8Char; label By1, By4; // ugly but faster begin begd := Dest; // first handle trailing 7 bit ASCII chars, by quad (Sha optimization) endSource := Source+SourceChars; endSourceBy4 := endSource-4; if Source<=endSourceBy4 then repeat By4:c := pCardinal(Source)^; if c and $80808080<>0 then goto By1; // break on first non ASCII quad inc(Source,4); pCardinal(Dest)^ := c; inc(Dest,4); until Source>endSourceBy4; // generic loop, handling one UTF-8 code per iteration if Source<endSource then repeat By1:c := byte(Source^); inc(Source); if byte(c) and $80=0 then begin Dest^ := AnsiChar(byte(c)); inc(Dest); if Source<endSource then continue else break; end else begin if Source>=endSource then break; if c and $20=0 then begin c := c shl 6+byte(Source^)-$00003080; inc(Source); end else begin c := c shl 6+byte(Source^); inc(Source); if Source>=endSource then break; c := c shl 6+byte(Source^)-$000E2080; inc(Source); end; Dest^ := AnsiChar(fWideToAnsi[c]); inc(Dest); if Source<endSourceBy4 then goto By4 else if Source<endSource then continue else break; end; until false; result := Dest; end; function TSynAnsiFixedWidth.WideCharToAnsiChar(wc: cardinal): integer; begin if wc<256 then if fAnsiToWide[wc]<256 then result := wc else result := -1 else if wc<=65535 then begin result := fWideToAnsi[wc]; if result=ord('?') then result := -1; end else result := -1; end; function UnicodeCharToUtf8(Dest: PUTF8Char; aWideChar: PtrUInt): integer; begin if aWideChar<=$7F then begin Dest^ := AnsiChar(aWideChar); result := 1; end else ................................................................................ Dest[0] := AnsiChar($C0 or (aWideChar shr 6)); Dest[1] := AnsiChar($80 or (aWideChar and $3F)); result := 2; end; end; function WinAnsiBufferToUtf8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal): PUTF8Char; begin result := WinAnsiConvert.AnsiBufferToUTF8(Dest,Source,SourceChars); end; procedure WinAnsiToUnicodeBuffer(const S: WinAnsiString; Dest: PWordArray; DestLen: integer); var L: PtrInt; begin L := length(S); if L<>0 then begin if L>=DestLen then L := DestLen-1; // truncate to avoid buffer overflow WinAnsiConvert.AnsiBufferToUnicode(PWideChar(Dest),pointer(S),L); // include last #0 end else Dest^[0] := 0; end; function WinAnsiToRawUnicode(const S: WinAnsiString): RawUnicode; begin WinAnsiConvert.AnsiToRawUnicode(S); end; function WinAnsiToUtf8(const S: WinAnsiString): RawUTF8; begin result := WinAnsiConvert.AnsiBufferToRawUTF8(pointer(S),length(s)); end; function WinAnsiToUtf8(WinAnsi: PAnsiChar; WinAnsiLen: integer): RawUTF8; begin result := WinAnsiConvert.AnsiBufferToRawUTF8(WinAnsi,WinAnsiLen); end; function WideCharToWinAnsiChar(wc: cardinal): AnsiChar; begin wc := WinAnsiConvert.WideCharToAnsiChar(wc); if integer(wc)=-1 then result := '?' else result := AnsiChar(wc); end; function WideCharToWinAnsi(wc: cardinal): integer; begin result := WinAnsiConvert.WideCharToAnsiChar(wc); end; function IsWinAnsi(WideText: PWideChar; Length: integer): boolean; begin result := WinAnsiConvert.IsValidAnsi(WideText,Length); end; function IsAnsiCompatible(PC: PAnsiChar): boolean; begin result := false; if PC<>nil then while true do ................................................................................ if PC^=#0 then break else if PC^<=#127 then inc(PC) else // 7 bits chars are always OK, whatever codepage/charset is used exit; result := true; end; function IsAnsiCompatible(PC: PAnsiChar; Len: integer): boolean; var i: integer; begin result := false; if PC<>nil then begin for i := 1 to Len shr 2 do if PCardinal(PC)^ and $80808080<>0 then exit else inc(PC,4); for i := 0 to (Len and 3)-1 do if PC[i]>=#127 then exit; end; result := true; end; function IsAnsiCompatible(const Text: RawByteString): boolean; overload; begin result := IsAnsiCompatible(PAnsiChar(pointer(Text)),length(Text)); end; function IsAnsiCompatible(PW: PWideChar): boolean; overload; begin result := false; if PW<>nil then while true do if ord(PW^)=0 then break else if ord(PW^)<=127 then inc(PW) else // 7 bits chars are always OK, whatever codepage/charset is used exit; result := true; end; function IsAnsiCompatible(PW: PWideChar; Len: integer): boolean; overload; var i: integer; ................................................................................ for i := 0 to Len-1 do if ord(PW[i])>127 then exit; result := true; end; function IsWinAnsi(WideText: PWideChar): boolean; begin result := WinAnsiConvert.IsValidAnsi(WideText); end; function IsWinAnsiU(UTF8Text: PUTF8Char): boolean; begin result := WinAnsiConvert.IsValidAnsiU(UTF8Text); end; function IsWinAnsiU8Bit(UTF8Text: PUTF8Char): boolean; begin result := WinAnsiConvert.IsValidAnsiU8Bit(UTF8Text); end; function UTF8ToWinPChar(dest: PAnsiChar; source: PUTF8Char; count: integer): integer; begin result := WinAnsiConvert.UTF8BufferToAnsi(dest,source,count)-dest; end; procedure UTF8ToShortString(var dest: shortstring; source: PUTF8Char); var c: cardinal; len: integer; begin len := 0; ................................................................................ end else begin c := c shl 6+byte(source^); inc(source); if source^=#0 then break; c := c shl 6+byte(source^)-$000E2080; inc(source); end; // #256.. -> slower but accurate conversion inc(len); dest[len] := AnsiChar(WinAnsiConvert.WideCharToAnsiChar(c)); if len<255 then continue else break; end; until false; dest[0] := AnsiChar(len); end; function Utf8ToWinAnsi(const S: RawUTF8): WinAnsiString; begin result := WinAnsiConvert.UTF8ToAnsi(S); end; function Utf8ToWinAnsi(P: PUTF8Char): WinAnsiString; begin result := WinAnsiConvert.UTF8ToAnsi(P); end; procedure Utf8ToRawUTF8(P: PUTF8Char; var result: RawUTF8); begin // fast and Delphi 2009/2010/XE ready SetString(result,PAnsiChar(P),StrLen(P)); end; ................................................................................ end; result := PtrInt(Dest)-result; end; /// convert a RawUnicode PChar into a UTF-8 string function RawUnicodeToUtf8(P: PWideChar; WideCharCount: integer): RawUTF8; var L,LW: integer; U8: array[0..511] of AnsiChar; begin if WideCharCount=0 then begin result := ''; exit; end; LW := WideCharCount*3; // maximum resulting length if LW<SizeOf(U8) then begin // faster computation without temporary heap allocation SetString(Result,U8,RawUnicodeToUtf8(U8,sizeof(U8),P,WideCharCount)); exit; end; SetString(result,nil,LW); L := RawUnicodeToUtf8(pointer(result),LW+1,P,WideCharCount); if L<=0 then result := '' else if L<>LW then ................................................................................ var LW: integer; begin result := ''; // somewhat faster if result is freed before any SetLength() if WideCharCount=0 then exit; LW := WideCharCount*3; // maximum resulting length SetLength(result,LW); UTF8Length := RawUnicodeToUtf8(pointer(result),LW+1,P,WideCharCount); if UTF8Length<=0 then result := ''; end; /// convert a RawUnicode string into a UTF-8 string function RawUnicodeToUtf8(const Unicode: RawUnicode): RawUTF8; ................................................................................ function RawUnicodeToSynUnicode(P: PWideChar; WideCharCount: integer): SynUnicode; overload; begin SetString(result,P,WideCharCount); end; procedure RawUnicodeToWinPChar(dest: PAnsiChar; source: PWideChar; WideCharCount: Integer); begin WinAnsiConvert.UnicodeBufferToAnsi(dest,source,WideCharCount); end; function RawUnicodeToWinAnsi(P: PWideChar; WideCharCount: integer): WinAnsiString; overload; begin result := WinAnsiConvert.UnicodeBufferToAnsi(P,WideCharCount); end; function RawUnicodeToWinAnsi(const Unicode: RawUnicode): WinAnsiString; begin result := WinAnsiConvert.UnicodeBufferToAnsi(pointer(Unicode),length(Unicode) shr 1); end; function WideStringToWinAnsi(const Wide: WideString): WinAnsiString; begin result := WinAnsiConvert.UnicodeBufferToAnsi(pointer(Wide),length(Wide)); end; procedure UnicodeBufferToWinAnsi(source: PWideChar; out Dest: WinAnsiString); var L: integer; begin L := StrLenW(source); SetLength(Dest,L); WinAnsiConvert.UnicodeBufferToAnsi(pointer(Dest),source,L); end; function UnicodeBufferToString(source: PWideChar): string; begin result := RawUnicodeToString(source,StrLenW(source)); end; procedure AnsiCharToUTF8(P: PAnsiChar; L: Integer; var result: RawUTF8; ACP: integer); begin result := TSynAnsiConvert.Engine(ACP).AnsiBufferToRawUTF8(P,L); end; {$ifdef UNICODE} function UnicodeStringToUtf8(const S: string): RawUTF8; begin result := RawUnicodeToUtf8(pointer(S),length(S)); end; function UnicodeStringToWinAnsi(const S: string): WinAnsiString; begin ................................................................................ SetLength(result,L2); end; function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString; var i: integer; begin SetString(result,nil,WinAnsiLen); WinAnsiConvert.AnsiBufferToUnicode(pointer(result),WinAnsi,WinAnsiLen); end; {$endif} {$ifdef UNICODE} function Ansi7ToString(const Text: RawByteString): string; var i: integer; begin ................................................................................ function StringToWinAnsi(const Text: string): WinAnsiString; begin result := RawUnicodeToWinAnsi(Pointer(Text),length(Text)); end; {$else} function StringToWinAnsi(const Text: string): WinAnsiString; begin result := WinAnsiConvert.AnsiToAnsi(CurrentAnsiConvert,Text); end; {$endif} {$ifdef UNICODE} function StringBufferToUtf8(Dest: PUTF8Char; Source: PChar; SourceChars: PtrInt): PUTF8Char; begin result := Dest+RawUnicodeToUtf8(Dest,SourceChars*3,PWideChar(Source),SourceChars); end; {$else} function StringBufferToUtf8(Dest: PUTF8Char; Source: PChar; SourceChars: PtrInt): PUTF8Char; begin result := CurrentAnsiConvert.AnsiBufferToUTF8(Dest,Source,SourceChars); end; {$endif} {$ifdef UNICODE} function StringToUTF8(const Text: string): RawUTF8; begin result := RawUnicodeToUtf8(pointer(Text),length(Text)); end; {$else} function StringToUTF8(const Text: string): RawUTF8; begin result := CurrentAnsiConvert.AnsiToUTF8(Text); end; {$endif} {$ifdef UNICODE} function StringToRawUnicode(const S: string): RawUnicode; begin SetString(result,PAnsiChar(pointer(S)),length(S)*2+1); // +1 for last wide #0 end; {$else} function StringToRawUnicode(const S: string): RawUnicode; begin result := CurrentAnsiConvert.AnsiToRawUnicode(S); end; {$endif} {$ifdef UNICODE} function StringToRawUnicode(P: PChar; L: integer): RawUnicode; begin SetString(result,PAnsiChar(P),L*2+1); // +1 for last wide #0 end; {$else} function StringToRawUnicode(P: PChar; L: integer): RawUnicode; begin result := CurrentAnsiConvert.AnsiToRawUnicode(P,L); end; {$endif} {$ifdef UNICODE} function RawUnicodeToString(P: PWideChar; L: integer): string; overload; begin SetString(result,P,L); end; {$else} function RawUnicodeToString(P: PWideChar; L: integer): string; overload; begin result := CurrentAnsiConvert.UnicodeBufferToAnsi(P,L); end; {$endif} {$ifdef UNICODE} procedure RawUnicodeToString(P: PWideChar; L: integer; var result: string); overload; begin SetString(result,P,L); end; {$else} procedure RawUnicodeToString(P: PWideChar; L: integer; var result: string); overload; begin result := CurrentAnsiConvert.UnicodeBufferToAnsi(P,L); end; {$endif} {$ifdef UNICODE} function RawUnicodeToString(const U: RawUnicode): string; begin // uses StrLenW() and not length(U) to handle case when was used as buffer SetString(result,PWideChar(pointer(U)),StrLenW(Pointer(U))); end; {$else} function RawUnicodeToString(const U: RawUnicode): string; begin // uses StrLenW() and not length(U) to handle case when was used as buffer result := CurrentAnsiConvert.UnicodeBufferToAnsi(Pointer(U),StrLenW(Pointer(U))); end; {$endif} {$ifdef UNICODE} function UTF8DecodeToString(P: PUTF8Char; L: integer): string; begin result := UTF8DecodeToUnicodeString(P,L); end; {$else} function UTF8DecodeToString(P: PUTF8Char; L: integer): string; begin result := CurrentAnsiConvert.UTF8BufferToAnsi(P,L); end; {$endif} {$ifdef UNICODE} function UTF8ToString(const Text: RawUTF8): string; begin result := UTF8DecodeToUnicodeString(pointer(Text),length(Text)); end; {$else} function UTF8ToString(const Text: RawUTF8): string; begin result := CurrentAnsiConvert.UTF8BufferToAnsi(pointer(Text),length(Text)); end; {$endif} function UTF8ToWideString(const Text: RawUTF8): WideString; begin UTF8ToWideString(Text,result); end; procedure UTF8ToWideString(const Text: RawUTF8; var result: WideString); ................................................................................ begin if Offset = 1 then Result := Pos(RawByteString(SubStr),RawByteString(S)) else begin I := Offset; LenSubStr := Length(SubStr); Len := Length(S)-LenSubStr+1; C := SubStr[1]; while I<=Len do begin if S[I]=C then begin X := 1; while (X<LenSubStr) and (S[I+X]=SubStr[X+1]) do Inc(X); if X=LenSubStr then begin Result := I; exit; ................................................................................ for i := 0 to 255 do begin d := NormToUpperByte[i]; if d in [ord('A')..ord('Z')] then inc(d,32); NormToLowerByte[i] := d; end; {$endif OWNNORMTOUPPER} // code below is 55 bytes long, therefore shorter than a const array fillchar(ConvertHexToBin[0],sizeof(ConvertHexToBin),255); // all to 255 v := 0; for i := ord('0') to ord('9') do begin ConvertHexToBin[i] := v; inc(v); end; for i := ord('A') to ord('F') do begin ConvertHexToBin[i] := v; ConvertHexToBin[i+(ord('a')-ord('A'))] := v; inc(v); end; // initialize our most used TSynAnsiConvert engines WinAnsiConvert := TSynAnsiConvert.Engine(CODEPAGE_US) as TSynAnsiFixedWidth; CurrentAnsiConvert := TSynAnsiConvert.Engine(GetACP); end; {$ifdef MSWINDOWS} const // lpMinimumApplicationAddress retrieved from Windows is very low $10000 // - i.e. maximum number of ID per table would be 65536 in TSQLRecord.GetID // - so we'll force an higher and almost "safe" value as 1,048,576 // (real value from runnning Windows is greater than $400000) MIN_PTR_VALUE = $100000; // see http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx ................................................................................ result := NormToUpperByte[result]; {$else} result := NormToUpperAnsi7Byte[result]); {$endif} exit; end else if result and $20=0 then begin result := result shl 6+byte(U[1])-$00003080; inc(U,2); if (result<=255) and (WinAnsiConvert.AnsiToWide[result]<=255) then {$ifdef USENORMTOUPPER} result := NormToUpperByte[result] {$else} result := NormToUpperAnsi7Byte[result]) {$endif} else result := ord('?'); // char ignored for soundex exit; end else begin inc(U,3); ................................................................................ if c and $80=0 then begin D[result] := AnsiChar(Table[c]); inc(result); end else if c and $20=0 then begin // UTF-8 decode c := c shl 6+byte(P[0])-$00003080; inc(P); if (c<=255) and (WinAnsiConvert.AnsiToWide[c]<=255) then begin c := WinAnsiConvert.AnsiToWide[Table[c]]; // convert to Unicode if c<=127 then begin D[result] := AnsiChar(Table[c]); inc(result); end else goto nxt; // leave UTF-8 encoding untouched continue; end; ................................................................................ FileWrite(F,pointer(aLine)^,length(aLine)); FileClose(F); end; procedure LogToTextFile(Msg: RawUTF8); begin if Msg='' then begin Msg := StringToUTF8(SysErrorMessage(GetLastError)); if Msg='' then exit; end; AppendToTextFile(Msg,ChangeFileExt(paramstr(0),'.log')); end; ................................................................................ vtInt64: Add(VInt64^); vtExtended: Add(VExtended^); end; end; procedure TTextWriter.AddJSONEscapeString(const s: string); {$ifndef UNICODE} var tmp: PWideChar; L: PtrInt; {$endif} begin {$ifdef UNICODE} AddJSONEscapeW(pointer(s)); {$else} L := length(S); GetMem(tmp,L*2+2); L := CurrentAnsiConvert.AnsiBufferToUnicode(tmp,pointer(s),L)-tmp; AddJSONEscapeW(pointer(tmp),L); Freemem(tmp); {$endif} end; procedure TTextWriter.AddPropName(const PropName: ShortString); begin if ord(PropName[0])=0 then exit; ................................................................................ ch: PtrInt; begin State := STATE_BEGIN; subdomains := 1; if P<>nil then repeat ch := NextUTF8Char(P,P); if (ch<=255) and (WinAnsiConvert.AnsiToWide[ch]<=255) then // convert into WinAnsi char c := AnsiChar(ch) else // invalid char c := #127; case State of STATE_BEGIN: if c in atom_chars then ................................................................................ Check(IsWinAnsi(pointer(Unic),length(Unic)shr 1)=WA); Check(IsWinAnsiU(pointer(U))=WA); Check(UpperCase(LowerCase(U))=UpperCase(U)); {$ifndef ENHANCEDRTL} Check(LowerCase(U)=RawUTF8(SysUtils.LowerCase(string(U)))); Check(UpperCase(U)=RawUTF8(SysUtils.UpperCase(string(U)))); {$endif} if CurrentAnsiConvert.CodePage=CODEPAGE_US then // initial text above is WinAnsiString (CP 1252) Check(StringToUTF8(UTF8ToString(U))=U); Up := UpperCaseUnicode(U); Check(Up=UpperCaseUnicode(LowerCaseUnicode(U))); Check(kr32(0,pointer(U),length(U))=kr32pas(pointer(U),length(U))); if U='' then continue; Check(UnQuoteSQLString(pointer(QuotedStr(U,'"')),res)<>nil); ................................................................................ end; end; {$ifdef USESYNTABLEVARIANT} function TSynTableFieldProperties.GetVariant(FieldBuffer: pointer): Variant; var len: integer; PB: PByte absolute FieldBuffer; PA: PAnsiChar absolute FieldBuffer; PU: PUTF8Char absolute FieldBuffer; tmp: RawByteString; begin case FieldType of // fixed-sized field value tftBoolean: result := PBoolean(FieldBuffer)^; tftUInt8: ................................................................................ tftVarInt32: result := FromVarInt32(PB); tftVarUInt64: result := FromVarUInt64(PB); tftVarInt64: result := FromVarInt64(PB); // text storage - WinAnsi could use less space than UTF-8 tftWinAnsi: begin len := FromVarUInt32(PB); if len>0 then {$ifdef UNICODE} result := WinAnsiToUnicodeString(PA,len) {$else} result := CurrentAnsiConvert.AnsiToAnsi(WinAnsiConvert,PA,len) {$endif} else result := ''; end; tftUTF8: begin len := FromVarUInt32(PB); if len>0 then {$ifdef UNICODE} result := UTF8DecodeToUnicodeString(PU,len) {$else} result := CurrentAnsiConvert.UTF8BufferToAnsi(PU,len) {$endif} else result := ''; end; tftBlobInternal: begin len := FromVarUInt32(PB); SetString(tmp,PA,len); result := tmp; // return internal BLOB content as string end else result := ''; // tftBlobExternal fields e.g. must be directly accessed end; end; {$endif} ................................................................................ result := Int64ToUtf8(FromVarInt64(PB)); // text storage - WinAnsi could use less space than UTF-8 tftWinAnsi, tftUTF8, tftBlobInternal: begin len := FromVarUInt32(PB); if len>0 then if FieldType<>tftWinAnsi then SetString(result,PC,len) else result := WinAnsiConvert.AnsiBufferToRawUTF8(PC,len) else result := ''; end; else result := ''; // tftBlobExternal fields e.g. must be directly accessed end; end; ................................................................................ SetString(result,PAnsiChar(@VD),sizeof(VD)); end; tftWinAnsi: begin VS := Value; {$ifdef UNICODE} ToSBFStr(UnicodeStringToWinAnsi(VS),result); {$else} ToSBFStr(WinAnsiConvert.AnsiToAnsi(CurrentAnsiConvert,VS),result); {$endif} end; tftUTF8: begin VS := Value; ToSBFStr(StringToUTF8(VS),result); end; else ................................................................................ var HostTmp, UserTmp: array[byte] of AnsiChar; HostSize, UserSize: cardinal; i: integer; begin with ExeVersion do if Version=nil then begin ProgramFileName := paramstr(0); ProgramFilePath := ExtractFilePath(ProgramFileName); Version := TFileVersion.Create(ProgramFileName,DefaultVersion); GarbageCollector.Add(Version); ProgramFullSpec := FormatUTF8(EXE_FMT, [ProgramFileName,Version.Detailed,DateToIso8601Text(Version.BuildDateTime)]); ProgramName := StringToUTF8(ExtractFileName(ProgramFileName)); i := length(ProgramName); while i>0 do ................................................................................ finalization SynLogExceptionEnabled := false; GarbageCollector.Free; // free all global instances (avoid memory leak) {$ifdef USESYNTABLEVARIANT} SynVariantTypes.Free; {$endif} end. |
Changes to SynPdf.pas.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 ... 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 .... 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 .... 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 .... 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 .... 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 |
metadata; new header with 8 bit characters; correct outlines and other minor issues: now pass www.pdf-tools.com/pdf/pdfa-online-pruefen.aspx test Version 1.15 - unit now tested with Delphi XE2 (32 Bit) Version 1.16 - fixed issue in TPdfDocument.CreateOrGetImage about guessing if a bitmap is to be reused as a pdf object - added TPdfDocument.ForceNoBitmapReuse property - added a "Decimals: cardinal=6" parameter to TPdfCanvas.ConcatToCTM - TPdfCanvas.SetDash parameter is now an array of integer - set PDF_MAX_FONTSIZE limit to 2000 - should be big enough in practice - fixed an issue when the first time a font was used is as Unicode ................................................................................ JPG, TIF, PNG and GIF image types (prefered way, but need XP or later OS) - if you'd rather use the default jpeg unit (and add some more code to your executable), undefine this conditional } interface uses Windows, SysConst, SysUtils, Classes, {$ifdef ISDELPHIXE2} VCL.Graphics, {$else} Graphics, {$endif} SynCommons, {$ifdef USE_SYNGDIPLUS} SynGdiPlus; // use our GDI+ library for handling TJpegImage and such {$else} jpeg; {$endif} ................................................................................ gdi, old: THandle; aFontName: RawUTF8; i, L: integer; Rec: ^TNameRecord; begin aFontName := FTrueTypeFonts[aFontIndex]; result := TrueTypeFontName(aFontName,AStyle); if IsAnsiCompatible(PAnsiChar(pointer(aFontName))) then exit; // no need to search for the PostScript name field in TTF content gdi := CreateFontIndirectW(ALogFont); if gdi=0 then exit; // if the function failed, the return value is NULL old := SelectObject(FDC,gdi); try name := GetTTFData(FDC,'name',fName); ................................................................................ end; {$endif} procedure TPdfCanvas.ShowText(const text: PDFString; NextLine: boolean); begin if (FContents<>nil) and (text<>'') then if (fDoc.FCharSet=ANSI_CHARSET) or IsAnsiCompatible(PAnsiChar(pointer(text))) then begin if FPage.Font.Unicode and (FPage.FFont.FTrueTypeFontsIndex<>0) then SetPDFFont(TPdfFontTrueType(FPage.Font).WinAnsiFont,FPage.FontSize); FContents.Writer.Add('(').AddEscapeText(pointer(text),FPage.Font).Add(')'). Add(SHOWTEXTCMD[NextLine]) end else begin if FPage.FFont.FTrueTypeFontsIndex<>0 then // write TrueType text after conversion to unicode ................................................................................ begin result := (fUsedWideChar.Count>0); end; function TPdfFontTrueType.GetWideCharWidth(aWideChar: WideChar): Integer; begin self := self.WinAnsiFont; // we need fUsedWide[] to be used glyphs if cardinal(aWideChar)<256 then if WinAnsiTable[cardinal(aWideChar)]<256 then result := cardinal(aWideChar) else result := -1 else // invalid ansi char for this code page (e.g. #128) result := WinAnsiTableSortedFind(cardinal(aWideChar)); if result>=0 then if (fWinAnsiWidth<>nil) and (result>=32) then result := fWinAnsiWidth[AnsiChar(result)] else result := fDefaultWidth else result := fUsedWide[FindOrAddUsedWideChar(aWideChar)].Width; end; ................................................................................ CreateFontPackage := GetProcAddress(FontSub,'CreateFontPackage'); end; if (FontSub<>0) and (@CreateFontPackage<>nil) then begin // subset magic is done by Windows (API available since XP) :) Used.Count := 0; for i := fFirstChar to fLastChar do if AnsiChar(i) in fWinAnsiUsed then Used.Add(WinAnsiTable[i]); with fUsedWideChar do for i := 0 to Count-1 do Used.Add(Values[i]); if CreateFontPackage(pointer(ttf),ttfSize, SubSetData,SubSetMem,SubSetSize, TTFCFP_FLAGS_SUBSET,0,TTFMFP_SUBSET,0, TTFCFP_MS_PLATFORMID,TTFCFP_UNICODE_CHAR_SET, |
> | | | | | < < < < | |
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 ... 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 .... 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 .... 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 .... 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 .... 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 |
metadata; new header with 8 bit characters; correct outlines and other minor issues: now pass www.pdf-tools.com/pdf/pdfa-online-pruefen.aspx test Version 1.15 - unit now tested with Delphi XE2 (32 Bit) Version 1.16 - includes new TSynAnsiConvert classes for handling Ansi charsets - fixed issue in TPdfDocument.CreateOrGetImage about guessing if a bitmap is to be reused as a pdf object - added TPdfDocument.ForceNoBitmapReuse property - added a "Decimals: cardinal=6" parameter to TPdfCanvas.ConcatToCTM - TPdfCanvas.SetDash parameter is now an array of integer - set PDF_MAX_FONTSIZE limit to 2000 - should be big enough in practice - fixed an issue when the first time a font was used is as Unicode ................................................................................ JPG, TIF, PNG and GIF image types (prefered way, but need XP or later OS) - if you'd rather use the default jpeg unit (and add some more code to your executable), undefine this conditional } interface uses Windows, SysConst, SysUtils, Classes, {$ifdef ISDELPHIXE2} VCL.Graphics, {$else} Graphics, {$endif} SynCommons, {$ifdef USE_SYNGDIPLUS} SynGdiPlus; // use our GDI+ library for handling TJpegImage and such {$else} jpeg; {$endif} ................................................................................ gdi, old: THandle; aFontName: RawUTF8; i, L: integer; Rec: ^TNameRecord; begin aFontName := FTrueTypeFonts[aFontIndex]; result := TrueTypeFontName(aFontName,AStyle); if IsAnsiCompatible(aFontName) then exit; // no need to search for the PostScript name field in TTF content gdi := CreateFontIndirectW(ALogFont); if gdi=0 then exit; // if the function failed, the return value is NULL old := SelectObject(FDC,gdi); try name := GetTTFData(FDC,'name',fName); ................................................................................ end; {$endif} procedure TPdfCanvas.ShowText(const text: PDFString; NextLine: boolean); begin if (FContents<>nil) and (text<>'') then if (fDoc.FCharSet=ANSI_CHARSET) or IsAnsiCompatible(text) then begin if FPage.Font.Unicode and (FPage.FFont.FTrueTypeFontsIndex<>0) then SetPDFFont(TPdfFontTrueType(FPage.Font).WinAnsiFont,FPage.FontSize); FContents.Writer.Add('(').AddEscapeText(pointer(text),FPage.Font).Add(')'). Add(SHOWTEXTCMD[NextLine]) end else begin if FPage.FFont.FTrueTypeFontsIndex<>0 then // write TrueType text after conversion to unicode ................................................................................ begin result := (fUsedWideChar.Count>0); end; function TPdfFontTrueType.GetWideCharWidth(aWideChar: WideChar): Integer; begin self := self.WinAnsiFont; // we need fUsedWide[] to be used glyphs result := WideCharToWinAnsi(ord(aWideChar)); if result>=0 then if (fWinAnsiWidth<>nil) and (result>=32) then result := fWinAnsiWidth[AnsiChar(result)] else result := fDefaultWidth else result := fUsedWide[FindOrAddUsedWideChar(aWideChar)].Width; end; ................................................................................ CreateFontPackage := GetProcAddress(FontSub,'CreateFontPackage'); end; if (FontSub<>0) and (@CreateFontPackage<>nil) then begin // subset magic is done by Windows (API available since XP) :) Used.Count := 0; for i := fFirstChar to fLastChar do if AnsiChar(i) in fWinAnsiUsed then Used.Add(WinAnsiConvert.AnsiToWide[i]); with fUsedWideChar do for i := 0 to Count-1 do Used.Add(Values[i]); if CreateFontPackage(pointer(ttf),ttfSize, SubSetData,SubSetMem,SubSetSize, TTFCFP_FLAGS_SUBSET,0,TTFMFP_SUBSET,0, TTFCFP_MS_PLATFORMID,TTFCFP_UNICODE_CHAR_SET, |