summaryrefslogtreecommitdiff
path: root/fonts/utilities/t1install/toil/toilb.awk
blob: f3fd8a1ca1ee32c2ed27f982f726b7f8064df441 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#This file belongs to the TOIL package : Ten plik nale/zy do pakietu TOIL
#This package is public domain         : Pakiet stanowi dobro powszechne
#For more info see `0TOILENG.LIC'      : Wi/ecej informacji w ,,0TOILPOL.LIC''
#============================================================================
# MAIN TASK FILE: generates the MF program which will generate the TFM file
# ==========================================================================
BEGIN {
  B_CHAR_NAME=".boundarychar" # local constant (unimportant, in fact)
  fix_const(); get_tmp_config(TMP_CFG)
# 0: READ AFM FILE:
  RS="(\012|\015|\012\015)"
  getline < CV["AFM"]
  if (!/^StartFontMetrics/) errmess(4, CV["AFM"], "is not an AFM file")
  mess("Converting", CV["AFM"], "to", CV["MF_MF"])
  FS=" *;? *" # convenient FS for parsing AFM files
  while (getline < CV["AFM"] > 0) {
    if ($1=="UnderlineThickness") UnderlineThickness=$2
    if ($1=="FontName") FontName=$2
    if ($1=="XHeight")  XHeight=$2
    if ($1=="ItalicAngle") ItalicAngle=$2
    if ($1 == "C") {
      AFM_C[++AFM_NUM]=round($2)
      AFM_W[AFM_NUM]=round($4)
      AFM_N[AFM_NUM]=$6
      AFM_SET[$6]=AFM_NUM
      XL[AFM_NUM]=round($8)
      YL[AFM_NUM]=round($9)
      XH[AFM_NUM]=round($10)
      YH[AFM_NUM]=round($11)
      AFM_CN[AFM_C[AFM_NUM]]=AFM_N[AFM_NUM]
      if (AFM_C[AFM_NUM]>Cmax) Cmax=AFM_C[AFM_NUM]
      for (i=12; i<=NF; i+=3) if ($i=="L") AFM_L[$6,$(i+1)]=$(i+2)
    }
    if ($1 == "KPX")
      if (($2 in AFM_SET) && ($3 in AFM_SET))
        # warning: a bit of trickery is comming:
        if ((is_digit($2)+is_digit($3))<=CV["USE_DIGIT_KPX"])
          K[$2,$3]=round($4)
  }
  FS=" " # restore standard FS
# 1: ``MATHEMATIZE'' FONT IF REQUIRED (USUALLY REQUIRED):
  if (CV["MATHEMATIZE"]) {
     # The innocent word ``mathematize'' means that
     # 1. All depths (e.g., accents) and heights (e.g., underscore) but
     #    the depth of equal sign are set par force to non-negative values
     #    according to the formula (x<0 ? 0 : x)
     # 2. Math axis is determined in the following way:
     #    (a) if an equals sign exists, the math axis is calculated
     #        on the basis of its bounding box,
     #    (b) if an equals sign does not exist, the math axis is
     #        assumed to be equal 1/4 of the design size of the font.
     # 3. If an equals sign exists, its height and depth are calculated from
     #    the AFM bounding box; it is the only character that can have
     #    a negative depth.
     # 4. The following characters are considered to be math operators:
     #      plus, minus, multiply, divide, plusminus, periodcentered,
     #      less, greater, greaterequal, approxequal, lessequal
     #    All the listed characters are subjected to the following
     #    uniformizing adjustments of their heights and depths:
     #      (a) a plus sign is considered first; if an equal sign is present,
     #          the height and depth of a plus sign are adjusted in such
     #          a way that the resulting bounding box is the smallest one
     #          having the following properties: it contains the AFM bounding
     #          box, depth is non-negative, and, moreover, m+d=h-m, where
     #          m is the height of the math axis, d -- resulting depth,
     #          h -- resulting height; if an equal sign is not present,
     #          plus sign receives merely a non-negative depth (see p. 1);
     #      (b) if a plus sign does not exist, it is considered to have
     #          zero height and depth;
     #      (c) the remaining characters from the list receive height and
     #          depth according to the following formula: h=max(h,h'),
     #          d=min(d,d'), where h' and d' are the height and depth of
     #          a plus sign, and h and d are, obviously, the height
     #          and depth of a given character.
     # 5. Finally, two parameters are fixed MATH_RULE (as equal to
     #    UnderlineThickness (or 2/5pt#, if UnderlineThickness<=0),
     #    and MATH_AXIS, as equal to the value determined in p. 2.
     #    These parameters are written to the TFM file as
     #    fontdimen registers 8 and 22, respectively.
     # COMMENT: the process of ``mathematization'' may seem somewhat
     #          arbitrary and, frankly speaking, it is. ``Though this
     #          be madness, yet there is method in 't'':
     #          * negative depths may manifest their presence only in
     #            math mode, as, according to ``The TeXbook'':
     #               The result of `\hbox' never has negative height
     #               or negative depth [...]
     #          * if accent characters have negative depths, they
     #            behave OK in the text mode, but fall too low
     #            when used as `\mathaccent' commands;
     #          * almost all math operators in CM fonts have uniformized
     #            height and depth basing on that of a plus sign;
     #            an equals sign, however, has different settings;
     #          * `\fontdimen22' of `\fam2' denotes the height of math axis,
     #            moreover, at least 22 `\fontdimen' registers are
     #            necessary for to make TeX accepting a font as
     #            a math one;
     #          * `\fontdimen8' of `\fam3' denotes the default (math) rule
     #            thickness;
    if ("equal" in AFM_SET) {
      math_axis=(YH[AFM_SET["equal"]]+YL[AFM_SET["equal"]])/2
      YL[AFM_SET["equal"]]=-YL[AFM_SET["equal"]]
    }
    MATH_AXIS=((math_axis+0)==0 ? "9/2u#" : "V" math_axis)
    MATH_RULE=(UnderlineThickness>0 ? "V" UnderlineThickness+0 : ".4pt#");
    if ("plus" in AFM_SET) {
      plus_ht=YH[AFM_SET["plus"]]
      plus_dp=nneg(-YL[AFM_SET["plus"]])
      plus_ht=max(plus_ht,math_axis+plus_dp)
    }
    math_op["minus"]=1; math_op["multiply"]=1; math_op["divide"]=1
    math_op["plusminus"]=1; math_op["periodcentered"]=1
    math_op["less"]=1; math_op["greater"]=1; math_op["greaterequal"]=1
    math_op["approxequal"]=1; math_op["lessequal"]=1
    for (m in math_op) if (m in AFM_SET) {
      YH[AFM_SET[m]]=max(YH[AFM_SET[m]],plus_ht)
      YL[AFM_SET[m]]=max(nneg(-YL[AFM_SET[m]]),plus_dp)
    }
    for (n in AFM_SET) if (n!="equal") {
      YH[AFM_SET[n]]=nneg(YH[AFM_SET[n]])
      YL[AFM_SET[n]]=nneg(-YL[AFM_SET[n]])
    }
  } else for (n in AFM_SET) YL[AFM_SET[n]]=-YL[AFM_SET[n]]
# 2: WRITE PREAMBLE
  getline < CV["MF1"] # skip first line
  print "% This is a METAFONT file created by TOIL ver. " CV["VER_NO"] > CV["MF_MF"]
  while (getline < CV["MF1"] > 0) {
    gsub("TONEFILE", QQ CV["PSF_XNAME"] QQ)
    gsub("ENCNNAME", QQ CV["ENC_NAME"] QQ)
    gsub("ENCXNAME", QQ CV["ENC_XNAME"] QQ)
    gsub("TFMNAME",  QQ CV["TFM"] QQ)
    gsub("TONENAME", QQ FontName QQ)
    gsub("DUMMYPPI", CV["PPI"])
    if (CV["MATHEMATIZE"]) {
      gsub("MATHRULE", MATH_RULE)
      gsub("MATHAXIS", MATH_AXIS)
    }
    gsub("MESSAGES", (CV["QUIET"] ? "true" : "false"))
    print > CV["MF_MF"]
  }
  close(CV["MF1"])
# 3: WRITE ENCODING AND ADD (OPTIONALLY) A BOUNDARY CHAR
  enc_kind="AFM-based encoding"
  internal_enc=1
  if ((CV["ENC_ACT"]=="") && (CV["ENC_GVN"]!=""))
    {mess("Encoding file", CV["ENC_GVN"], "not found (ignored)")}
  else if (CV["ENC_ACT"]!="") {
    getline < CV["ENC_ACT"]
    if (!/^[ \t]*EXTERNAL ENCODING FOR TOIL/) {
      mess(CV["ENC_ACT"], "is not an encoding file for TOIL")
      mess("AFM encoding will be used")
    } else {
      mess("Encoding taken from", CV["ENC_ACT"])
      getline enc_kind < CV["ENC_ACT"]; gsub(/ *%.*$/,"",enc_kind)
      for (i in AFM_CN) delete AFM_CN[i]; Cmax=0
      while (getline < CV["ENC_ACT"] > 0)
      if ((/^[^%]/) && ($2 in AFM_SET)) {AFM_CN[$1+0]=$2; if (($1+0)>Cmax) Cmax=$1+0}
      internal_enc=0
    }
    close(CV["ENC_ACT"])
  }
  if (B_CHAR>=0)
    if (B_CHAR in AFM_CN)
      mess("Slot " B_CHAR " occupied; boundary char specification ignored")
    else {
      AFM_C[++AFM_NUM]=B_CHAR
      AFM_W[AFM_NUM]=0
      AFM_N[AFM_NUM]=B_CHAR_NAME
      AFM_SET[B_CHAR_NAME]=AFM_NUM
      XL[AFM_NUM]=0; YL[AFM_NUM]=0; XH[AFM_NUM]=0; YH[AFM_NUM]=0
      AFM_CN[AFM_C[AFM_NUM]]=AFM_N[AFM_NUM]
      if (AFM_C[AFM_NUM]>Cmax) Cmax=AFM_C[AFM_NUM]
      for (every_char in AFM_SET) if (every_char!=B_CHAR_NAME) {
        K[B_CHAR_NAME,every_char]=-XL[AFM_SET[every_char]]
        K[every_char,B_CHAR_NAME]=-AFM_W[AFM_SET[every_char]]+XH[AFM_SET[every_char]]
      }
    }
  print "string enc_kind; enc_kind=" QQ enc_kind QQ ";" > CV["MF_MF"]
  print "internal_enc:=" (internal_enc ? "true" : "false") ";" > CV["MF_MF"]
  print "vardef Enc expr tit =" > CV["MF_MF"]
  first=1
  for (i=0; i<=Cmax; ++i) if (i in AFM_CN) {
    print (first ? " if tit=" : " elseif tit=") QQ AFM_CN[i] QQ ":" i > CV["MF_MF"]
    first=0
  }
  print " else:-1\n fi\nenddef;" > CV["MF_MF"]
# 4: WRITE INTERAMBLE
  getline < CV["MF2"] # skip first line
  while (getline < CV["MF2"] > 0) {
    gsub("XHEIGHT","V" XHeight+0);
    if (ItalicAngle==0) {gsub("SLANT","0")}
    else gsub("SLANT","-sind(" ItalicAngle ")/cosd(" ItalicAngle ")")
    print > CV["MF_MF"]
  }
  close(CV["MF2"])
# 5: WRITE DUMMY CHAR PROGRAMS
  for (i=1; i<=AFM_NUM; ++i) {
    print "char_title", QQ AFM_N[i] QQ ";" > CV["MF_MF"]
    print "beginchar(curr_enc,H" AFM_W[i] ",V" YH[i] ",V" (YL[i]) ");" > CV["MF_MF"]
    print "IC(H" XH[i] ",V" YH[i] "); endchar;" > CV["MF_MF"]
    print "% ---" > CV["MF_MF"]
  }
# 6: CLEAN UP LIGATURES ALREADY READ IN AND READ OPTIONAL LIGATURES
  for (i in AFM_L) {
    split(i,u,SUBSEP) # by definition, (u[1] in AFM_SET) == true
##    if ((!(u[2] in AFM_SET)) || (!(AFM_L[i] in AFM_SET))) delete AFM_L[i]
    if ((!(u[2] in AFM_SET)) || (!(AFM_L[i] in AFM_SET))) AFM_L[i]=""
  }
  if ((CV["LIG_ACT"]=="") && (CV["LIG_GVN"]!="")) {
    mess("Ligature file", CV["LIG_GVN"], "not found (ignored)")
  } else if (CV["LIG_ACT"]!="") {
    getline < CV["LIG_ACT"]
    if (!/^[ \t]*OPTIONAL LIGATURES FOR TOIL/) {
      mess(CV["LIG_ACT"], "is not a ligature file for TOIL")
      mess("Only AFM ligatures will be used")
    } else {
      mess("Ligatures taken from", CV["LIG_ACT"])
      while (getline < CV["LIG_ACT"] > 0)
        if ((/^[^%]/) && ($1 in AFM_SET) && ($2 in AFM_SET) && ($3 in AFM_SET)) {
          if (($1,$2) in AFM_L) if (AFM_L[$1,$2]!=$3)
          {# inform about collision
            mess("  afm ligature  " $1 " " $2 " " AFM_L[$1,$2])
            mess("  new ligature  " $1 " " $2 " " $3)
          }
          AFM_L[$1,$2]=$3
        }
    }
    close(CV["LIG_ACT"])
  }
# 7: WRITE LIGTABLE
# 6a: write ligatures and kerns
##  for (i in AFM_L) {
  for (i in AFM_L) if (AFM_L[i]!="") {
    split(i,u,SUBSEP)
    print "LK(" QQ u[1] QQ ")" > CV["MF_MF"]
    print " LP(" QQ u[2] QQ "," QQ AFM_L[i] QQ ")" > CV["MF_MF"]
##    delete AFM_L[i]
    AFM_L[i]=""
##    for (j in AFM_L) {
    for (j in AFM_L) if (AFM_L[j]!="") {
      split(j,v,SUBSEP)
      if (v[1]==u[1]) {
        print " LP(" QQ v[2] QQ "," QQ AFM_L[j] QQ ")" > CV["MF_MF"]
##        delete AFM_L[j]
        AFM_L[j]=""
      }
    }
##    for (j in K) {
    for (j in K) if (K[j]!="") {
      split(j,v,SUBSEP)
      if (v[1]==u[1]) {
        print " KP(" QQ v[2] QQ ",H" K[j] ")" > CV["MF_MF"]
##        delete K[j]
        K[j]=""
      }
    }
    print "KL;" > CV["MF_MF"]
  }
# 6b: write remaining kerns
##  for (i in K) {
  for (i in K) if (K[i]!="") {
    split(i,u,SUBSEP)
    print "LK(" QQ u[1] QQ ")" > CV["MF_MF"]
    print " KP(" QQ u[2] QQ ",H" K[i] ")" > CV["MF_MF"]
##    delete K[i]
    K[i]=""
    for (j in K) if (K[j]!="") {
      split(j,v,SUBSEP)
      if (v[1]==u[1]) {
        print " KP(" QQ v[2] QQ ",H" K[j] ")" > CV["MF_MF"]
##        delete K[j]
        K[j]=""
      }
    }
    print "KL;" > CV["MF_MF"]
  }
# 8: WRITE POSTAMBLE
  getline < CV["MF3"] # skip first line
  while (getline < CV["MF3"] > 0) print > CV["MF_MF"]
  close(CV["MF3"])
# 9: SAY BYE
  if ((!CV["QUIET"]) && (LOG!="")) print "Transcript written on " LOG > "CON"
}

