Font.st
changeset 0 48194c26a46c
child 2 b35336ab0de3
equal deleted inserted replaced
-1:000000000000 0:48194c26a46c
       
     1 "
       
     2  COPYRIGHT (c) 1992-93 by Claus Gittinger
       
     3               All Rights Reserved
       
     4 
       
     5  This software is furnished under a license and may be used
       
     6  only in accordance with the terms of that license and with the
       
     7  inclusion of the above copyright notice.   This software may not
       
     8  be provided or otherwise made available to, or used by, any
       
     9  other person.  No title to or ownership of the software is
       
    10  hereby transferred.
       
    11 "
       
    12 
       
    13 Object subclass:#Font
       
    14        instanceVariableNames:'family face style size encoding
       
    15                               device fontId replacementFont
       
    16                               ascent descent height width isFixedWidth
       
    17 			      minWidth maxWidth'
       
    18        classVariableNames:'lobby'
       
    19        poolDictionaries:''
       
    20        category:'Graphics-Support'
       
    21 !
       
    22 
       
    23 Font comment:'
       
    24 
       
    25 COPYRIGHT (c) 1992-93 by Claus Gittinger
       
    26               All Rights Reserved
       
    27 
       
    28 see Font class documentation for more info
       
    29 
       
    30 %W% %E%
       
    31 
       
    32 total rewrite from XFont summer 92 by claus
       
    33 '!
       
    34 
       
    35 !Font class methodsFor:'documentation'!
       
    36 
       
    37 documentation
       
    38     "
       
    39 Font represents fonts in a device independent manner; after beeing
       
    40 created using 'Font family:family face:face style:style size:size',
       
    41 the returned font is not associated to a specific device.
       
    42 These device independent font instances cannot be used for drawing. 
       
    43 
       
    44 To get a device font, any font can be sent the message
       
    45 'aFont on:aDevice' which returns an instance of Font which is
       
    46 associated to a device (it returns the receiver, if that is already
       
    47 assiciated to that device). 
       
    48 
       
    49 For proper operation, each graphics operation working with fonts
       
    50 must get a device font before doing the draw.
       
    51  
       
    52 Sometimes, a font cannot be represented on a device, then a replacement
       
    53 font is chosen and kept in the replacementFont instance variable. 
       
    54 
       
    55 Instance variables:
       
    56 
       
    57 family		<String>	the fonts family ('courier', 'helvetica' etc)
       
    58 face		<String>	the fonts face ('bold', 'medium' etc)
       
    59 style		<String>	the fonts style ('roman', 'italic', 'oblique')
       
    60 size		<String>	the fonts size (not in pixels) 
       
    61 encoding	<Symbol>	the fonts encoding (usually #iso8859)
       
    62 
       
    63 device		<Object>	the device the font is associated to, or nil
       
    64 fontId		<Object>	the id of the font on that device, or nil
       
    65 replacement	<Font>		the replacement font or nil
       
    66 
       
    67 ascent		<Integer>	the fonts ascent in device units on device
       
    68 descent		<Integer>	the fonts descent in device units on device
       
    69 height		<Integer>	the fonts height in device units on device
       
    70 width		<Integer>	the character width in device units on device
       
    71 				(for variable fonts, its the width of a space)
       
    72 isFixedWidth	<Boolean>	true if font is a fixed width font
       
    73 minWidth	<Integer>	width of the smallest-width character in
       
    74 				in device units on device
       
    75 maxWidth	<Integer>	width of the largest-width character in
       
    76 				in device units on device
       
    77 
       
    78 class variables:
       
    79 
       
    80 lobby		<Registry>	keeps track of all known fonts
       
    81 
       
    82 Replacements	<Dictionary>	replacement fonts
       
    83     "
       
    84 ! !
       
    85 
       
    86 !Font class methodsFor:'initialization'!
       
    87 
       
    88 initialize
       
    89     "initialize the font tracking array"
       
    90 
       
    91     lobby isNil ifTrue:[
       
    92         lobby := Registry new.
       
    93 
       
    94         "want to be informed when returning from snapshot"
       
    95         ObjectMemory addDependent:self.
       
    96 
       
    97 	Replacements := Dictionary new.
       
    98 
       
    99 	Replacements at:'clean'                  put:'courier'.
       
   100 	Replacements at:'fixed'                  put:'courier'.
       
   101 	Replacements at:'new century schoolbook' put:'times'.
       
   102 	Replacements at:'lucida'                 put:'helvetica'.
       
   103 	Replacements at:'lucidabright'           put:'helvetica'.
       
   104 	Replacements at:'lucidatypewriter'       put:'courier'.
       
   105 	Replacements at:'charter'                put:'times'.
       
   106 	Replacements at:'terminal'               put:'courier'.
       
   107     ]
       
   108 !
       
   109 
       
   110 flushDeviceFonts
       
   111     "unassign all fonts from their device"
       
   112 
       
   113     lobby contentsDo:[:aFont |
       
   114         aFont resetDevice.
       
   115         lobby changed:aFont
       
   116     ]
       
   117 !
       
   118 
       
   119 update:something
       
   120     (something == #restarted) ifTrue:[
       
   121         self flushDeviceFonts
       
   122     ]
       
   123 ! !
       
   124 
       
   125 !Font class methodsFor:'instance creation'!
       
   126 
       
   127 family:familyString face:faceString style:styleString size:sizeNum
       
   128     "returns a font for given family, face, style and size. 
       
   129      The returned font is not associated to s specific device"
       
   130 
       
   131     ^ self family:familyString face:faceString style:styleString size:sizeNum encoding:#iso8859
       
   132 !
       
   133 
       
   134 family:familyString face:faceString style:styleString size:sizeNum encoding:encodingSym
       
   135     "returns a font for given family, face, style, size and encoding. 
       
   136      The returned font is not associated to s specific device"
       
   137 
       
   138     |family  "<String>"
       
   139      newFont "<Font>" |
       
   140 
       
   141     (familyString at:1) isUppercase ifTrue:[
       
   142         family := familyString asLowercase
       
   143     ] ifFalse:[
       
   144         family := familyString
       
   145     ].
       
   146 
       
   147     "look if this font is already known"
       
   148 
       
   149     lobby contentsDo:[:aFont |
       
   150         (aFont family = family) ifTrue:[
       
   151             (aFont face = faceString) ifTrue:[
       
   152                 (aFont style = styleString) ifTrue:[
       
   153                      (aFont size == sizeNum) ifTrue:[
       
   154                         (encodingSym isNil or:[aFont encoding == encodingSym]) ifTrue:[
       
   155                             ^ aFont
       
   156                         ]
       
   157                     ]
       
   158                 ]
       
   159             ]
       
   160         ]
       
   161     ].
       
   162     newFont := self basicNew setFamily:familyString face:faceString
       
   163                                  style:styleString size:sizeNum encoding:encodingSym device:nil.
       
   164     lobby register:newFont.
       
   165     ^ newFont
       
   166 ! !
       
   167 
       
   168 !Font methodsFor:'instance release'!
       
   169 
       
   170 disposed
       
   171     "some Font has been collected - tell it to the x-server"
       
   172 
       
   173     fontId notNil ifTrue:[
       
   174         device releaseFont:fontId.
       
   175     ]
       
   176 ! !
       
   177 
       
   178 !Font methodsFor:'getting a device font'!
       
   179 
       
   180 on:aDevice
       
   181     "create a new Font representing the same font as
       
   182      myself on aDevice; if one already exists, return the one."
       
   183 
       
   184     |newFont index id rep|
       
   185 
       
   186     "if I am already assigned to that device ..."
       
   187     (device == aDevice) ifTrue:[^ self].
       
   188 
       
   189     "first look if not already there"
       
   190     lobby contentsDo:[:aFont |
       
   191         (aDevice == aFont device) ifTrue:[
       
   192             (size == aFont size) ifTrue:[
       
   193                 (family = aFont family) ifTrue:[
       
   194                     (face = aFont face) ifTrue:[
       
   195                         (style = aFont style) ifTrue:[
       
   196                             (encoding == aFont encoding) ifTrue:[
       
   197                                 ^ aFont
       
   198                             ]
       
   199                         ]
       
   200                     ]
       
   201                 ]
       
   202             ]
       
   203         ]
       
   204     ].
       
   205 
       
   206     "ask that device for the font"
       
   207     id := aDevice getFontWithFamily:family face:face style:style size:size encoding:encoding.
       
   208     id isNil ifTrue:[
       
   209         "oops did not work - (device has no such font)"
       
   210 
       
   211 	rep := self replacementFontOn:aDevice.
       
   212 	device isNil ifTrue:[
       
   213 	    device := aDevice.
       
   214 	    replacementFont := rep.
       
   215 	    lobby changed:self.
       
   216 	    ^ self
       
   217 	].
       
   218 	newFont := (self class basicNew)
       
   219                      setFamily:family face:face style:style size:size encoding:encoding device:aDevice.
       
   220 	newFont setReplacementFont:rep.
       
   221         lobby register:newFont.
       
   222 	^ newFont
       
   223     ].
       
   224 
       
   225     "receiver was not associated - do it now"
       
   226     device isNil ifTrue:[
       
   227         device := aDevice.
       
   228         fontId := id.
       
   229 
       
   230         self getFontInfos.
       
   231         lobby changed:self.
       
   232         ^ self
       
   233     ].
       
   234 
       
   235     "receiver was already associated to another device - need a new font"
       
   236     newFont := (self class basicNew)
       
   237                      setFamily:family face:face style:style size:size encoding:encoding device:aDevice.
       
   238     newFont setFontId:id.
       
   239     newFont getFontInfos.
       
   240     lobby register:newFont.
       
   241     ^ newFont
       
   242 !
       
   243 
       
   244 replacementFontOn:aDevice
       
   245     "return a replacement font for the receiver"
       
   246 
       
   247     "currently, all are mapped to the devices defaultFont,
       
   248      but could do much more here (map all fixed fonts to courier,
       
   249      all serif fonts to times and non-serif fonts to helvetica for example"
       
   250 
       
   251     |id f alternative|
       
   252 
       
   253     alternative := Replacements at:family.
       
   254     alternative notNil ifTrue:[
       
   255         id := aDevice getFontWithFamily:alternative face:face style:style size:size encoding:encoding.
       
   256 	id notNil ifTrue:[
       
   257 	    ('replaced ' , family , '- with ' , alternative , '-font') print.
       
   258 	] ifFalse:[
       
   259             id := aDevice getDefaultFont.
       
   260 	    ('replaced ' , family , '- with default-font') print.
       
   261 	]
       
   262     ].
       
   263     id isNil ifTrue:[
       
   264         "oops did not work - this is a serious an error"
       
   265         self error:'cannot get default font'.
       
   266         ^ nil
       
   267     ].
       
   268     f := self class basicNew.
       
   269     f setDevice:aDevice fontId:id.
       
   270     f getFontInfos.
       
   271     lobby register:f.
       
   272     ^ f
       
   273 ! !
       
   274 
       
   275 !Font methodsFor:'instance creation'!
       
   276 
       
   277 bold
       
   278     "return the bold font corresponding to the receiver"
       
   279 
       
   280     ^ self class family:family face:'bold' style:style size:size encoding:encoding
       
   281 ! !
       
   282 
       
   283 !Font methodsFor:'private'!
       
   284 
       
   285 setFamily:familyString face:faceString style:styleString size:sizeNum encoding:encodingSym device:aDevice
       
   286     family := familyString.
       
   287     face := faceString.
       
   288     style := styleString.
       
   289     size := sizeNum.
       
   290     encoding := encodingSym.
       
   291     device := aDevice
       
   292 !
       
   293 
       
   294 resetDevice
       
   295     device := nil.
       
   296     fontId := nil.
       
   297     replacementFont := nil
       
   298 !
       
   299 
       
   300 setReplacementFont:aFont
       
   301     replacementFont := aFont
       
   302 !
       
   303 
       
   304 setDevice:aDevice
       
   305     device := aDevice
       
   306 !
       
   307 
       
   308 setFontId:aFontId
       
   309     fontId := aFontId
       
   310 !
       
   311 
       
   312 setDevice:aDevice fontId:aFontId
       
   313     device := aDevice.
       
   314     fontId := aFontId
       
   315 !
       
   316 
       
   317 getFontInfos
       
   318     replacementFont isNil ifTrue:[
       
   319         ascent := device ascentOf:fontId.
       
   320         descent := device descentOf:fontId.
       
   321         height := descent + ascent.
       
   322         width := device widthOf:' ' inFont:fontId.
       
   323 	minWidth := device minWidthOfFont:fontId.
       
   324 	maxWidth := device maxWidthOfFont:fontId.
       
   325     ] ifFalse:[
       
   326         ascent := replacementFont ascent.
       
   327         descent := replacementFont descent.
       
   328         height := descent + ascent.
       
   329         width := replacementFont width.
       
   330         minWidth := replacementFont minWidth.
       
   331         maxWidth := replacementFont maxWidth.
       
   332     ].
       
   333     isFixedWidth := minWidth == maxWidth
       
   334 ! !
       
   335 
       
   336 !Font methodsFor:'comparing'!
       
   337 
       
   338 = aFont
       
   339     "two fonts are considered equal, if the font-name components are;
       
   340      independent of the device, the font is on"
       
   341 
       
   342     (aFont isKindOf:Font) ifTrue:[
       
   343         (size == aFont size) ifTrue:[
       
   344             (family = aFont family) ifTrue:[
       
   345                 (face = aFont face) ifTrue:[
       
   346                     (style = aFont style) ifTrue:[
       
   347                         (encoding == aFont encoding) ifTrue:[
       
   348                             ^ true
       
   349                         ]
       
   350                     ]
       
   351                 ]
       
   352             ]
       
   353         ]
       
   354     ].
       
   355     ^ false
       
   356 ! !
       
   357 
       
   358 !Font methodsFor:'accessing'!
       
   359 
       
   360 family
       
   361     "return the family, a string"
       
   362 
       
   363     ^ family
       
   364 !
       
   365 
       
   366 face
       
   367     "return the face, a string"
       
   368 
       
   369     ^ face
       
   370 !
       
   371 
       
   372 style
       
   373     "return the style, a string"
       
   374 
       
   375     ^ style
       
   376 !
       
   377 
       
   378 size
       
   379     "return the size, a number"
       
   380 
       
   381     ^ size
       
   382 !
       
   383 
       
   384 encoding
       
   385     "return the encoding, a symbol such as #iso8859"
       
   386 
       
   387     ^ encoding
       
   388 !
       
   389 
       
   390 fontId
       
   391     "return the device-dependent font-id"
       
   392 
       
   393     ^ fontId
       
   394 !
       
   395 
       
   396 device
       
   397     "return the device I am on"
       
   398 
       
   399     ^ device
       
   400 ! !
       
   401 
       
   402 !Font methodsFor:'errors'!
       
   403 
       
   404 errorNoDevice
       
   405     "a query was made for device-specific info"
       
   406 
       
   407     self error:'query device independent font for for device specific info'
       
   408 ! !
       
   409 
       
   410 !Font methodsFor:'queries'!
       
   411 
       
   412 isFixedWidth
       
   413     "return true, if all characters have same width (as in courier"
       
   414 
       
   415     device isNil ifTrue:[
       
   416         self errorNoDevice
       
   417     ].
       
   418     ^ isFixedWidth
       
   419 !
       
   420 
       
   421 height
       
   422     "return the characters maximum height;
       
   423      That is the number of units (usually pixels) on the device"
       
   424 
       
   425     device isNil ifTrue:[
       
   426         self errorNoDevice
       
   427     ].
       
   428     ^ height
       
   429 !
       
   430 
       
   431 width
       
   432     "return the characters width;
       
   433      That is the number of units (usually pixels) on the device.
       
   434      For variable pitch fonts, the width of the space character is returned"
       
   435 
       
   436     device isNil ifTrue:[
       
   437         self errorNoDevice
       
   438     ].
       
   439     ^ width
       
   440 !
       
   441 
       
   442 minWidth
       
   443     "return the width of the smallest character;
       
   444      if the receiver is a fixed width font its the width of every character"
       
   445 
       
   446     device isNil ifTrue:[
       
   447         self errorNoDevice
       
   448     ].
       
   449     ^ minWidth
       
   450 !
       
   451 
       
   452 maxWidth
       
   453     "return the width of the widest character;
       
   454      if the receiver is a fixed width font its the width of every character"
       
   455 
       
   456     device isNil ifTrue:[
       
   457         self errorNoDevice
       
   458     ].
       
   459     ^ maxWidth
       
   460 !
       
   461 
       
   462 ascent
       
   463     "return the font-ascent i.e. the maximum of all characters;
       
   464      That is the number of units (usually pixels) above the baseline"
       
   465 
       
   466     device isNil ifTrue:[
       
   467         self errorNoDevice
       
   468     ].
       
   469     ^ ascent
       
   470 !
       
   471 
       
   472 descent
       
   473     "return the font-descent i.e. the maximum of all characters;
       
   474      That is the number of units (usually pixels) below the baseline"
       
   475 
       
   476     device isNil ifTrue:[
       
   477         self errorNoDevice
       
   478     ].
       
   479     ^ descent
       
   480 !
       
   481 
       
   482 widthOf:aStringOrText
       
   483     "return the width (device specific) of the argument;
       
   484      the argument may be a String or some Text;
       
   485      in the later case the width of the longest line in the text is returned"
       
   486 
       
   487     |this max|
       
   488 
       
   489     device isNil ifTrue:[
       
   490         self errorNoDevice.
       
   491         ^ 0
       
   492     ].
       
   493     replacementFont notNil ifTrue:[
       
   494 	^ replacementFont widthOf:aStringOrText
       
   495     ].
       
   496 
       
   497     (aStringOrText isMemberOf:String) ifTrue:[
       
   498         isFixedWidth ifFalse:[
       
   499             ^ device widthOf:aStringOrText inFont:fontId
       
   500         ].
       
   501         ^ width * aStringOrText size
       
   502     ].
       
   503 
       
   504     max := 0.
       
   505     isFixedWidth ifFalse:[
       
   506         aStringOrText do:[:line |
       
   507             line notNil ifTrue:[
       
   508                 this := device widthOf:line inFont:fontId.
       
   509                 (this > max) ifTrue:[max := this]
       
   510             ]
       
   511         ].
       
   512         ^ max
       
   513     ].
       
   514 
       
   515     aStringOrText do:[:lineString |
       
   516         this := lineString size.
       
   517         (this > max) ifTrue:[max := this]
       
   518     ].
       
   519     ^ max * width
       
   520 !
       
   521 
       
   522 widthOf:aString from:start to:stop
       
   523     "return the width of a substring"
       
   524 
       
   525     device isNil ifTrue:[
       
   526         self errorNoDevice.
       
   527         ^ 0
       
   528     ].
       
   529     replacementFont notNil ifTrue:[
       
   530 	^ replacementFont widthOf:aString from:start to:stop
       
   531     ].
       
   532     (stop < start) ifTrue:[^ 0].
       
   533     isFixedWidth ifFalse:[
       
   534         ^ device widthOf:aString from:start to:stop inFont:fontId
       
   535     ].
       
   536     ^ (stop - start + 1) * width
       
   537 ! !
       
   538 
       
   539 !Font methodsFor:'printing & storing'!
       
   540 
       
   541 printString
       
   542     ^ 'a ' , family , '-', face, '-', style, '-', size printString, '-Font'
       
   543 !
       
   544 
       
   545 storeString
       
   546     "return a String with a representation of myself, from which I can be
       
   547      recreated"
       
   548 
       
   549     ^ ('(Font family:' , family ,
       
   550        ' face:' , face ,
       
   551        ' style:' , style ,
       
   552        ' size:' , size printString ,
       
   553        ' encoding:' , encoding storeString , ')')
       
   554 ! !