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