# ==========================================================================

function max(x,y) {return (x>y ? x : y)}
function abs(x) {return (x>0 ? x : -x)}
function nneg(x) {return max(x,0)}
function round(x) {return (x>0 ? int(x+.5) : -int(-x+.5))}

function is_digit(n) {# let's be explicit here; it suffices that it is used
                      # in a tricky way...
  return (n~/^(zero|one|two|three|four|five|six|seven|eight|nine)$/?1:0)
}

function errmess(e,s1,s2,s3) {# at most three parts may occur
  mess("TOIL ERROR:",s1, s2, s3); exit(e)
}

function mess(s1,s2,s3,s4, s) {# at most four parts may occur
  s=s1; if (s2!="") s=s " " s2; if (s3!="") s=s " " s3; if (s4!="") s=s " " s4
  if (length(s)<=max_line) {
    if (!CV["QUIET"]) print s > "CON"; if (LOG!="") print s > LOG
  } else {
    if ((!CV["QUIET"]) && (s1!="")) print s1 > "CON"
    if ((LOG!="") && (s1!="")) print s1 > LOG
    if ((!CV["QUIET"]) && (s2!="")) print s2 > "CON"
    if ((LOG!="") && (s2!="")) print s2 > LOG
    if ((!CV["QUIET"]) && (s3!="")) print s3 > "CON"
    if ((LOG!="") && (s3!="")) print s3 > LOG
    if ((!CV["QUIET"]) && (s4!="")) print s4 > "CON"
    if ((LOG!="") && (s4!="")) print s4 > LOG
  }
}

function get_tmp_cfg_item(item_name, item) {
  CV[$1]=$0; gsub("^" $1 " +", "", CV[$1]); 
}

function get_tmp_config (TMP_CFG, v) {
  while (getline < TMP_CFG > 0) get_tmp_cfg_item()
#  for (v in CV) printf("%20s %s\n", v, CV[v])
  TMP_TMP=CV["TMP_TMP"]
  TMP_ENC=CV["TMP_ENC"]
  TMP_MAP=CV["TMP_MAP"]
  B_CHAR=CV["B_CHAR"]+0
}

function fix_const() {
# GENERAL CONSTANTS (used also in toila.awk and toilc.awk)
  QQ="\""
  max_line=79      # screen limit
  max_sys_line=127 # MSDOS limit
  TMP_CFG="~t~m~p~.cfg"
}