|
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 ! ! |