{%MainUnit ../graphics.pp} {****************************************************************************** TFONT ****************************************************************************** ***************************************************************************** This file is part of the Lazarus Component Library (LCL) See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** } { TFontHandleCache } type TLogFontAndName = record LogFont: TLogFont; LongFontName: string; end; PLogFontAndName = ^TLogFontAndName; function CompareLogFontAndNameWithResDesc(Key: PLogFontAndName; Desc: TFontHandleCacheDescriptor): integer; begin Result := CompareStr(Key^.LongFontName, Desc.LongFontName); //debugln('CompareLogFontAndNameWithResDesc A ',Key^.LongFontName,' ',Desc.LongFontName,' ',DbgS(Desc),' Result=',Result); if Result = 0 then Result := CompareMemRange(@Key^.LogFont, @Desc.LogFont, SizeOf(Desc.LogFont) - LF_FACESIZE * SizeOf(Char)); if Result = 0 then Result := StrLComp(@Key^.LogFont.lfFaceName[0], @Desc.LogFont.lfFaceName[0], LF_FACESIZE); //debugln('CompareLogFontAndNameWithResDesc END Result=',Result); end; procedure TFontHandleCache.RemoveItem(Item: TResourceCacheItem); begin DeleteObject(HGDIOBJ(Item.Handle)); inherited RemoveItem(Item); end; constructor TFontHandleCache.Create; begin inherited Create; FResourceCacheDescriptorClass := TFontHandleCacheDescriptor; end; function TFontHandleCache.CompareDescriptors(Tree: TAvlTree; Desc1, Desc2: Pointer): integer; var Descriptor1: TFontHandleCacheDescriptor absolute Desc1; Descriptor2: TFontHandleCacheDescriptor absolute Desc2; begin Result := CompareStr(Descriptor1.LongFontName, Descriptor2.LongFontName); if Result = 0 then Result := CompareMemRange(@Descriptor1.LogFont, @Descriptor2.LogFont, SizeOf(Descriptor1.LogFont) - LF_FACESIZE * SizeOf(Char)); if Result = 0 then Result := StrLComp(@Descriptor1.LogFont.lfFaceName[0], @Descriptor2.LogFont.lfFaceName[0], LF_FACESIZE); end; function TFontHandleCache.FindFont(TheFont: TLCLHandle): TResourceCacheItem; var ANode: TAvlTreeNode; begin ANode := FItems.FindKey(@TheFont, TListSortCompare(@ComparePHandleWithResourceCacheItem)); if ANode <> nil then Result := TResourceCacheItem(ANode.Data) else Result := nil; end; function TFontHandleCache.FindFontDesc(const LogFont: TLogFont; const LongFontName: string): TFontHandleCacheDescriptor; var LogFontAndName: TLogFontAndName; ANode: TAvlTreeNode; begin LogFontAndName.LogFont := LogFont; LogFontAndName.LongFontName := LongFontName; ANode := FDescriptors.Findkey(@LogFontAndName, TListSortCompare(@CompareLogFontAndNameWithResDesc)); if ANode <> nil then Result := TFontHandleCacheDescriptor(ANode.Data) else Result := nil; end; function TFontHandleCache.Add(TheFont: TLCLHandle; const LogFont: TLogFont; const LongFontName: string): TFontHandleCacheDescriptor; var Item: TResourceCacheItem; begin if FindFontDesc(LogFont, LongFontName) <> nil then RaiseGDBException('TFontHandleCache.Add font desc added twice'); // find cache item with TheFont Item := FindFont(TheFont); if Item = nil then begin // create new item Item := TResourceCacheItem.Create(Self, TheFont); FItems.Add(Item); end; // create descriptor Result := TFontHandleCacheDescriptor.Create(Self, Item); Result.LongFontName := LongFontName; Result.LogFont := LogFont; FDescriptors.Add(Result); if FindFontDesc(LogFont, LongFontName) = nil then begin DebugLn('TFontHandleCache.Add Added: %p LongFontName=%s', [Pointer(Result), Result.LongFontName]); RaiseGDBException(''); end; end; { TFont } procedure GetCharsetValues(Proc: TGetStrProc); var I: Integer; begin for I := Low(FontCharsets) to High(FontCharsets) do Proc(FontCharsets[I].Name); end; function CharsetToIdent(Charset: Longint; out Ident: string): Boolean; begin Result := IntToIdent(Charset, Ident, FontCharsets); end; function IdentToCharset(const Ident: string; out Charset: Longint): Boolean; begin Result := IdentToInt(Ident, CharSet, FontCharsets); end; function GetFontData(Font: HFont): TFontData; var ALogFont: TLogFont; begin Result := DefFontData; if Font <> 0 then begin if GetObject(Font, SizeOf(ALogFont), @ALogFont) <> 0 then with Result, ALogFont do begin Height := lfHeight; if lfWeight >= FW_BOLD then Include(Style, fsBold); if lfItalic > 0 then Include(Style, fsItalic); if lfUnderline > 0 then Include(Style, fsUnderline); if lfStrikeOut > 0 then Include(Style, fsStrikeOut); Charset := TFontCharset(lfCharSet); Name := lfFaceName; case lfPitchAndFamily and $F of VARIABLE_PITCH: Pitch := fpVariable; FIXED_PITCH: Pitch := fpFixed; else Pitch := fpDefault; end; Orientation := lfOrientation; Handle := Font; end; end; end; function GetDefFontCharSet: TFontCharSet; begin Result := DEFAULT_CHARSET; end; {------------------------------------------------------------------------------ function: FindXLFDItem Params: const XLFDName: string; Index: integer; var ItemStart, ItemEnd: integer Returns: boolean Searches the XLFD item on position Index. Index starts from 0. Returns true on sucess. ItemStart will be on the first character and ItemEnd after the last character. ------------------------------------------------------------------------------} function FindXLFDItem(const XLFDName: string; Index: integer; var ItemStart, ItemEnd: integer): boolean; begin if Index<0 then begin Result := False; exit; end; ItemStart := 1; ItemEnd := ItemStart; while true do begin if (ItemEnd>length(XLFDName)) then begin dec(Index); break; end; if XLFDName[ItemEnd] = '-' then begin dec(Index); if Index < 0 then break; ItemStart := ItemEnd + 1; end; inc(ItemEnd); end; Result := (Index = -1); end; {------------------------------------------------------------------------------ function: ExtractXLFDItem Params: const XLFDName: string; Index: integer Returns: string Parses a font name in XLFD format and extracts one item. (see http://wwwinfo.cern.ch/umtf/working-groups/X11/fonts/hp_xlfd.html) An XLFD name is FontNameRegistry-Foundry-FamilyName-WeightName-Slant-SetwidthName -AddStyleName-PixelSize-PointSize-ResolutionX-ResolutionY-Spacing -AverageWidth-CharSetRegistry-CharSetCoding ------------------------------------------------------------------------------} function ExtractXLFDItem(const XLFDName: string; Index: integer): string; var StartPos, EndPos: integer; begin if FindXLFDItem(XLFDName, Index, StartPos, EndPos) then Result := copy(XLFDName, StartPos, EndPos - StartPos) else Result := ''; end; {------------------------------------------------------------------------------ function: ExtractFamilyFromXLFDName Params: const XLFDName: string Returns: string Parses a font name in XLFD format and extracts the FamilyName. (see http://wwwinfo.cern.ch/umtf/working-groups/X11/fonts/hp_xlfd.html) An XLFD name is FontNameRegistry-Foundry-FamilyName-WeightName-Slant-SetwidthName -AddStyleName-PixelSize-PointSize-ResolutionX-ResolutionY-Spacing -AverageWidth-CharSetRegistry-CharSetCoding ------------------------------------------------------------------------------} function ExtractFamilyFromXLFDName(const XLFDName: string): string; var StartPos, EndPos: integer; begin if FindXLFDItem(XLFDName, 2, StartPos, EndPos) then Result:=copy(XLFDName, StartPos, EndPos - StartPos) else Result := ''; end; {------------------------------------------------------------------------------ Method: XLFDNameToLogFont Params: const XLFDName: string Returns: TLogFont Parses a font name in XLFD format and creates a TLogFont record from it. (see http://wwwinfo.cern.ch/umtf/working-groups/X11/fonts/hp_xlfd.html) An XLFD name is FontNameRegistry-Foundry-FamilyName-WeightName-Slant-SetwidthName -AddStyleName-PixelSize-PointSize-ResolutionX-ResolutionY-Spacing -AverageWidth-CharSetRegistry-CharSetCoding ------------------------------------------------------------------------------} function XLFDNameToLogFont(const XLFDName: string): TLogFont; type TWeightMapEntry = record Name: string; Weight: integer; end; const WeightMap: array[1..15] of TWeightMapEntry = ( (Name: 'DONTCARE'; Weight: FW_DONTCARE), (Name: 'THIN'; Weight: FW_THIN), (Name: 'EXTRALIGHT'; Weight: FW_EXTRALIGHT), (Name: 'LIGHT'; Weight: FW_LIGHT), (Name: 'NORMAL'; Weight: FW_NORMAL), (Name: 'MEDIUM'; Weight: FW_MEDIUM), (Name: 'SEMIBOLD'; Weight: FW_SEMIBOLD), (Name: 'BOLD'; Weight: FW_BOLD), (Name: 'EXTRABOLD'; Weight: FW_EXTRABOLD), (Name: 'HEAVY'; Weight: FW_HEAVY), (Name: 'ULTRALIGHT'; Weight: FW_ULTRALIGHT), (Name: 'REGULAR'; Weight: FW_REGULAR), (Name: 'DEMIBOLD'; Weight: FW_DEMIBOLD), (Name: 'ULTRABOLD'; Weight: FW_ULTRABOLD), (Name: 'BLACK'; Weight: FW_BLACK) ); var ItemStart, ItemEnd: integer; Item: string; procedure GetNextItem; begin ItemStart:=ItemEnd+1; ItemEnd:=ItemStart; while (ItemEnd<=length(XLFDName)) and (XLFDName[ItemEnd]<>'-') do inc(ItemEnd); Item:=copy(XLFDName,ItemStart,ItemEnd-ItemStart); end; function WeightNameToWeightID(const WeightName: string): integer; var i: integer; begin for i:=Low(WeightMap) to High(WeightMap) do begin if AnsiCompareText(WeightMap[i].Name,WeightName)=0 then begin Result:=WeightMap[i].Weight; exit; end; end; Result:=FW_DONTCARE; end; var l, FaceNameMax, PixelSize, PointSize, Resolution, AverageWidth: integer; begin FillChar(Result,SizeOf(TLogFont),0); ItemEnd:=0; GetNextItem; // 1. read FontNameRegistry // ToDo GetNextItem; // 2. read Foundry // ToDo GetNextItem; // 3. read FamilyName l:=length(Item); FaceNameMax:=High(Result.lfFaceName)-Low(Result.lfFaceName); // max without #0 if l>FaceNameMax then l:=FaceNameMax; if l>0 then Move(Item[1],Result.lfFaceName[Low(Result.lfFaceName)],l); Result.lfFaceName[Low(Result.lfFaceName)+l]:=#0; GetNextItem; // 4. read WeightName Result.lfWeight:=WeightNameToWeightID(Item); GetNextItem; // 5. read Slant if (AnsiCompareText(Item,'I')=0) or (AnsiCompareText(Item,'RI')=0) or (AnsiCompareText(Item,'O')=0) then // I = italic, RI = reverse italic, O = oblique Result.lfItalic:=1 else Result.lfItalic:=0; GetNextItem; // 6. read SetwidthName // ToDO: NORMAL, CONDENSED, NARROW, WIDE, EXPANDED GetNextItem; // 7. read AddStyleName // calculate Style name extensions (=rotation) // API XLFD // --------------------- -------------- // Orientation 1/10 deg 1/64 deg Result.lfOrientation:=(StrToIntDef(Item,0)*10) div 64; GetNextItem; // 8. read PixelSize PixelSize:=StrToIntDef(Item,0); GetNextItem; // 9. read PointSize PointSize:=StrToIntDef(Item,0) div 10; GetNextItem; // 10. read ResolutionX Resolution:=StrToIntDef(Item,0); if Resolution<=0 then Resolution:=72; if PixelSize=0 then begin if PointSize<=0 then Result.lfHeight:=(12*Resolution) div 72 else Result.lfHeight:=(PointSize*Resolution) div 72; end else begin Result.lfHeight:=PixelSize; end; GetNextItem; // 11. read ResolutionY Resolution:=StrToIntDef(Item,0); if Resolution<=0 then Resolution:=72; GetNextItem; // 12. read Spacing {M Monospaced (fixed pitch) P Proportional spaced (variable pitch) C Character cell. The glyphs of the font can be thought of as "boxes" of the same width and height that are stacked side by side or top to bottom.} if AnsiCompareText(Item,'M')=0 then Result.lfPitchAndFamily:=FIXED_PITCH else if AnsiCompareText(Item,'P')=0 then Result.lfPitchAndFamily:=VARIABLE_PITCH else if AnsiCompareText(Item,'C')=0 then Result.lfPitchAndFamily:=FIXED_PITCH; GetNextItem; // 13. read AverageWidth AverageWidth := StrToIntDef(Item,0); Result.lfWidth := AverageWidth div 10; GetNextItem; // 14. read CharSetRegistry // ToDo GetNextItem; // 15. read CharSetCoding // ToDo end; {------------------------------------------------------------------------------ function: ClearXLFDItem Params: const LongFontName: string; Index: integer Returns: string Replaces an item of a font name in XLFD format with a '*'. ------------------------------------------------------------------------------} function ClearXLFDItem(const LongFontName: string; Index: integer): string; var ItemStart, ItemEnd: integer; begin if FindXLFDItem(LongFontName,Index,ItemStart,ItemEnd) and ((ItemEnd-ItemStart<>1) or (LongFontName[ItemStart]<>'*')) then Result:=LeftStr(LongFontName,ItemStart-1)+'*' +RightStr(LongFontName,length(LongFontName)-ItemEnd+1) else Result:=LongFontName; end; {------------------------------------------------------------------------------ function: ClearXLFDHeight Params: const LongFontName: string Returns: string Replaces the PixelSize, PointSize, ResolutionX, ResolutionY and AverageWidth of a font name in XLFD format with '*'. An XLFD name is FontNameRegistry-Foundry-FamilyName-WeightName-Slant-SetwidthName -AddStyleName-PixelSize-PointSize-ResolutionX-ResolutionY-Spacing -AverageWidth-CharSetRegistry-CharSetCoding ------------------------------------------------------------------------------} function ClearXLFDHeight(const LongFontName: string): string; begin Result:=ClearXLFDItem(LongFontName,7); // PixelSize Result:=ClearXLFDItem(Result,8); // PointSize Result:=ClearXLFDItem(Result,9); // ResolutionX Result:=ClearXLFDItem(Result,10); // ResolutionY Result:=ClearXLFDItem(Result,12); // AverageWidth end; {------------------------------------------------------------------------------ function: ClearXLFDPitch Params: const LongFontName: string Returns: string Replaces the spacing a font name in XLFD format with a '*'. ------------------------------------------------------------------------------} function ClearXLFDPitch(const LongFontName: string): string; begin Result:=ClearXLFDItem(LongFontName,11); end; {------------------------------------------------------------------------------ function: ClearXLFDStyle Params: const LongFontName: string Returns: string Replaces the WeightName, Slant and SetwidthName of a font name in XLFD format with '*'. ------------------------------------------------------------------------------} function ClearXLFDStyle(const LongFontName: string): string; begin Result:=ClearXLFDItem(ClearXLFDItem(ClearXLFDItem(LongFontName,3),4),5); end; function XLFDHeightIsSet(const LongFontName: string): boolean; begin Result:=(ExtractXLFDItem(LongFontName,7)<>'') or (ExtractXLFDItem(LongFontName,8)<>'') or (ExtractXLFDItem(LongFontName,9)<>'') or (ExtractXLFDItem(LongFontName,10)<>''); end; {------------------------------------------------------------------------------ function: IsFontNameXLogicalFontDesc Params: const LongFontName: string Returns: boolean Checks if font name is in X Logical Font Description format. (see http://wwwinfo.cern.ch/umtf/working-groups/X11/fonts/hp_xlfd.html) An XLFD name is FontNameRegistry-Foundry-FamilyName-WeightName-Slant-SetwidthName -AddStyleName-PixelSize-PointSize-ResolutionX-ResolutionY-Spacing -AverageWidth-CharSetRegistry-CharSetCoding ------------------------------------------------------------------------------} function IsFontNameXLogicalFontDesc(const LongFontName: string): boolean; // Quick test: check if LongFontName contains 14 times the char '-' var MinusCnt, p: integer; begin MinusCnt:=0; for p:=1 to length(LongFontName) do if LongFontName[p]='-' then inc(MinusCnt); Result:=(MinusCnt=14); end; // split a given fontName into Pango Font description components // font name is supposed to follow this layout: // [FAMILY-LIST][STYLE-LIST][SIZE] // where: // [FAMILY-LIST] is a comma separated list of families optionally // ended by a comma // [STYLE-LIST] is white space separated list of words where each word // describe one of style, variant, slant, weight or stretch // [SIZE] is a decimal number (size in points) (... and points in PANGO_UNITS) // any of these options may be absent. procedure FontNameToPangoFontDescStr(const LongFontName: string; out aFamily,aStyle: string; out aSize: Integer; out aSizeInPixels: Boolean); var ParsePos: Integer; procedure addStyle(const s: string); begin if (s<>'') and (s<>'*') and (s<>'r') then begin // 'r' is regular if aStyle<>'' then aStyle := aStyle + ' ' + s else aStyle := s; end; end; function GetSize: string; var c: char; ValidBlank, CheckPixelsNeeded: boolean; InitPos: Integer; function IsBlank: boolean; begin result := c in [#0..' ']; end; function IsDigit: boolean; begin result := c in ['0'..'9']; end; begin Result := ''; ValidBlank := True; CheckPixelsNeeded := True; ParsePos := Length(LongFontname); InitPos := ParsePos; while ParsePos>0 do begin c := longFontName[ParsePos]; if IsBlank then if ValidBlank then begin dec(ParsePos); dec(InitPos); continue end else break; ValidBlank := False; if CheckPixelsNeeded then begin CheckPixelsNeeded := False; aSizeInPixels := (ParsePos > 2) and (longFontName[ParsePos - 1] = 'p') and (longFontName[ParsePos] = 'x'); if aSizeInPixels then begin dec(ParsePos, 2); Continue; end; end; if IsDigit then begin Result := C + Result; dec(ParsePos); end else begin if not IsBlank and (C <> ',')then begin Result := ''; ParsePos := InitPos; end; if C = ',' then dec(ParsePos); break; end; end; end; begin aStyle := ''; aFamily := ''; aSize := 0; aSizeInPixels := False; if IsFontNameXLogicalFontDesc(LongFontName) then begin aFamily := ExtractXLFDItem(LongFontName, XLFD_FAMILY); if aFamily='*' then aFamily:=''; aSize := StrToIntDef(ExtractXLFDItem(LongFontName, XLFD_POINTSIZE),0) div 10; addStyle( ExtractXLFDItem(LongFontName, XLFD_STYLENAME )); addStyle( ExtractXLFDItem(LongFontname, XLFD_WEIGHTNAME)); addStyle( ExtractXLFDItem(LongFontname, XLFD_SLANT)); addStyle( ExtractXLFDItem(LongFontname, XLFD_WidthName)); end else begin // this could go through, but we want to know at least the pointSize from // the fontname aSize := StrToIntDef(GetSize,0); aFamily := Copy(LongFontName, 1, ParsePos); // todo: parse aFamily to separate Family and Style end; end; { TFont } {------------------------------------------------------------------------------ Method: TFont.Create Params: none Returns: Nothing Constructor for the class. ------------------------------------------------------------------------------} constructor TFont.Create; begin inherited Create; FColor := {$ifdef UseCLDefault}clDefault{$else}clWindowText{$endif}; FPixelsPerInch := ScreenInfo.PixelsPerInchY; FPitch := DefFontData.Pitch; FCharSet := DefFontData.CharSet; FQuality := DefFontData.Quality; FHeight := DefFontData.Height; FStyle := DefFontData.Style; inherited SetSize(-MulDiv(FHeight, 72, FPixelsPerInch)); DelayAllocate := True; inherited SetName(DefFontData.Name); inherited SetFPColor(colBlack); end; {------------------------------------------------------------------------------ Method: TFont.Assign Params: Source: Another font Returns: nothing Copies the Source font to itself ------------------------------------------------------------------------------} procedure TFont.Assign(Source: TPersistent); begin if Source is TFont then begin //TODO:lock; try //TODO: TFont(Source).Lock; try BeginUpdate; try CharSet := TFont(Source).CharSet; SetColor(TFont(Source).Color, TFPCanvasHelper(Source).FPColor); if TFont(Source).PixelsPerInch <> FPixelsPerInch then // use size to convert source height pixels to current resolution Size := TFont(Source).Size else // use height which users could have changed directly Height := TFont(Source).Height; Name := TFont(Source).Name; Orientation := TFont(Source).Orientation; Pitch := TFont(Source).Pitch; Style := TFont(Source).Style; Quality := TFont(Source).Quality; finally EndUpdate; end; finally //TODO: TFont(Source).UnLock; end; finally //TODO: UnLock; end; Exit; end; inherited Assign(Source); end; {------------------------------------------------------------------------------ Method: TFont.Assign Params: ALogFont: TLogFont Returns: nothing Copies the logfont settings to itself ------------------------------------------------------------------------------} procedure TFont.Assign(const ALogFont: TLogFont); var AStyle: TFontStyles; begin BeginUpdate; try Height := ALogFont.lfHeight; Charset := TFontCharset(ALogFont.lfCharSet); AStyle := []; if ALogFont.lfWeight >= FW_SEMIBOLD then Include(AStyle, fsBold); if ALogFont.lfItalic <> 0 then Include(AStyle, fsItalic); if ALogFont.lfUnderline <> 0 then Include(AStyle, fsUnderline); if ALogFont.lfStrikeOut <> 0 then Include(AStyle, fsStrikeOut); if (FIXED_PITCH and ALogFont.lfPitchAndFamily) <> 0 then Pitch := fpFixed else if (VARIABLE_PITCH and ALogFont.lfPitchAndFamily) <> 0 then Pitch := fpVariable else Pitch := fpDefault; Style := AStyle; Quality := TFontQuality(ALogFont.lfQuality); Name := ALogFont.lfFaceName; finally EndUpdate; end; end; function TFont.IsEqual(AFont: TFont): boolean; begin if (AFont = Self) then Exit(true); if (AFont=nil) or (CharSet<>AFont.CharSet) or (Color<>AFont.Color) or (Size<>AFont.Size) or (Height<>AFont.Height) or (Name<>AFont.Name) or (Pitch<>AFont.Pitch) or (Quality<>AFont.Quality) or (Style<>AFont.Style) then Result := False else Result := True; end; procedure TFont.BeginUpdate; begin inc(FUpdateCount); end; procedure TFont.EndUpdate; begin if FUpdateCount=0 then exit; dec(FUpdateCount); if (FUpdateCount=0) and FChanged then Changed; end; {------------------------------------------------------------------------------ Method: TFont.HandleAllocated Params: none Returns: boolean Resturns True on handle allocated. ------------------------------------------------------------------------------} function TFont.HandleAllocated: boolean; begin Result := FReference.Allocated; end; {------------------------------------------------------------------------------ function TFont.IsDefault: boolean; ------------------------------------------------------------------------------} function TFont.IsDefault: boolean; begin Result:=(CharSet=DEFAULT_CHARSET) and (Color={$ifdef UseCLDefault}clDefault{$else}clWindowText{$endif}) and (Height=0) and (not IsNameStored) and (Orientation=0) and (Pitch=fpDefault) and (Size=0) and (Quality=fqDefault) and (Style=[]); end; {------------------------------------------------------------------------------ procedure TFont.SetDefault; Set Font properties to default. ------------------------------------------------------------------------------} procedure TFont.SetDefault; begin BeginUpdate; try Name := DefFontData.Name; Charset := DefFontData.CharSet; Height := DefFontData.Height; Pitch := DefFontData.Pitch; Quality := DefFontData.Quality; Style := DefFontData.Style; Color := {$ifdef UseCLDefault}clDefault{$else}clWindowText{$endif}; finally EndUpdate; end; end; {------------------------------------------------------------------------------ Method: TFont.SetSize Params: AValue: the new value Returns: nothing ------------------------------------------------------------------------------} procedure TFont.SetSize(AValue: Integer); begin if Size <> AValue then begin BeginUpdate; try FreeReference; inherited SetSize(AValue); FHeight := -MulDiv(AValue, FPixelsPerInch, 72); if IsFontNameXLogicalFontDesc(Name) then Name := ClearXLFDHeight(Name); Changed; finally EndUpdate; end; end; end; {------------------------------------------------------------------------------ Function: TFont.GetSize Params: none Returns: The font size Calculates the size based on height ------------------------------------------------------------------------------} function TFont.GetSize: Integer; begin Result := inherited Size; end; {------------------------------------------------------------------------------ Method: TFont.SetPitch Params: Value: the new value Returns: nothing Sets the pitch of a font ------------------------------------------------------------------------------} procedure TFont.SetPitch(Value : TFontPitch); Begin if FPitch <> Value then begin BeginUpdate; FreeReference; FPitch := Value; if IsFontNameXLogicalFontDesc(Name) then Name := ClearXLFDPitch(Name); Changed; EndUpdate; end; end; procedure TFont.SetPixelsPerInch(const APixelsPerInch: Integer); var OldPPI: Integer; begin if FPixelsPerInch = APixelsPerInch then Exit; OldPPI := FPixelsPerInch; FPixelsPerInch := APixelsPerInch; // the Height value is not correct anymore -> force recalculate it if Height<>0 then Height := MulDiv(Height, APixelsPerInch, OldPPI); Changed; end; {------------------------------------------------------------------------------ Method: TFont.SetHeight Params: Value: the new value Returns: nothing Sets the height of a font ------------------------------------------------------------------------------} procedure TFont.SetHeight(AValue: Integer); begin // Don't update Size only. The LogFont contains a lfHeight value and on Windows, // Qt and Carbon it is the main parameter which determins the font height. if Height <> AValue then begin BeginUpdate; try FreeReference; FHeight := AValue; // update size to equivalent value inherited SetSize(-MulDiv(AValue, 72, FPixelsPerInch)); if IsFontNameXLogicalFontDesc(Name) then Name := ClearXLFDHeight(Name); Changed; finally EndUpdate; end; end; end; {------------------------------------------------------------------------------ Method: TFont.SetStyle Params: Value: the new value Returns: nothing Sets the style of a font ------------------------------------------------------------------------------} procedure TFont.SetStyle(value : TFontStyles); begin if FStyle <> Value then begin BeginUpdate; FreeReference; FStyle := Value; inherited SetFlags(5, fsBold in FStyle); inherited SetFlags(6, fsItalic in FStyle); inherited SetFlags(7, fsUnderline in FStyle); inherited SetFlags(8, fsStrikeOut in FStyle); if IsFontNameXLogicalFontDesc(Name) then Name := ClearXLFDStyle(Name); Changed; EndUpdate; end; end; {------------------------------------------------------------------------------ Method: TFont.SetColor Params: Value: the new value Returns: nothing Sets the pencolor of a font ------------------------------------------------------------------------------} procedure TFont.SetColor(Value : TColor); begin if FColor <> Value then SetColor(Value, TColorToFPColor(Value)); end; function TFont.GetColor: TColor; begin Result := Color; if (Result = clDefault) and (Canvas is TCanvas) then Result := TCanvas(Canvas).GetDefaultColor(dctFont); end; {------------------------------------------------------------------------------ Function: TFont.GetName Params: none Returns: The font name Returns the name of the font ------------------------------------------------------------------------------} function TFont.GetName: string; begin Result := inherited Name; end; {------------------------------------------------------------------------------ Returns the orientation of the font The orientation is defined as the angle, in tenths of degrees, between the X axis of the Canvas and the baseline of the font. The property and it's setter/getter pair are compatible with Delphi ------------------------------------------------------------------------------} function TFont.GetOrientation: Integer; begin Result := FOrientation; end; {------------------------------------------------------------------------------ Method: TFont.SetName Params: Value: the new value Returns: nothing Sets the name of a font ------------------------------------------------------------------------------} procedure TFont.SetName(AValue: string); begin if Name <> AValue then begin FreeReference; inherited SetName(AValue); Changed; end; end; {------------------------------------------------------------------------------ Changes the orientation of the font The orientation is defined as the angle, in tenths of degrees, between the X axis of the Canvas and the baseline of the font. The property and it's setter/getter pair are compatible with Delphi ------------------------------------------------------------------------------} procedure TFont.SetOrientation(AValue: Integer); begin if FOrientation <> AValue then begin FreeReference; FOrientation := AValue; Changed; end; end; procedure TFont.DoAllocateResources; begin inherited DoAllocateResources; GetReference; end; procedure TFont.DoDeAllocateResources; begin FreeReference; inherited DoDeAllocateResources; end; procedure TFont.DoCopyProps(From: TFPCanvasHelper); var SrcFont: TFont; begin BeginUpdate; try inherited DoCopyProps(From); if From is TFont then begin SrcFont := TFont(From); Pitch := SrcFont.Pitch; CharSet := SrcFont.CharSet; Quality := SrcFont.Quality; Style := SrcFont.Style; end; finally EndUpdate; end; end; procedure TFont.SetFlags(Index: integer; AValue: boolean); procedure SetStyleFlag(Flag: TFontStyle; NewValue: boolean); begin BeginUpdate; FreeReference; if NewValue then Include(FStyle, Flag) else Exclude(FStyle, Flag); if IsFontNameXLogicalFontDesc(Name) then Name := ClearXLFDStyle(Name); Changed; EndUpdate; end; begin if GetFlags(Index) = AValue then Exit; inherited SetFlags(Index, AValue); case Index of 5: SetStyleFlag(fsBold, AValue); 6: SetStyleFlag(fsItalic, AValue); 7: SetStyleFlag(fsUnderline, AValue); 8: SetStyleFlag(fsStrikeOut, AValue); end; end; {------------------------------------------------------------------------------ procedure TFont.SetFPColor(const AValue: TFPColor); Set FPColor and Color ------------------------------------------------------------------------------} procedure TFont.SetFPColor(const AValue: TFPColor); begin if FPColor <> AValue then SetColor(FPColorToTColor(AValue), AValue); end; procedure TFont.SetColor(const NewColor: TColor; const NewFPColor: TFPColor); begin if (NewColor = Color) and (NewFPColor = FPColor) then Exit; FColor := NewColor; inherited SetFPColor(NewFPColor); Changed; end; {------------------------------------------------------------------------------ Method: TFont.Destroy Params: None Returns: Nothing Destructor for the class. ------------------------------------------------------------------------------} destructor TFont.Destroy; begin FreeReference; inherited Destroy; end; {------------------------------------------------------------------------------ Method: TFont.SetHandle Params: a font handle Returns: nothing sets the font to an external created font ------------------------------------------------------------------------------} procedure TFont.SetHandle(const Value: HFONT); begin SetData(GetFontData(Value)); end; procedure TFont.ReferenceNeeded; const LF_BOOL: array[Boolean] of Byte = (0, 255); LF_WEIGHT: array[Boolean] of Integer = (FW_NORMAL, FW_BOLD); LF_QUALITY: array[TFontQuality] of Integer = (DEFAULT_QUALITY, DRAFT_QUALITY, PROOF_QUALITY, NONANTIALIASED_QUALITY, ANTIALIASED_QUALITY, CLEARTYPE_QUALITY, CLEARTYPE_NATURAL_QUALITY); var ALogFont: TLogFont; CachedFont: TFontHandleCacheDescriptor; procedure SetLogFontName(const NewName: string); var l: integer; aName: string; begin if IsFontNameXLogicalFontDesc(NewName) then aName := ExtractFamilyFromXLFDName(NewName) else aName := NewName; l := High(ALogFont.lfFaceName) - Low(ALogFont.lfFaceName); if l > length(aName) then l := length(aName); if l > 0 then Move(aName[1], ALogFont.lfFaceName[Low(ALogFont.lfFaceName)], l); ALogFont.lfFaceName[Low(ALogFont.lfFaceName) + l] := #0; end; begin if FReference.Allocated then Exit; FillChar(ALogFont, SizeOf(ALogFont), 0); with ALogFont do begin lfHeight := Height; lfWidth := 0; lfEscapement := FOrientation; lfOrientation := FOrientation; lfWeight := LF_WEIGHT[fsBold in Style]; lfItalic := LF_BOOL[fsItalic in Style]; lfUnderline := LF_BOOL[fsUnderline in Style]; lfStrikeOut := LF_BOOL[fsStrikeOut in Style]; lfCharSet := Byte(FCharset); SetLogFontName(Name); lfQuality := LF_QUALITY[FQuality]; lfOutPrecision := OUT_DEFAULT_PRECIS; lfClipPrecision := CLIP_DEFAULT_PRECIS; case Pitch of fpVariable: lfPitchAndFamily := VARIABLE_PITCH; fpFixed: lfPitchAndFamily := FIXED_PITCH; else lfPitchAndFamily := DEFAULT_PITCH; end; end; FontResourceCache.Lock; try // ask the font cache for the nearest font CachedFont := FontResourceCache.FindFontDesc(ALogFont, Name); //DebugLn(['TFont.GetHandle in cache: ',CachedFont<>nil]); if CachedFont <> nil then begin CachedFont.Item.IncreaseRefCount; FReference._lclHandle := CachedFont.Item.Handle; end else begin // ask the interface for the nearest font FReference._lclHandle := TLCLHandle(CreateFontIndirectEx(ALogFont, Name)); FontResourceCache.Add(FReference.Handle, ALogFont, Name); end; FFontHandleCached := True; finally FontResourceCache.Unlock; end; FIsMonoSpaceValid := False; end; procedure TFont.SetQuality(const AValue: TFontQuality); begin if FQuality <> AValue then begin BeginUpdate; FreeReference; FQuality := AValue; if IsFontNameXLogicalFontDesc(Name) then Name := ClearXLFDStyle(Name); Changed; EndUpdate; end; end; {------------------------------------------------------------------------------ Function: TFont.GetHandle Params: none Returns: a handle to a font gdiobject Creates a font if needed ------------------------------------------------------------------------------} function TFont.GetHandle: HFONT; begin Result := HFONT(Reference.Handle); end; {------------------------------------------------------------------------------ Method: TFont.FreeReference Params: none Returns: Nothing Frees a font handle if needed ------------------------------------------------------------------------------} procedure TFont.FreeReference; begin if not FReference.Allocated then Exit; // Changing triggers deselecting the current handle Changing; if FFontHandleCached then begin if Assigned(FontResourceCache) then begin FontResourceCache.Lock; try FontResourceCache.FindFont(FReference.Handle).DecreaseRefCount; FFontHandleCached := False; finally FontResourceCache.Unlock; end; end; end else DeleteObject(HGDIOBJ(FReference.Handle)); FReference._lclHandle := 0; end; function TFont.GetCharSet: TFontCharSet; begin Result := FCharSet; end; procedure TFont.SetCharSet(const AValue: TFontCharSet); begin if FCharSet <> AValue then begin FreeReference; FCharSet := AValue; Changed; end; end; function TFont.GetData: TFontData; begin Result := DefFontData; if HandleAllocated then Result.Handle := Reference.Handle else Result.Handle := 0; Result.Height := Height; Result.Pitch := Pitch; Result.Style := Style; Result.CharSet := CharSet; Result.Quality := Quality; Result.Name := LeftStr(Name, SizeOf(Result.Name) - 1); Result.Orientation := Orientation; end; function TFont.GetIsMonoSpace: boolean; begin if not FIsMonoSpaceValid then begin FIsMonoSpace := FontIsMonoSpace(HFONT(Reference.Handle)); FIsMonoSpaceValid := True; end; Result := FIsMonoSpace; end; function TFont.GetReference: TWSFontReference; begin ReferenceNeeded; Result := FReference; end; function TFont.IsHeightStored: boolean; begin Result := DefFontData.Height <> Height; end; function TFont.IsNameStored: boolean; begin Result := DefFontData.Name <> Name; end; procedure TFont.SetData(const FontData: TFontData); var OldStyle: TFontStylesbase; begin if (HFONT(FReference.Handle) <> FontData.Handle) or not FReference.Allocated then begin OldStyle := FStyle; FreeReference; FReference._lclHandle := TLCLHandle(FontData.Handle); inherited SetSize(-MulDiv(FontData.Height, 72, FPixelsPerInch)); FHeight := FontData.Height; FPitch := FontData.Pitch; FStyle := FontData.Style; FCharSet := FontData.CharSet; FQuality := FontData.Quality; inherited SetName(FontData.Name); Bold; // it calls GetFlags if (fsBold in OldStyle)<>(fsBold in FStyle) then inherited SetFlags(5, fsBold in FStyle); if (fsItalic in OldStyle)<>(fsItalic in FStyle) then inherited SetFlags(6, fsItalic in FStyle); if (fsUnderline in OldStyle)<>(fsUnderline in FStyle) then inherited SetFlags(7, fsUnderline in FStyle); if (fsStrikeOut in OldStyle)<>(fsStrikeOut in FStyle) then inherited SetFlags(8, fsStrikeOut in FStyle); FOrientation := FontData.Orientation; Changed; end; end; function TFont.GetHeight: Integer; begin Result := FHeight; end; function TFont.GetPitch: TFontPitch; begin Result := FPitch; end; function TFont.GetStyle: TFontStyles; begin Result := FStyle; end; procedure TFont.Changed; begin if FUpdateCount > 0 then begin FChanged := True; exit; end; FChanged := False; inherited Changed; // ToDo: we need interfaces: // if FNotify <> nil then FNotify.Changed; end; // included by graphics.pp