1 {
2 Copyright (C) Alexey Torgashin, uvviewsoft.com
3 License: MPL 2.0 or LGPL
4 }
5 unit ATSynEdit_CharSizer;
6 
7 {$mode objfpc}{$H+}
8 
9 interface
10 
11 uses
12   {$ifdef windows}
13   Windows,
14   {$endif}
15   Classes, SysUtils, Graphics, Controls,
16   Forms,
17   ExtCtrls,
18   Math,
19   LCLType, LCLIntf;
20 
21 type
22   { TATCharSizer }
23 
24   TATCharSizer = class
25   private
26     const SaveScale=2; //max stored char width (in %) is 255*SaveScale
27   private
28     FontName: string;
29     FontSize: integer;
30     FontReset: boolean;
31     SizeAvg: integer;
32     FPanel: TPanel;
33     FOwner: TComponent;
34     Sizes: packed array[word] of byte; //width of WideChars, divided by SizeAvg, divided by SaveScale
35     procedure InitPanel;
GetCharWidth_FromCachenull36     function GetCharWidth_FromCache(ch: WideChar): integer;
37   public
38     constructor Create(AOwner: TComponent);
39     destructor Destroy; override;
40     procedure Init(const AFontName: string; AFontSize: integer);
GetCharWidthnull41     function GetCharWidth(ch: WideChar): integer;
GetStrWidthnull42     //function GetStrWidth(const S: WideString): integer;
43   end;
44 
45 var
46   //must be created after MainForm is inited, e.g. in TATSynEdit.Create
47   GlobalCharSizer: TATCharSizer = nil;
48 
IsCharAsciiControlnull49 function IsCharAsciiControl(ch: WideChar): boolean; inline;
IsCharAccentnull50 function IsCharAccent(ch: WideChar): boolean; inline;
IsCharUnicodeSpacenull51 function IsCharUnicodeSpace(ch: WideChar): boolean; inline;
IsCharHexDisplayednull52 function IsCharHexDisplayed(ch: WideChar): boolean; inline;
IsCharUnusualWidthnull53 function IsCharUnusualWidth(ch: WideChar): boolean; inline;
IsStringWithUnusualWidthCharsnull54 function IsStringWithUnusualWidthChars(const S: UnicodeString): boolean;
55 
56 
57 implementation
58 
59 uses
60   ATSynEdit_Options,
61   ATSynEdit_CharSizeArray;
62 
IsCharAsciiControlnull63 function IsCharAsciiControl(ch: WideChar): boolean; inline;
64 begin
65   Result:= (ch<>#9) and (Ord(ch)<$20);
66 end;
67 
IsCharHexDisplayednull68 function IsCharHexDisplayed(ch: WideChar): boolean;
69 begin
70   Result:= FixedSizes[Ord(ch)]=uw_hexshow;
71 end;
72 
IsCharAccentnull73 function IsCharAccent(ch: WideChar): boolean;
74 begin
75   Result:= FixedSizes[Ord(ch)]=uw_combined;
76 end;
77 
IsCharUnicodeSpacenull78 function IsCharUnicodeSpace(ch: WideChar): boolean;
79 begin
80   Result:= FixedSizes[Ord(ch)]=uw_space;
81 end;
82 
IsCharUnusualWidthnull83 function IsCharUnusualWidth(ch: WideChar): boolean;
84 begin
85   //tab-char must have unusual width (to render it with Dx offset)
86   if Ord(ch)<32 then
87     exit(true);
88 
89   case FixedSizes[Ord(ch)] of
90     uw_normal, uw_space:
91       Result:= false
92     else
93       Result:= true;
94   end;
95 end;
96 
IsStringWithUnusualWidthCharsnull97 function IsStringWithUnusualWidthChars(const S: UnicodeString): boolean;
98 var
99   i: integer;
100 begin
101   for i:= 1 to Length(S) do
102     if IsCharUnusualWidth(S[i]) then
103       exit(true);
104   Result:= false;
105 end;
106 
107 {$ifdef windows}
_WidestrWidthnull108 function _WidestrWidth(C: TCanvas; S: WideChar): integer; inline;
109 var
110   Size: TSize;
111 begin
112   Windows.GetTextExtentPointW(C.Handle, @S, 1{Len}, Size);
113   Result:= Size.cx;
114 end;
115 {$else}
_WidestrWidthnull116 function _WidestrWidth(C: TCanvas; S: WideChar): integer; inline;
117 begin
118   Result:= C.TextWidth(WideString(S));
119   //debug
120   //Write('#'+IntToHex(Ord(S),2)+'"'+S+'" ');
121 end;
122 {$endif}
123 
124 { TATCharSizer }
125 
126 procedure TATCharSizer.Init(const AFontName: string; AFontSize: integer);
127 begin
128   if (FontName<>AFontName) or (FontSize<>AFontSize) then
129   begin
130     FontReset:= true;
131     FontName:= AFontName;
132     FontSize:= AFontSize;
133     FillChar(Sizes, SizeOf(Sizes), 0);
134   end;
135   SizeAvg:= 0;
136 end;
137 
TATCharSizer.GetCharWidth_FromCachenull138 function TATCharSizer.GetCharWidth_FromCache(ch: WideChar): integer;
139 begin
140   Result:= Sizes[Ord(ch)] * SaveScale;
141   if Result=0 then
142   begin
143     InitPanel;
144 
145     if SizeAvg=0 then
146       SizeAvg:= _WidestrWidth(FPanel.Canvas, 'N');
147 
148     Result:= _WidestrWidth(FPanel.Canvas, ch) * 100 div SizeAvg;
149     Sizes[Ord(ch)]:= Math.Min(255, Result div SaveScale);
150   end;
151 end;
152 
153 constructor TATCharSizer.Create(AOwner: TComponent);
154 begin
155   FOwner:= AOwner;
156 end;
157 
158 procedure TATCharSizer.InitPanel;
159 begin
160   if FPanel=nil then
161   begin
162     FPanel:= TPanel.Create(nil);
163     FPanel.Name:= 'AppSizerPanel';
164     FPanel.Visible:= false;
165     FPanel.SetBounds(0, 0, 50, 20);
166     if IsLibrary then
167       FPanel.Parent:= FOwner as TWinControl
168     else
169       FPanel.Parent:= Application.MainForm;
170   end;
171 
172   if FontReset then
173   begin
174     FontReset:= false;
175     FPanel.Canvas.Font.Name:= FontName;
176     FPanel.Canvas.Font.Size:= FontSize;
177     FPanel.Canvas.Font.Style:= [];
178   end;
179 end;
180 
181 destructor TATCharSizer.Destroy;
182 begin
183   if Assigned(FPanel) then
184     FreeAndNil(FPanel);
185   inherited;
186 end;
187 
GetCharWidthnull188 function TATCharSizer.GetCharWidth(ch: WideChar): integer;
189 const
190   CharScaleHex_Small = 300; //width of 'xNN'
191   CharScaleHex_Big = 500; //width of 'xNNNN'
192 var
193   n: word absolute ch;
194 begin
195   Result:= 100;
196 
197   case FixedSizes[n] of
198     uw_normal: exit;
199     uw_fullwidth: exit(ATEditorOptions.CharScaleFullWidth);
200     uw_space: exit;
201     uw_combined: exit(0);
202     uw_hexshow:
203       begin
204         if n<$100 then
205           exit(CharScaleHex_Small)
206         else
207           exit(CharScaleHex_Big);
208       end;
209   end;
210 
211   if ATEditorOptions.UnprintedReplaceSpec and IsCharAsciiControl(ch) then
212     exit;
213 
214   if IsCharHexDisplayed(ch) then
215   begin
216     if n<$100 then
217       exit(CharScaleHex_Small)
218     else
219       exit(CharScaleHex_Big);
220   end;
221 
222   if ATEditorOptions.CharSizeProportional then
223     if n>=128 then
224       exit(GetCharWidth_FromCache(ch));
225 
226   //for other codes, use full-width size
227   Result:= ATEditorOptions.CharScaleFullWidth;
228 end;
229 
230 {
GetStrWidthnull231 function TATCharSizer.GetStrWidth(const S: WideString): integer;
232 begin
233   Result:= _WidestrWidth(FPanel.Canvas, S) * 100 div SizeAvg;
234 end;
235 }
236 
237 finalization
238 
239   if Assigned(GlobalCharSizer) then
240     FreeAndNil(GlobalCharSizer);
241 
242 end.
243 
244