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