unit mainunit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Graphics, Dialogs, StdCtrls, Grids, IniFiles, LCLType, LCLIntf, LazUTF8; type { TfrmMain } TfrmMain = class(TForm) btnResetText: TButton; btnApplyFilter: TButton; btnFontDlg: TButton; cbCharset: TComboBox; cbPitch: TComboBox; chkStrike: TCheckBox; chkUnderLine: TCheckBox; FontDialog1: TFontDialog; lflFontFaceList: TLabel; lblStyles: TLabel; lblCharset: TLabel; lblFilter: TLabel; lblSizes: TLabel; lbFamily: TListBox; lbStyles: TListBox; lbSizes: TListBox; lbCharset: TListBox; grid: TStringGrid; procedure btnFontDlgClick(Sender: TObject); procedure btnResetTextClick(Sender: TObject); procedure btnApplyFilterClick(Sender: TObject); procedure chkStrikeChange(Sender: TObject); procedure chkUnderLineChange(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure lbFamilyClick(Sender: TObject); procedure lbCharsetClick(Sender: TObject); procedure lbSizesClick(Sender: TObject); procedure lbStylesClick(Sender: TObject); private FTime: LongWord; FIniTime: LongWord; FCurrentFamily,FCurrentStyle,FCurrentSize,FCurrentCharset: string; procedure StartTimer; Procedure EndTimer; function GetCharSet: byte; function GetPitch: integer; procedure EnableEvents(Ok: boolean; Lb: TListbox = nil); procedure SelectFont; procedure ResetSampleText; procedure SaveSelection; procedure RestoreSelection(Sender: TListbox); procedure LoadFontList; procedure LoadFamilyFonts(Charset: integer); procedure UpdateFont(F: TFont); public end; var frmMain: TfrmMain; implementation {.$define Debug} {$R *.lfm} { TfrmMain } var LStyles, LSizes: TStringList; function EnumFontsNoDups( var LogFont: TEnumLogFontEx; var Metric: TNewTextMetricEx; FontType: Longint; Data: LParam):LongInt; stdcall; var L: TStringList; S: String; begin L := TStringList(ptrint(Data)); S := LogFont.elfLogFont.lfFaceName; if L.IndexOf(S)<0 then L.Add(S); result := 1; end; var NeedTTF: boolean; function EnumFamilyFonts( var eLogFont: TEnumLogFontEx; var Metric:TNewTextMetricEx; FontType:longint; Data:LParam):longint; stdcall; var s: string; n: integer; lcharsets: TStringList; begin LCharSets := TStringList(ptrint(Data)); if Lcharsets<>nil then begin // collect charsets // when collecting charsets no need to collect all other info s :=CharSetToString(eLogFont.elfLogFont.lfCharSet); if LCharsets.indexOf(s)<0 then LCharsets.AddObject(s, TObject(ptrint(eLogFont.elfLogFont.lfCharSet))); exit; end; // collect styles s :=eLogFont.elfStyle; if LStyles.IndexOf(s)<0 then begin // encode bold (bit 0), italic (bit 1) -- see SelectFont n := 0; {$IF DEFINED(LCLWin32) or DEFINED(LCLCocoa) } if (eLogFont.elfLogFont.lfItalic <> 0) then n := n or 1; if (eLogFont.elfLogFont.lfWeight > FW_MEDIUM) then n := n or 2; {$ENDIF} {$IF DEFINED(LCLGtk2) or DEFINED(LCLGtk3) or DEFINED(LCLQt) or DEFINED(LCLQt5)} s := Lowercase(s); if (pos('italic', s) <> 0) or (pos('oblique', s) <> 0) then n := n or 1; if (pos('bold', s) <> 0) then n := n or 2; {$ENDIF} LStyles.AddObject(eLogFont.elfStyle, TObject(ptrint(n))); end; // collect sizes if FontType=TRUETYPE_FONTTYPE then NeedTTF := True else with metric.ntmentm do if tmDigitizedAspectY <> 0 then begin n := (tmHeight-tmInternalLeading)*72+tmDigitizedAspectY shr 1; n := n div tmDigitizedAspectY; if n>0 then begin s := IntToStr(n)+'*'; // font sizes with * indicate raster fonts if LSizes.IndexOf(s)<0 then LSizes.AddObject(s, TObject(ptrint(n))); end; end; result := 1; end; procedure TfrmMain.btnFontDlgClick(Sender: TObject); begin if FontDialog1.Execute then UpdateFont(FontDialog1.Font); end; procedure TfrmMain.btnResetTextClick(Sender: TObject); begin ResetSampleText; end; procedure TfrmMain.btnApplyFilterClick(Sender: TObject); begin LoadFontList; end; procedure TfrmMain.chkStrikeChange(Sender: TObject); begin SelectFont; end; procedure TfrmMain.chkUnderLineChange(Sender: TObject); begin SelectFont; end; procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: boolean); var Ini: TInifile; begin SaveSelection; Ini := TIniFile.Create(UTF8ToSys(ChangeFileExt(Application.ExeName,'.ini'))); try Ini.WriteString('General','CurrentFamily', FCurrentFamily); Ini.WriteString('General','CurrentCharset',FCurrentCharset); Ini.WriteString('General','CurrentStyle', FCurrentStyle); Ini.WriteString('General','CurrentSize', FCurrentSize); finally Ini.Free; end; end; procedure TfrmMain.FormCreate(Sender: TObject); procedure Add(Charset: Integer); begin cbCharset.Items.AddObject(CharSetToString(CharSet), TObject(ptrint(Charset))); end; var Ini: TIniFile; begin // populate cbcharset cbCharset.Items.clear; Add(ANSI_CHARSET); Add(DEFAULT_CHARSET); Add(SYMBOL_CHARSET); Add(MAC_CHARSET); Add(SHIFTJIS_CHARSET); Add(HANGEUL_CHARSET); Add(JOHAB_CHARSET); Add(GB2312_CHARSET); Add(CHINESEBIG5_CHARSET); Add(GREEK_CHARSET); Add(TURKISH_CHARSET); Add(VIETNAMESE_CHARSET); Add(HEBREW_CHARSET); Add(ARABIC_CHARSET); Add(BALTIC_CHARSET); Add(RUSSIAN_CHARSET); Add(THAI_CHARSET); Add(EASTEUROPE_CHARSET); Add(OEM_CHARSET); Add(FCS_ISO_10646_1); Add(FCS_ISO_8859_1); Add(FCS_ISO_8859_2); Add(FCS_ISO_8859_3); Add(FCS_ISO_8859_4); Add(FCS_ISO_8859_5); Add(FCS_ISO_8859_6); Add(FCS_ISO_8859_7); Add(FCS_ISO_8859_8); Add(FCS_ISO_8859_9); Add(FCS_ISO_8859_10); Add(FCS_ISO_8859_15); ResetSampleText; Ini := TIniFile.Create(UTF8ToSys(ChangeFileExt(Application.ExeName,'.ini'))); try FCurrentFamily := Ini.ReadString('General','CurrentFamily', ''); FCurrentCharset := Ini.ReadString('General','CurrentCharset',''); FCurrentStyle := Ini.ReadString('General','CurrentStyle', ''); FCurrentSize := Ini.ReadString('General','CurrentSize', ''); finally Ini.Free; end; end; procedure TfrmMain.FormShow(Sender: TObject); begin LoadFontlist; lbCharsetClick(nil); SelectFont; end; procedure TfrmMain.lbFamilyClick(Sender: TObject); begin LoadFamilyFonts(-1); lbCharsetClick(nil); SelectFont; end; procedure TfrmMain.lbCharsetClick(Sender: TObject); var i: Integer; begin i := lbCharset.ItemIndex; if i<0 then exit; i := ptrint(lbCharSet.Items.Objects[i]); LoadFamilyFonts(byte(i)); end; procedure TfrmMain.lbSizesClick(Sender: TObject); begin SelectFont; end; procedure TfrmMain.lbStylesClick(Sender: TObject); begin SelectFont; end; procedure TfrmMain.StartTimer; begin FIniTime := GetTickCount; end; procedure TfrmMain.EndTimer; begin FTime := GetTickCount-FIniTime; end; function TfrmMain.GetCharSet: Byte; begin if cbCharSet.Itemindex<0 then result := ANSI_CHARSET else result := byte(ptrint(cbCharset.items.Objects[CbCharset.ItemIndex])); end; function TfrmMain.GetPitch: integer; begin case cbPitch.ItemIndex of 1: result := FIXED_PITCH; 2: result := VARIABLE_PITCH; 3: result := MONO_FONT; else result := DEFAULT_PITCH; end; btnApplyFilter.Caption := IntToStr(result); end; procedure TfrmMain.EnableEvents(Ok: boolean; Lb: TListbox = nil); procedure SetEvent(L: TListbox); var Event: TNotifyEvent; begin Event := nil; if ok then begin if l=lbFamily then Event := @lbFamilyClick else if l=lbStyles then Event := @LbStylesClick else if l=lbCharset then Event := @lbCharsetClick else if l=lbSizes then Event := @lbSizesClick; end; L.OnClick := Event; end; begin if Lb<>nil then SetEvent(Lb) else begin SetEvent(lbFamily); SetEvent(lbStyles); SetEvent(lbCharset); SetEvent(lbSizes); end; end; procedure TfrmMain.SelectFont; var F: TFont; i: integer; function GetFontSize(s: string): Integer; begin i := pos('*',s); if i<>0 then result := StrToInt(Copy(S, 1, i-1)) else result := StrToInt(s); end; begin if lbFamily.ItemIndex>=0 then if lbCharSet.ItemIndex>=0 then if lbStyles.ItemIndex>=0 then if lbSizes.ItemIndex>=0 then begin F := TFont.Create; try F.Name := lbFamily.Items[lbFamily.ItemIndex]; F.CharSet := TFontCharSet(ptrint(lbCharSet.Items.Objects[lbCharset.ItemIndex])); F.Size := GetFontSize(lbSizes.Items[lbSizes.ItemIndex]); i := ptrint(lbStyles.Items.Objects[lbStyles.ItemIndex]); F.Style := []; if i and 1 <> 0 then F.Style := F.Style + [fsItalic]; if i and 2 <> 0 then F.Style := F.Style + [fsBold]; if chkUnderLine.Checked then F.Style := F.Style + [fsUnderline]; if chkStrike.Checked then F.Style := F.Style + [fsStrikeOut]; UpdateFont(F); SaveSelection; finally F.Free; end; end; end; procedure TfrmMain.ResetSampleText; var L: TStringList; begin L := TStringList.Create; L.Add('abcdefghijklmnopqrstuvwxyz'); L.Add('ABCDEFGHIJKLMNOPQRSTUVWXYZ'); L.Add('01234567891 ўЈ¤Ґ§'); L.Add('абвгдежзийклмнопрстуфхцшщъыь'); L.add('АБВГДЕЖЗИЙКЛМНОПРСТУФХХШЩЪЫЬЭЯ'); grid.Cols[0] := L; l.Free; end; procedure TfrmMain.SaveSelection; function doGet(lb: TListbox): string; begin if lb.itemindex>=0 then result := lb.Items[lb.ItemIndex] else result := ''; end; begin FCurrentFamily := doGet(LbFamily); FCurrentCharset := doGet(LbCharset); FCurrentStyle := doGet(LbStyles); FCurrentSize := doGet(LbSizes); end; procedure TfrmMain.RestoreSelection(Sender: TListbox); function GetSelection: string; begin if Sender.itemindex>=0 then result := Sender.Items[Sender.ItemIndex] else result := ''; end; function GetCurrent: string; begin if Sender=lbFamily then result := FCurrentFamily else if Sender=lbCharset then result := FCurrentCharset else if Sender=lbStyles then result := FCurrentStyle else if Sender=lbSizes then result := FCurrentSize; end; var i: Integer; s: string; begin s := GetCurrent; if GetSelection <> s then begin i := Sender.Items.IndexOf(s); if i>-1 then begin {$ifdef debug} debugln('RestoreSelection: listbox=',Sender.Name,' Old=',GetSelection,' New=',S); {$endif} if i<>Sender.ItemIndex then Sender.ItemIndex := i; end; end; end; procedure TfrmMain.LoadFontList; var DC: HDC; lf: TLogFont; L: TStringList; i: Integer; begin // this could be have done also with screen.fonts // but here, we have the list filtered by Charset lf.lfCharSet := GetCharSet; lf.lfFaceName := ''; case cbPitch.ItemIndex of 1: i:=FIXED_PITCH; 2: i:=VARIABLE_PITCH; 3: i:=MONO_FONT; else i:=DEFAULT_PITCH; end; lf.lfPitchAndFamily := i; {$ifdef debug} debugln('LoadFontList: for charset=',CharSetToString(lf.lfcharset)); {$endif} L := TStringList.create; lbStyles.Clear; lbCharset.Clear; lbSizes.Clear; DC := GetDC(0); EnableEvents(False, lbFamily); try StartTimer; EnumFontFamiliesEX(DC, @lf, @EnumFontsNoDups, ptrint(L), 0); EndTimer; L.Sort; lbFamily.Items.Assign(L); lbFamily.Itemindex := -1; RestoreSelection(lbFamily); if lbFamily.ItemIndex<0 then begin if lbFamily.Items.Count>0 then lbFamily.ItemIndex := 0; end; LoadFamilyFonts(-1); lflFontFaceList.Caption := format('Fontfaces, found %d, %d ms',[lbFamily.Items.Count, FTime]); finally EnableEvents(True, lbFamily); ReleaseDC(0, DC); L.Free; end; end; function CompareSizes(List: TStringList; Index1, Index2: Integer): Integer; begin result := ptrint(List.Objects[Index1]) - ptrint(List.Objects[Index2]); end; procedure TfrmMain.LoadFamilyFonts(Charset: integer); var LCharset: TStringList; dc: HDC; Lf: TLogFont; i: LongInt; LoadingCharsets: boolean; procedure AddScalableSizes; procedure Add(Sz: Integer); begin if LSizes.IndexOfObject(TObject(ptrint(Sz)))<0 then LSizes.AddObject(IntToStr(Sz), TObject(ptrint(Sz))); end; begin add(8); add(9); add(10); add(11); add(12); add(14); add(16); add(18); add(20); add(22); add(24); add(26); add(28); add(36); add(48); add(72); end; begin i := lbFamily.ItemIndex; if i<0 then exit; LoadingCharsets := Charset<0; {$ifdef debug} Write('LoadFamilyFonts: for family=', lbFamily.Items[i],' and Charset='); if LoadingCharsets then debugln('ALL_CHARSETS') else debugln(CharsetToString(byte(Charset))); {$endif} // at the moment only global fonts are enumerated // ie. fonts selected in a device context are not enumerated DC := GetDC(0); // create global variables, EnumFamilyFonts use them if LoadingCharsets then begin // need to fill charset listbox too LCharset := TStringList.Create; CharSet := DEFAULT_CHARSET; end else begin // charset listbox is already filled, so fill styles and sizes LCharSet := nil; LStyles := TStringList.Create; LSizes := TStringList.Create; end; try // enumerate fonts Lf.lfFaceName := lbFamily.Items[i]; Lf.lfCharSet := byte(Charset); Lf.lfPitchAndFamily := 0; NeedTTF := False; EnumFontFamiliesEX(DC, @Lf, @EnumFamilyFonts, ptrint(LCharset), 0); // fill charset listbox if necessary if LCharset<>nil then begin LCharset.Sort; EnableEvents(False, LbCharset); LbCharset.Items.Assign(LCharset); LbCharset.ItemIndex := -1; EnableEvents(true, LbCharset); end else begin // fill styles listbox LStyles.Sort; EnableEvents(False, LbStyles); LbStyles.Items.Assign(LStyles); lbStyles.ItemIndex := -1; EnableEvents(true, LbStyles); RestoreSelection(lbStyles); if lbStyles.ItemIndex<0 then begin if LbStyles.Items.Count>0 then LbStyles.ItemIndex := 0; end; // fill sizes listbox // any raster font size is already there if NeedTTF then AddScalableSizes; LSizes.CustomSort(@CompareSizes); EnableEvents(False, lbSizes); lbSizes.Items.Assign(LSizes); lbSizes.ItemIndex := -1; EnableEvents(true, LbSizes); RestoreSelection(LbSizes); if lbSizes.ItemIndex<0 then begin if lbSizes.Items.Count>0 then LbSizes.ItemIndex := 0; end; end; finally if LCharset=nil then begin LSizes.Free; LStyles.Free; end else LCharset.Free; releaseDC(0, DC); end; if LoadingCharsets then begin // make an initial charset selection RestoreSelection(lbCharset); if lbCharset.ItemIndex<0 then begin if lbCharset.Items.Count>0 then lbCharset.ItemIndex := 0; end; end; end; procedure TfrmMain.UpdateFont(F: TFont); begin grid.Font := F; grid.DefaultRowHeight := grid.canvas.textHeight('Бj') + 5; end; end.