home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
historic
/
v941.tgz
/
icon.v941src.tar
/
icon.v941src
/
ipl
/
gprocs
/
xbfont.icn
< prev
next >
Wrap
Text File
|
2001-05-02
|
11KB
|
323 lines
############################################################################
#
# File: xbfont.icn
#
# Subject: Procedures for X font selection
#
# Author: Gregg M. Townsend
#
# Date: May 2, 2001
#
############################################################################
#
# This file is in the public domain.
#
############################################################################
#
# BestFont(W, s, ...) generates X-windows font names matching a
# given specification, beginning with the closest match. The
# ranking algorithm is similar to that used in Font() but it is
# not identical.
#
############################################################################
#
# BestFont(window, spec, ...) returns the name of whichever available
# X-Windows font most closely matches the given specification. Note that
# matching is done using a slightly different algorithm from that of the
# Icon runtime system; this procedure preceded Icon's font selection
# implementation and served as a prototype.
#
# The font specification is one or more strings containing whitespace-
# or comma-separated tokens. Tokens are case-insensitive. There are
# three kinds of tokens.
# A token having the form of an integer specifies the desired "pixel
# size" (height). If no size is included, a target size of 14 is used.
# An unrecognized token is taken as a substring of the desired X font
# name. Family names, weights, and other such factors are specified this
# way.
# Certain tokens are recognized and handled specially:
# m mono monospaced
# p prop proportional
# r roman
# i italic
# o oblique
# s sans sans-serif sansserif
# These are turned into search strings of a particular form. For example,
# "roman" and "r" specify the search string "-r-".
#
# The "best match" to a given specification is calculated by reviewing
# all the available fonts, assigning a score to each, then choosing the
# one with the highest value. There are several aspects of scoring.
# Size is the most important factor. A tuned font of the correct size
# gets the maximum score. Nearby sizes receive partial credit, with
# an undersized font preferred over an oversized font. Scalable fonts
# are also recognized, but a tuned font of the correct or nearly-correct
# size gets a higher score.
# Each successful substring match increases the score, whether the
# test string comes from an unrecognized token or a special keyword.
# Earlier tokens receive slightly more weight than later ones.
# All tokens need not match. The string "lucida gill sans 18"
# is perfectly reasonable; it specifies a preference for Lucida Sans
# over Gill Sans by the position of the tokens, but will match either.
# Ties are broken by giving slight preferences for normal weight,
# no slant, normal width, and ASCII ("iso8859") encoding. A slight
# penalty is assessed for "typewriter" fonts. Oblique fonts receive
# partial credit for matching "italic" requests, and vice versa.
# The scoring function can be altered by assigning values to certain
# global variables. See XBF_defaults() for a commented list of these.
#
# For a scalable font, the returned value is a string specifying an
# instance of the font scaled to the target size. For large sizes, the
# scaling time may be noticeable when the font is used.
#
# BestFont() is actually a generator that produces the entire list
# of available fonts in order of preference. RankFonts(w, spec, ...)
# is similar to BestFont but produces a sequence of two-element records,
# where result.str is the font name and result.val is its score. For
# either of these, a list of X font names can be passed instead of a
# window.
#
# There is some startup cost the first time BestFont is called; it
# opens a pipe to the "xlsfonts" program and reads the output. Results
# are cached, so this overhead is only incurred once.
#
# Examples:
# Font(w, BestFont(w, "times bold italic 20"))
# s := BestFont(w, size, family, "italic")
#
############################################################################
#
# Requires: Version 9 graphics under Unix
#
############################################################################
record XBF_rec(str, val)
global XBF_wantsize # requested font size
global XBF_sizval # array of scores indexed by actual font size
# globals used for tuning the scoring function; see XBF_defaults()
global XFW_defsize, XFW_size, XFW_maxover, XFW_maxunder, XFW_scaled
global XFW_spacing, XFW_slant, XFW_aslant, XFW_sans
global XFW_default, XFW_exact, XFW_posn, XFW_tiebreakers
# BestFont(window, spec...) - generate ranked sequence of font names
procedure BestFont(args[]) #: generate best X fonts
suspend (RankFonts ! args) . str
end
# XRankFont(window, spec...) - generate sequence of (name,score) tuples
procedure RankFonts(w, args[]) #: generate scores for X fonts
local tokens, cklist, sclist, fspec, ranks, r
if type(w) ~== "window" & type(w) ~== "list" then {
push(args, w)
w := &window
}
XBF_defaults() # set default values
XBF_wantsize := XFW_defsize # set target size to default
tokens := XBF_tokenlist(args) # break args into list of tokens
cklist := XBF_weights(tokens) # get list of (substring,weight)s
XBF_sizval := XBF_sizes(XBF_wantsize) # build array for scoring sizes
# make a list of (fontname,score) tuples, and sort it
sclist := []
every fspec := XBF_fontlist(w) do
put(sclist, XBF_rec(fspec, XBF_eval(fspec, cklist)))
ranks := sortf(sclist, 2)
# generate results from hightest to lowest rank
while r := pull(ranks) do
suspend XBF_rec(XBF_spec(r.str, XBF_wantsize), r.val)
end
# XBF_defaults() - assign default values to any unset tuning parameters
procedure XBF_defaults()
/XFW_defsize := 14 # default size if unspecified
/XFW_size := 1000 # points for matching size exactly
/XFW_maxover := 30 # max allowable overage on size (per cent)
/XFW_maxunder := 60 # max allowable shortfall on size (per cent)
/XFW_scaled := 800 # points for matching size with scaled font
/XFW_spacing := 500 # points for matching prop/mono spacing
/XFW_slant := 500 # points for matching slant
/XFW_aslant := 300 # points for approx slant (oblique : italic)
/XFW_sans := 500 # points for matching "sans" spec
/XFW_exact := 1100 # points for matching entire font name
/XFW_default := 500 # points for matching unrecognized token
/XFW_posn := 10 # points for position in request list
/XFW_tiebreakers := [ # "tiebreaker" strings always scored
XBF_rec("-normal-", 1), # prefer normal width
XBF_rec("-medium-", 1), # prefer medium weight
XBF_rec("-r-", 2), # upright slant is even more important
XBF_rec("-iso8859-", 1), # prefer ASCII, not symbol/kana/etc
XBF_rec("typewriter", -4)] # penalize typewriter fonts
return
end
# XBF_tokenlist(args) -- turn list of args into list of tokens
procedure XBF_tokenlist(args)
local tokens
tokens := []
every map(trim(!args)) ? repeat {
tab(many(' \t,'))
if pos(0) then
break
put(tokens, tab(upto(' \t,') | 0))
}
return tokens
end
# XBF_weights(tokens) -- turn tokens into list of substrings and weights
#
# Also saves the size value in the global XBF_wantsize.
procedure XBF_weights(tokens)
local cklist, tk, pf
cklist := []
pf := *tokens * XFW_posn
every tk := !tokens do {
if not (XBF_wantsize := integer(tk)) then {
pf -:= XFW_posn
case tk of {
"m" | "mono" | "monospaced":
every put(cklist, XBF_rec("-m-" | "-c-", XFW_spacing + pf))
"p" | "prop" | "proportional":
put(cklist, XBF_rec("-p-", XFW_spacing + pf))
"r" | "roman":
put(cklist, XBF_rec("-r-", XFW_slant + pf))
"i" | "italic": {
put(cklist, XBF_rec("-i-", XFW_slant + pf))
put(cklist, XBF_rec("-o-", XFW_aslant + pf))
}
"o" | "oblique": {
put(cklist, XBF_rec("-o-", XFW_slant + pf))
put(cklist, XBF_rec("-i-", XFW_aslant + pf))
}
"s" | "sans" | "sans-serif" | "sansserif":
put(cklist, XBF_rec("sans", XFW_sans + pf))
default:
put(cklist, XBF_rec(tk, XFW_default + pf))
}
}
}
every put(cklist, !XFW_tiebreakers)
return cklist
end
# XBF_sizes(wantsize) -- build array of scores for evaluating font sizes
procedure XBF_sizes(wantsize)
local l, sz, diff, score, maxunder, maxover
l := [XFW_scaled] # initial entry scores scaled fonts
# set scores for undersized fonts
maxunder := (XFW_maxunder / 100.0) * wantsize
every sz := 1 to wantsize-1 do {
diff := wantsize - sz
score := integer(XFW_size * (1 - diff / maxunder))
score <:= 0
put(l, score)
}
# set scores for correct and oversized fonts
maxover := (XFW_maxover / 100.0) * wantsize
repeat {
sz +:= 1
diff := sz - wantsize
score := integer(XFW_size * (1 - diff / maxover))
if score <= 0 then
break # quit when too big to be useful
put(l, score)
}
return l
end
# XBF_fontlist(w) - generate list of font names for window (or list) w
procedure XBF_fontlist(w)
static fontlist
local pipe
if type(w) == "list" then
suspend !w
else {
if /fontlist then {
fontlist := []
pipe := open("xlsfonts", "rp") | stop("can't open xlsfonts pipe")
while put(fontlist, trim(read(pipe)))
close(pipe)
}
suspend !fontlist
}
end
# XBF_eval(fontname, cklist) -- evaluate the score of an X font name
procedure XBF_eval(fontname, cklist)
local t, r
# find the size and look up its score in the XBF_sizval array
fontname ? {
every 1 to 7 do
tab(upto('-')) & move(1)
t := XBF_sizval [1 + integer(tab(upto('-')))] | 0
}
# add the corresponding value for every substring that matches
every r := !cklist do
if find(r.str, fontname) then
if r.str == fontname then
t +:= XFW_exact # high score for matching entire name
else
t +:= r.val # else give specified value
return t
end
# XBF_spec(fontname, size) -- return the correct form of an X font name
#
# This is just the name itself except in the case of scalable fonts.
procedure XBF_spec(fontname, size)
local s
fontname ? {
s := tab(find("-0-0-")) | return fontname # return if not scalable
move(5) # skip pixel size, point size
tab(upto('-')) & move(1) # skip x-resolution
tab(upto('-')) & move(1) # skip y-resolution
s ||:= "-"
s ||:= size # spec pixel size
s ||:= "-*-*-*-" # wildcard ptsize & resolutions
s ||:= tab(upto('-')) # copy spacing field
s ||:= move(1)
tab(upto('-')) # skip average width
s ||:= "*"
s ||:= tab(0) # copy the rest
}
return s
end