source: trunk/Components/CanvasFontManager.pas@ 188

Last change on this file since 188 was 188, checked in by RBRi, 18 years ago

refactored, now uses FileUtilsUnit

  • Property svn:eol-style set to native
File size: 31.6 KB
Line 
1Unit CanvasFontManager;
2
3Interface
4
5Uses
6 OS2Def,
7 Classes, Forms, PMWIN, Graphics;
8
9Const
10 // This defines the fraction of a pixel that
11 // font character widths will be given in
12 FontWidthPrecisionFactor = 256;
13
14Type
15 // a user-oriented specification of a font;
16 // does not include OS/2 API data
17 TFontSpec = record
18 FaceName: string[ 64 ];
19 PointSize: integer; // if 0 then use x/y size
20 XSize: integer;
21 YSize: integer;
22 Attributes: TFontAttributes; // set of faBold, faItalic etc
23 end;
24
25 // NOTE: Char widths are in 1/FontWidthPrecisionFactor units
26 TCharWidthArray = array[ #0..#255 ] of longint;
27 TPCharWidthArray = ^ TCharWidthArray;
28
29 // Used internally for storing full info on font
30 TLogicalFont= Class( TComponent )
31 pFaceName: pstring; // user-selected name
32 pUseFaceName: pstring; // after substitutions.
33
34 // Selected bits of FONTMETRICS
35 fsSelection: USHORT;
36
37 FontType: TFontType;
38 FixedWidth: boolean;
39 PointSize: integer;
40 ID: integer;
41 Attributes: TFontAttributes;
42
43 // this can be nil if not already fetched
44 pCharWidthArray: TPCharWidthArray;
45 lMaxbaselineExt: LONG;
46 lAveCharWidth: LONG;
47 lMaxCharInc: LONG;
48 lMaxDescender: LONG;
49
50 destructor Destroy; override;
51 end;
52
53 TFontFace = class
54 pName: pstring;
55 FixedWidth: boolean;
56 FontType: TFontType;
57
58 Sizes: TList; // relevant for bitmap fonts only
59 // contains TLogicalFont objects
60 constructor Create;
61 destructor Destroy; override;
62 end;
63
64 TCanvasFontManager = class
65 protected
66 FCanvas: TCanvas;
67 FLogicalFonts: TList;
68 FCurrentFontSpec: TFontSpec;
69 FDefaultFontSpec: TFontSpec;
70 FCurrentFont: TLogicalFont;
71 FAllowBitmapFonts: boolean;
72
73 protected
74 function CreateFont( const FontSpec: TFontSpec ): TLogicalFont;
75
76 function GetFont( const FontSpec: TFontSpec ): TLogicalFont;
77
78 procedure RegisterFont( Font: TLogicalFont );
79 procedure SelectFont( Font: TLogicalFont;
80 Scale: longint );
81
82 // Retrieve character widths for current font
83 procedure LoadMetrics;
84
85 // load metrics if needed
86 procedure EnsureMetricsLoaded;
87 public
88 Constructor Create( Canvas: TCanvas;
89 AllowBitmapFonts: boolean );
90 destructor Destroy; override;
91
92 // Useful functions:
93
94 // Set the font for the associated canvas.
95 procedure SetFont( const FontSpec: TFontSpec );
96
97 // Retrieve the width of the given char, in the current font
98 function CharWidth( const C: Char ): longint;
99
100 function AverageCharWidth: longint;
101 function MaximumCharWidth: longint;
102
103 function IsFixed: boolean;
104
105 function CharHeight: longint;
106 function CharDescender: longint;
107
108 procedure DrawString( Var Point: TPoint;
109 const Length: longint;
110 const S: PChar );
111
112 property Canvas: TCanvas read FCanvas;
113 property DefaultFontSpec: TFontSpec read FDefaultFontSpec write FDefaultFontSpec;
114 end;
115
116// Convert a Sibyl font to a FontSpec (Color is left the same)
117procedure SibylFontToFontSpec( Font: TFont; Var FontSpec: TFontSpec );
118
119 // Thoughts on how it works....
120
121 // SelectFont looks for an existing logical font that
122 // matches the request. If found selects that logical font
123 // onto the canvas.
124
125 // If not found it creates a logical font and selects that onto
126 // the canvas.
127
128 // For bitmap fonts the logical font definition includes pointsize
129 // For outline fonts the defn is only face+attr; in this case
130 // selectfont also ses the 'CharBox' according to the point size.
131Implementation
132
133uses
134 PMWin,
135 PMGpi,
136 OS2Def,
137 PmDev,
138 SysUtils,
139 ACLStringUtility;
140
141Imports
142 Function GpiQueryCharStringPosAt( PS: Hps;
143 StartPoint: PPointL;
144 Options: ULONG;
145 Count: LONG;
146 TheString: PChar;
147 IncrementsArray: PLONG;
148 CharacterPoints: PPointL ): BOOL;
149 ApiEntry; 'PMGPI' Index 585;
150 Function GpiQueryCharStringPos( PS: Hps;
151 Options: ULONG;
152 Count: LONG;
153 TheString: PChar;
154 IncrementsArray: PLONG;
155 CharacterPoints: PPointL ): BOOL;
156 ApiEntry; 'PMGPI' Index 584;
157end;
158
159Type
160 // A little pretend window to send font name.size
161 // and get definite font info back. (See .CreateFont)
162 TFontWindow = class( TControl )
163 public
164 procedure CreateWnd; override;
165 property OwnerDraw;
166 Function SetPPFontNameSize( Const FNS: String ): Boolean;
167 end;
168
169var
170 FontFaces: TList = nil; // of TFontface
171 FontWindow: TFontWindow;
172
173 DefaultOutlineFixedFace: TFontFace;
174 DefaultOutlineProportionalFace: TFontFace;
175
176// TFontFace
177//------------------------------------------------------------------------
178
179constructor TFontface.Create;
180begin
181 Sizes := TList.Create;
182end;
183
184destructor TFontface.Destroy;
185begin
186 Sizes.Destroy;
187end;
188
189// TLogicalFont
190//------------------------------------------------------------------------
191
192// frees allocated memory, if any.
193// Note - does not delete the Gpi Logical Font
194destructor TLogicalFont.Destroy;
195begin
196 DisposeStr( pFaceName );
197 DisposeStr( pUseFaceName );
198
199 if pCharWidthArray <> nil then
200 FreeMem( pCharWidthArray,
201 sizeof( TCharWidthArray ) );
202
203 inherited Destroy;
204end;
205
206// TFontWindow
207//------------------------------------------------------------------------
208
209procedure TFontWindow.CreateWnd;
210begin
211 inherited CreateWnd;
212end;
213
214Function TFontWindow.SetPPFontNameSize( Const FNS: String ): Boolean;
215Var
216 CS: Cstring;
217Begin
218 CS := FNS;
219
220 Result := WinSetPresParam( Handle,
221 PP_FONTNAMESIZE,
222 Length( CS ) + 1,
223 CS );
224End;
225
226//------------------------------------------------------------------------
227
228// Convert a Sibyl font to a FontSpec
229//------------------------------------------------------------------------
230procedure SibylFontToFontSpec( Font: TFont; Var FontSpec: TFontSpec );
231begin
232 FontSpec.FaceName := Font.FaceName;
233 FontSpec.PointSize := Font.PointSize;
234 FontSpec.Attributes := Font.Attributes;
235end;
236
237// Find a font face with the given name
238//------------------------------------------------------------------------
239function FindFaceName( const name: string ): TFontFace;
240Var
241 FaceIndex: LongInt;
242 Face: TFontFace;
243begin
244 for FaceIndex := 0 to FontFaces.Count - 1 do
245 begin
246 Face := FontFaces[ FaceIndex ];
247
248 if StringsSame( Face.pName^, Name ) then
249 begin
250 Result := Face;
251 exit;
252 end;
253 end;
254 Result := nil;
255end;
256
257// Return the first font face of type = Outline (scalable)
258//------------------------------------------------------------------------
259function GetFirstOutlineFace( FixedWidth: boolean ): TFontFace;
260Var
261 FaceIndex: LongInt;
262 Face: TFontFace;
263begin
264 for FaceIndex := 0 to FontFaces.Count - 1 do
265 begin
266 Face := FontFaces[ FaceIndex ];
267
268 if ( Face.FixedWidth = FixedWidth )
269 and ( Face.FontType = ftOutline ) then
270 begin
271 Result := Face;
272 exit;
273 end;
274 end;
275 Result := nil;
276end;
277
278// Find the bitmap font which best matches the given pointsize.
279//------------------------------------------------------------------------
280function GetClosestBitmapFixedFont( const PointSize: longint ): TLogicalFont;
281Var
282 FaceIndex: Longint;
283 FontIndex: longint;
284 Face: TFontFace;
285 Font: TLogicalFont;
286begin
287 Result := nil;
288 for FaceIndex := 0 to FontFaces.Count - 1 do
289 begin
290 Face := FontFaces[ FaceIndex ];
291
292 if Face.FontType = ftBitmap then
293 begin
294 for FontIndex := 0 to Face.Sizes.Count - 1 do
295 begin
296 Font := Face.Sizes[ FontIndex ];
297 if Font.FixedWidth then
298 begin
299 if ( Result = nil )
300 or ( Abs( Font.PointSize - PointSize )
301 < Abs( Result.PointSize - PointSize ) ) then
302 Result := Font;
303 end;
304 end;
305 end;
306 end;
307end;
308
309// Pick some nice default fonts.
310//------------------------------------------------------------------------
311procedure GetDefaultFonts;
312begin
313 // courier new is common and reasonably nice
314 DefaultOutlineFixedFace := FindFaceName( 'Courier New' );
315 if DefaultOutlineFixedFace = nil then
316 begin
317 DefaultOutlineFixedFace := GetFirstOutlineFace( true ); // first fixed outline face
318 end;
319
320 DefaultOutlineProportionalFace := FindFaceName( 'Helvetica' );
321 if DefaultOutlineProportionalFace = nil then
322 begin
323 DefaultOutlineProportionalFace := GetFirstOutlineFace( false ); // first prop outline face
324 end;
325end;
326
327Type
328 TMyFontMetrics = Array[ 0..1 ] Of FONTMETRICS;
329 PMyFontMetrics = ^TMyFontMetrics;
330
331// Fetch the global list of font faces and sizes
332//------------------------------------------------------------------------
333procedure GetFontList;
334Var
335 Count: LongInt;
336 aPS: HPS;
337 T: LongInt;
338 Font: TLogicalFont;
339 Face: TFontFace;
340 pfm: PMyFontMetrics;
341 FamilyName: string;
342 fsDefn: USHORT;
343Begin
344 FontFaces := TList.Create;
345
346 aPS := WinGetPS( HWND_DESKTOP );
347 Count := 0;
348 // Get font count
349 Count := GpiQueryFonts( aPS,
350 QF_PUBLIC,
351 Nil,
352 Count,
353 0,
354 Nil );
355 If Count > 0 Then
356 Begin
357 // allocate memory to retrieve all the font data.
358 GetMem( pfm, Count * SizeOf( FONTMETRICS ) );
359 GpiQueryFonts( aPS,
360 QF_PUBLIC,
361 Nil,
362 Count,
363 SizeOf(FONTMETRICS),
364 pfm^[ 0 ] );
365
366 For T := 0 To Count - 1 Do
367 Begin
368 Font := TLogicalFont.Create( Screen );
369
370 Font.pFaceName := NewStr( pfm^[ T ].szFaceName );
371 FamilyName := pfm^[ T ].szFamilyName;
372
373 // See what type it is. Actually this is not very
374 // useful as various substitutions are normally made.
375 fsDefn := pfm^[ T ].fsDefn;
376 If ( fsDefn And FM_DEFN_OUTLINE ) <> 0 Then
377 Font.FontType := ftOutline
378 else
379 Font.FontType := ftBitmap;
380
381 if Font.pFaceName^ = 'Helvetica' then
382 Font := Font;
383 Font.PointSize := pfm^[ T ].sNominalPointSize div 10;
384 Font.FixedWidth := pfm^[ T ].fsType And FM_TYPE_FIXED <> 0 ;
385 Font.fsSelection := pfm^[ T ].fsSelection;
386 Font.lMaxbaselineExt := pfm^[ T ].lMaxbaselineExt;
387 Font.lAveCharWidth := pfm^[ T ].lAveCharWidth;
388 Font.lMaxCharInc := pfm^[ T ].lMaxCharInc;
389
390 Font.ID := -1; // and always shall be so...
391
392 Face := FindFaceName( Font.pFaceName^ );
393 if Face = nil then
394 begin
395 // new face found
396 Face := TFontFace.Create;
397 Face.pName := Font.pFaceName; // point to the actual face name string!
398 Face.FixedWidth := Font.FixedWidth;
399 Face.FontType := Font.FontType;
400 FontFaces.Add( Face );
401 end;
402 Face.Sizes.Add( Font );
403 End;
404 End;
405
406 FreeMem( pfm, Count * SizeOf( FONTMETRICS ) );
407 WinReleasePS( aPS );
408
409 // pick some for defaults
410 GetDefaultFonts;
411
412 FontWindow := TFontWindow.Create( Nil );
413 FontWindow.OwnerDraw := True;
414 FontWindow.CreateWnd;
415end;
416
417// Add .subscript to font name for attributes
418//------------------------------------------------------------------------
419Function ModifyFontName( const FontName: string;
420 const Attrs: TFontAttributes ): String;
421Begin
422 Result := FontName;
423 If faItalic in Attrs Then
424 Result := Result + '.Italic';
425 If faBold in Attrs Then
426 Result := Result + '.Bold';
427 If faOutline in Attrs Then
428 Result := Result + '.Outline';
429 If faStrikeOut in Attrs Then
430 Result := Result + '.Strikeout';
431 If faUnderScore in Attrs Then
432 Result := Result + '.Underscore';
433End;
434
435// Create a font without attributes
436//------------------------------------------------------------------------
437function CreateFontBasic( const FaceName: string;
438 const PointSize: integer ): TLogicalFont;
439var
440 PPString: string;
441 PresSpace: HPS;
442 FontInfo: FONTMETRICS;
443begin
444 Result := TLogicalFont.Create( nil );
445
446 if FindFaceName( FaceName ) = nil then
447 exit;
448
449 Result.PointSize := PointSize; // will use later if the result was an outline font...
450 Result.pFaceName := NewStr( FaceName );
451
452 // OK now we have found the font face...
453 PPString := IntToStr( PointSize) + '.' + FaceName;
454
455 PPString := ModifyFontName( PPString, [] );
456 If Not FontWindow.SetPPFontNameSize( PPString ) Then
457 // Gurk!
458 Exit;
459
460 PresSpace := WinGetPS( FontWindow.Handle );
461 If Not GpiQueryFontMetrics( PresSpace,
462 SizeOf( FONTMETRICS ),
463 FontInfo ) Then
464 begin
465 // Clurkle!?
466 WinReleasePS( PresSpace );
467 Exit;
468 end;
469 WinReleasePS( PresSpace );
470
471 if ( FontInfo.fsDefn And FM_DEFN_OUTLINE ) > 0 then
472 Result.FontType := ftOutline
473 else
474 Result.FontType := ftBitmap;
475end;
476
477// Provide outline substitutes for some common bitmap fonts
478// From Mozilla/2 source.
479//------------------------------------------------------------------------
480function SubstituteBitmapFontToOutline( const FaceName: string ): string;
481begin
482 if StringsSame( FaceName, 'Helv' ) then
483 result := 'Helvetica'
484 else if StringsSame( FaceName, 'Tms Rmn' ) then
485 result := 'Times New Roman'
486 else if StringsSame( FaceName, 'System Proportional' ) then
487 result := 'Helvetica'
488 else if StringsSame( FaceName, 'System Monospaced' ) then
489 result := 'Courier'
490 else if StringsSame( FaceName, 'System VIO' ) then
491 result := 'Courier'
492 else
493 result := FaceName; // no substitution
494
495end;
496
497// NOTE!!! Not currently used or working...
498// Find a font with exact width and height
499//------------------------------------------------------------------------
500function FindXYSizeFont( const Face: TFontFace;
501 const XSize: longint;
502 const YSize: longint ): TLogicalFont;
503var
504 SizeIndex: longint;
505 F: TLogicalFont;
506 FontInfo: FONTMETRICS;
507begin
508 for SizeIndex := 0 to Face.Sizes.Count - 1 do
509 begin
510 F := Face.Sizes[ SizeIndex ];
511 if ( F.lMaxbaselineExt = YSize )
512 and ( F.lAveCharWidth = XSize ) then
513 begin
514 // found exact match
515 FontInfo.lMaxbaselineExt := F.lMaxbaselineExt;
516 FontInfo.lAveCharWidth := F.lAveCharWidth;
517 Result.FontType := ftBitmap;
518 end;
519 end;
520end;
521
522// Ask OS/2 dummy font window to convert a font spec
523// into a FONTMETRICS.
524//------------------------------------------------------------------------
525procedure AskOS2FontDetails( const FaceName: string;
526 const PointSize: longint;
527 const Attributes: TFontAttributes;
528 var FontInfo: FONTMETRICS );
529var
530 PPString: string;
531 PresSpace: HPS;
532begin
533 // Hack from Sibyl code - we don't know WTF the algorithm is
534 // for selecting between outline/bitmap and doing substitutions
535 // so send it to a dummy window and find out the resulting details
536 PPString := IntToStr( PointSize )
537 + '.'
538 + FaceName;
539
540 PPString := ModifyFontName( PPString, Attributes );
541
542 FontWindow.SetPPFontNameSize( PPString );
543
544 PresSpace := WinGetPS( FontWindow.Handle );
545 GpiQueryFontMetrics( PresSpace,
546 SizeOf( FontInfo ),
547 FontInfo );
548 WinReleasePS( PresSpace );
549end;
550
551// Look for the best match for the given face, size and attributes.
552// If FixedWidth is set then makes sure that the result is fixed
553// (if there is any fixed font on the system at all!)
554// This uses the OS/2 GPI and therefore makes some substitutions,
555// such as Helv 8 (bitmap) for Helvetica 8 (outline)
556//------------------------------------------------------------------------
557procedure FindBestFontMatch( const FaceName: string;
558 const PointSize: longint;
559 const Attributes: TFontAttributes;
560 const FixedWidth: boolean;
561 var FontInfo: FONTMETRICS );
562var
563 BestBitmapFontMatch: TLogicalFont;
564begin
565 // First just ask GPI to give us a font
566 AskOS2FontDetails( FaceName,
567 PointSize,
568 Attributes,
569 FontInfo );
570
571 if not FixedWidth then
572 // OK, whatever it gave us.
573 exit;
574
575 // we want a fixed width font...
576 if ( FontInfo.fsType and FM_TYPE_FIXED ) <> 0 then
577 // got a suitable font
578 exit;
579
580 // the stoopid freaking OS/2 GPI has given us
581 // a proportional font for that size
582 if DefaultOutlineFixedFace <> nil then
583 // use the default fixed width outline face
584 AskOS2FontDetails( DefaultOutlineFixedFace.pName^,
585 PointSize,
586 Attributes,
587 FontInfo );
588
589
590 if ( FontInfo.fsType and FM_TYPE_FIXED ) <> 0 then
591 // got a suitable font
592 exit;
593
594 // still got a proportional font,
595 // or we didn't have any fixed width outline face
596 // so see what we can find in the way of a bitmap fixed font
597
598 BestBitmapFontMatch := GetClosestBitmapFixedFont( PointSize );
599 if BestBitmapFontMatch <> nil then
600 begin
601 FontInfo.lMaxbaseLineExt := BestBitmapFontMatch.lMaxbaselineExt;
602 FontInfo.lAveCharWidth := BestBitmapFontMatch.lAveCharWidth;
603 FontInfo.fsDefn := 0;
604 FontInfo.szFaceName := BestBitmapFontMatch.pFaceName^;
605 end;
606 // else - there are no fixed fonts of any kind on the system. Oh dear.
607
608end;
609
610//------------------------------------------------------------------------
611// Font manager
612//------------------------------------------------------------------------
613
614// constructor
615//------------------------------------------------------------------------
616constructor TCanvasFontManager.Create( Canvas: TCanvas;
617 AllowBitmapFonts: boolean );
618begin
619 inherited Create;
620
621 if FontFaces = nil then
622 GetFontList;
623
624 FCanvas := Canvas;
625 FLogicalFonts := TList.Create;
626 FCurrentFontSpec.FaceName := 'notafont';
627 FCurrentFont := nil;
628 FAllowBitmapFonts := AllowBitmapFonts;
629
630 // get system default font spec
631 // as default default ;)
632 SibylFontToFontSpec( Screen.DefaultFont,
633 FDefaultFontSpec );
634
635end;
636
637// Destructor
638//------------------------------------------------------------------------
639destructor TCanvasFontManager.Destroy;
640var
641 i: integer;
642 Font: TLogicalFont;
643 rc: ERRORID;
644begin
645 // select default font so none of our logical fonts are in use
646 if not GpiSetCharSet( FCanvas.Handle, LCID_DEFAULT ) then
647 rc := WinGetLastError( AppHandle );
648
649 // delete each logical font and our record of it
650 for i := 0 to FLogicalFonts.Count - 1 do
651 begin
652 Font := FLogicalFonts[ i ];
653 if not GpiDeleteSetID( FCanvas.Handle, Font.ID ) then
654 rc := WinGetLastError( AppHandle );
655 Font.Destroy;
656 end;
657 FLogicalFonts.Destroy;
658 inherited Destroy;
659end;
660
661// Create a logical font for the given spec
662//------------------------------------------------------------------------
663function TCanvasFontManager.CreateFont( const FontSpec: TFontSpec ): TLogicalFont;
664var
665 UseFaceName: string;
666 Face: TFontFace;
667 RemoveBoldFromSelection: boolean;
668 RemoveItalicFromSelection: boolean;
669 UseAttributes: TFontAttributes;
670 MatchAttributes: TFontAttributes;
671 BaseFont: TLogicalFont;
672 BaseFontIsBitmapFont: Boolean;
673 FontInfo: FONTMETRICS;
674 FixedWidth: boolean;
675begin
676 Face := nil;
677 RemoveBoldFromSelection := false;
678 RemoveItalicFromSelection := false;
679
680 UseAttributes := FontSpec.Attributes;
681
682 // see if the originally specified font is a fixed width one.
683 FixedWidth := false;
684 Face := FindFaceName( FontSpec.FaceName );
685 if Face <> nil then
686 FixedWidth := Face.FixedWidth;
687
688 Face := nil;
689
690 if not FAllowBitmapFonts then
691 UseFaceName := SubstituteBitmapFontToOutline( FontSpec.FaceName )
692 else
693 UseFaceName := FontSpec.FaceName;
694
695 if FontSpec.Attributes <> [] then
696 begin
697 BaseFontIsBitmapFont := false;
698 if FAllowBitmapFonts then
699 begin
700 // First see if the base font (without attributes)
701 // would be a bitmap font...
702 BaseFont := CreateFontBasic( UseFaceName, FontSpec.PointSize );
703 if BaseFont <> nil then
704 begin
705 BaseFontIsBitmapFont := BaseFont.FontType = ftBitmap;
706 BaseFont.Destroy;
707 end;
708 end;
709
710 If not BaseFontIsBitmapFont Then
711 begin
712 // Result is an outline font so look for specific bold/italic fonts
713 if ( faBold in FontSpec.Attributes )
714 and ( faItalic in FontSpec.Attributes ) then
715 begin
716 Face := FindFaceName( UseFaceName + ' BOLD ITALIC' );
717 if Face <> nil then
718 begin
719 Exclude( UseAttributes, faBold );
720 Exclude( UseAttributes, faItalic );
721 RemoveBoldFromSelection := true;
722 RemoveItalicFromSelection := true;
723 end;
724 end;
725
726 if Face = nil then
727 if faBold in FontSpec.Attributes then
728 begin
729 Face := FindFaceName( UseFaceName + ' BOLD' );
730 if Face <> nil then
731 begin
732 Exclude( UseAttributes, faBold );
733 RemoveBoldFromSelection := true;
734 end;
735 end;
736
737 if Face = nil then
738 if faItalic in FontSpec.Attributes then
739 begin
740 Face := FindFaceName( UseFaceName + ' ITALIC' );
741 if Face <> nil then
742 begin
743 Exclude( UseAttributes, faItalic );
744 RemoveItalicFromSelection := true;
745 end;
746 end;
747 end;
748 end;
749
750 if Face <> nil then
751 // found a styled face, does it match fixed width?
752 if Face.FixedWidth <> FixedWidth then
753 // no so we don't want to use it.
754 Face := nil;
755
756 if Face = nil then
757 // didn't find a styled face (or no styles set)
758 // so find unmodified, we will use simulation bits
759 Face := FindFaceName( UseFaceName );
760
761 if not FAllowBitmapFonts then
762 if Face.FontType = ftBitmap then
763 // we aren't allowed bitmaps, but that's what this
764 // face is. So use the default outline face of the
765 // appropriate width type
766 if FixedWidth then
767 Face := DefaultOutlineFixedFace
768 else
769 Face := DefaultOutlineProportionalFace;
770
771 if Face = nil then
772 begin
773 // Could not find the specified font name. Bummer.
774 Result := nil;
775 exit;
776 end;
777
778 // OK now we have found the font face...
779
780 Result := TLogicalFont.Create( nil );
781
782 Result.PointSize := FontSpec.PointSize; // will use later if the result was an outline font...
783 Result.pFaceName := NewStr( FontSpec.FaceName );
784 Result.pUseFaceName := NewStr( Face.pName^ );
785 Result.Attributes := FontSpec.Attributes;
786
787 Result.fsSelection := 0;
788
789 Result.FixedWidth := Face.FixedWidth;
790
791 if FAllowBitmapFonts then
792 begin
793 if BaseFontIsBitmapFont then
794 MatchAttributes := []
795 else
796 MatchAttributes := UseAttributes;
797 FindBestFontMatch( Face.pName^,
798 FontSpec.PointSize,
799 MatchAttributes,
800 FixedWidth,
801 FontInfo );
802
803 AssignStr( Result.pUseFaceName, FontInfo.szFaceName );
804
805 // We may actually get a bitmap OR an outline font back
806 If ( FontInfo.fsDefn And FM_DEFN_OUTLINE ) <> 0 Then
807 Result.FontType := ftOutline
808 else
809 Result.FontType := ftBitmap;
810 end
811 else
812 begin
813 // no bitmap fonts please.
814 Result.FontType := ftOutline
815 end;
816
817 // store the baseline and average char width.
818 // For bitmap fonts, these tell GPI which font we really want
819 // For outline fonts, we are just storing them for later ref.
820 Result.lMaxbaseLineExt := FontInfo.lMaxbaselineExt;
821 Result.lAveCharWidth := FontInfo.lAveCharWidth;
822 Result.lMaxCharInc := FontInfo.lMaxCharInc;
823
824 // Set style flags
825 with Result do
826 begin
827 If faBold in UseAttributes Then
828 fsSelection := fsSelection or FM_SEL_BOLD;
829 If faItalic in UseAttributes Then
830 fsSelection := fsSelection or FM_SEL_ITALIC;
831 If faUnderScore in UseAttributes Then
832 fsSelection := fsSelection or FM_SEl_UNDERSCORE;
833 If faStrikeOut in UseAttributes Then
834 fsSelection := fsSelection or FM_SEl_STRIKEOUT;
835 If faOutline in UseAttributes Then
836 fsSelection := fsSelection or FM_SEl_OUTlINE;
837 end;
838
839 Result.pCharWidthArray := Nil;
840end;
841
842// Register the given logical font with GPI and store for later use
843//------------------------------------------------------------------------
844procedure TCanvasFontManager.RegisterFont( Font: TLogicalFont );
845var
846 fa: FATTRS;
847 rc: LONG;
848begin
849 FLogicalFonts.Add( Font );
850 Font.ID := FLogicalFonts.Count + 1; // add 1 to stay out of Sibyl's way
851
852 // Initialise GPI font attributes
853 FillChar( fa, SizeOf( FATTRS ), 0 );
854 fa.usRecordLength := SizeOf( FATTRS );
855
856 // Copy facename and 'simulation' attributes from what we obtained
857 // earlier
858 fa.szFaceName := Font.pUseFaceName^;
859 fa.fsSelection := Font.fsSelection;
860
861 fa.lMatch := 0; // please Mr GPI be helpful and do clever stuff for us, we are ignorant
862
863 fa.idRegistry := 0; // IBM magic number
864 fa.usCodePage := 0; // use current codepage
865
866 If Font.FontType = ftOutline then
867 // Outline font wanted
868 fa.fsFontUse := FATTR_FONTUSE_OUTLINE Or FATTR_FONTUSE_TRANSFORMABLE
869 else
870 // bitmap font
871 fa.fsFontUse := 0;
872
873 // don't need mixing with graphics (for now)
874 fa.fsFontUse := fa.fsFontUse or FATTR_FONTUSE_NOMIX;
875
876 // copy char cell width/height from the (valid) one we
877 // found earlier in GetFont (will be zero for outline)
878 fa.lMaxbaseLineExt := Font.lMaxbaselineExt;
879 fa.lAveCharWidth := Font.lAveCharWidth;
880
881 fa.fsType := 0;
882
883 // create logical font
884 rc := GpiCreateLogFont( FCanvas.Handle,
885 nil,
886 Font.ID,
887 fa );
888end;
889
890// Select the given (existing) logical font
891//------------------------------------------------------------------------
892procedure TCanvasFontManager.SelectFont( Font: TLogicalFont;
893 Scale: longint );
894var
895 aHDC: HDC;
896 xRes: LongInt;
897 yRes: LongInt;
898 aSizeF: SIZEF;
899begin
900 // Select the logical font
901 GpiSetCharSet( FCanvas.Handle, Font.ID );
902 if Font.FontType = ftOutline then
903 begin
904 // For outline fonts, also set character Box
905 aHDC := GpiQueryDevice( FCanvas.Handle );
906 DevQueryCaps( aHDC,
907 CAPS_HORIZONTAL_FONT_RES,
908 1,
909 xRes );
910 DevQueryCaps( aHDC,
911 CAPS_VERTICAL_FONT_RES,
912 1,
913 yRes );
914
915 aSizeF.CX := 65536 * xRes* Font.PointSize Div 72 * Scale;
916 aSizeF.CY := 65536 * yRes* Font.PointSize Div 72 * Scale;
917
918 GpiSetCharBox( FCanvas.Handle, aSizeF );
919 end;
920end;
921
922// Get a font to match the given spec, creating or re-using an
923// existing font as needed.
924//------------------------------------------------------------------------
925function TCanvasFontManager.GetFont( const FontSpec: TFontSpec ): TLogicalFont;
926var
927 AFont: TLogicalFont;
928 FontIndex: integer;
929begin
930 for FontIndex := 0 to FLogicalFonts.Count - 1 do
931 begin
932 AFont := FLogicalFonts[ FontIndex ];
933 if AFont.PointSize = FontSpec.PointSize then
934 begin
935 if ( AFont.PointSize > 0 )
936 or ( ( AFont.lAveCharWidth = FontSpec.XSize )
937 and ( AFont.lMaxbaselineExt = FontSpec.YSize ) ) then
938 begin
939 if AFont.Attributes = FontSpec.Attributes then
940 begin
941 // search name last since it's the slowest thing
942 if AFont.pFaceName^ = FontSpec.FaceName then
943 begin
944 // Found a logical font already created
945 Result := AFont;
946 // done
947 exit;
948 end;
949 end;
950 end;
951 end;
952 end;
953
954 // Need to create new logical font
955 Result := CreateFont( FontSpec );
956 if Result <> nil then
957 begin
958 RegisterFont( Result );
959 end;
960end;
961
962// Set the current font for the canvas to match the given
963// spec, creating or re-using fonts as needed.
964//------------------------------------------------------------------------
965procedure TCanvasFontManager.SetFont( const FontSpec: TFontSpec );
966var
967 Font: TLogicalFont;
968 DefaultFontSpec: TFontSpec;
969begin
970 if FCurrentFontSpec = FontSpec then
971 // same font
972 exit;
973
974 Font := GetFont( FontSpec );
975
976 if Font = nil then
977 begin
978 // ack! Pfffbt! Couldn't find the font.
979
980 // Try to get the default font
981 Font := GetFont( FDefaultFontSpec );
982 if Font = nil then
983 begin
984 SibylFontToFontSpec( Screen.DefaultFont,
985 DefaultFontSpec );
986 Font := GetFont( DefaultFontSpec );
987 if Font = nil then
988 // Jimminy! We can't even get the default system font
989 raise Exception.Create( 'Could not access default font '
990 + 'in place of '
991 + FontSpec.FaceName
992 + ' '
993 + IntToStr( FontSpec.PointSize ) );
994 end;
995
996 end;
997
998 SelectFont( Font, 1 );
999 FCurrentFontSpec := FontSpec;
1000 FCurrentFont := Font;
1001end;
1002
1003// Get the widths of all characters for current font
1004// and other dimensions
1005//------------------------------------------------------------------------
1006procedure TCanvasFontManager.LoadMetrics;
1007var
1008 TheChar: Char;
1009 fm: FONTMETRICS;
1010begin
1011 // Retrieve all character widths
1012 if FCurrentFont.FontType = ftOutline then
1013 begin
1014 SelectFont( FCurrentFont, FontWidthPrecisionFactor );
1015 end;
1016
1017 // allocate memory for storing the char widths
1018 GetMem( FCurrentFont.pCharWidthArray,
1019 sizeof( TCharWidthArray ) );
1020 if not GpiQueryWidthTable( FCanvas.Handle,
1021 0, 256,
1022 FCurrentFont.pCharWidthArray^[ #0 ] ) then
1023 begin
1024 raise Exception.Create( 'Error getting character width table: '
1025 + 'GpiQueryWidthTable error '
1026 + IntToStr( WinGetLastError( AppHandle ) ) );
1027 end;
1028
1029 // Convert all widths to positive!
1030 // For unknown reason, sometimes GPI returns negative values...
1031 for TheChar := #0 to #255 do
1032 begin
1033 FCurrentFont.pCharWidthArray^[ TheChar ] := Abs( FCurrentFont.pCharWidthArray^[ TheChar ] );
1034 end;
1035
1036 if FCurrentFont.FontType = ftOutline then
1037 begin
1038 SelectFont( FCurrentFont, 1 );
1039 end
1040 else
1041 begin
1042 // For bitmap fonts, multiply by 256 manually
1043 for TheChar := #0 to #255 do
1044 begin
1045 FCurrentFont.pCharWidthArray^[ TheChar ] :=
1046 FCurrentFont.pCharWidthArray^[ TheChar ]
1047 * FontWidthPrecisionFactor;
1048 end;
1049 end;
1050
1051 GpiQueryFontMetrics( FCanvas.Handle,
1052 sizeof( fm ),
1053 fm );
1054 FCurrentFont.lMaxbaseLineExt := fm.lMaxbaselineExt;
1055 FCurrentFont.lAveCharWidth := fm.lAveCharWidth;
1056 FCurrentFont.lMaxCharInc := fm.lMaxCharInc;
1057 FCurrentFont.lMaxDescender := fm.lMaxDescender;
1058end;
1059
1060procedure TCanvasFontManager.EnsureMetricsLoaded;
1061begin
1062 if FCurrentFont = nil then
1063 raise( Exception.Create( 'No font selected before getting font metrics' ) );
1064
1065 if FCurrentFont.pCharWidthArray = Nil then
1066 LoadMetrics;
1067end;
1068
1069function TCanvasFontManager.CharWidth( const C: Char ): longint;
1070begin
1071 EnsureMetricsLoaded;
1072 Result := FCurrentFont.pCharWidthArray^[ C ];
1073end;
1074
1075function TCanvasFontManager.AverageCharWidth: longint;
1076begin
1077 EnsureMetricsLoaded;
1078 Result := FCurrentFont.lAveCharWidth;
1079end;
1080
1081function TCanvasFontManager.MaximumCharWidth: longint;
1082begin
1083 EnsureMetricsLoaded;
1084 Result := FCurrentFont.lMaxCharInc;
1085end;
1086
1087function TCanvasFontManager.CharHeight;
1088begin
1089 EnsureMetricsLoaded;
1090 Result := FCurrentFont.lMaxBaseLineExt;
1091end;
1092
1093function TCanvasFontManager.CharDescender;
1094begin
1095 EnsureMetricsLoaded;
1096 Result := FCurrentFont.lMaxDescender;
1097end;
1098
1099function TCanvasFontManager.IsFixed: boolean;
1100begin
1101 Result := FCurrentFont.FixedWidth;
1102end;
1103
1104procedure TCanvasFontManager.DrawString( Var Point: TPoint;
1105 const Length: longint;
1106 const S: PChar );
1107begin
1108 GpiCharStringAt( FCanvas.Handle,
1109 Point,
1110 Length,
1111 S^ );
1112 Point := FCanvas.PenPos;
1113end;
1114
1115end.
Note: See TracBrowser for help on using the repository browser.