rfil 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +340 -0
- data/README +77 -0
- data/examples/afm2tfm.rb +204 -0
- data/examples/afminfo +305 -0
- data/examples/encodingtable +65 -0
- data/examples/pldiff +295 -0
- data/examples/plinfo +108 -0
- data/examples/rfii +257 -0
- data/examples/rfont +188 -0
- data/lib/rfil/font.rb +722 -0
- data/lib/rfil/font/afm.rb +414 -0
- data/lib/rfil/font/glyph.rb +198 -0
- data/lib/rfil/font/metric.rb +135 -0
- data/lib/rfil/font/truetype.rb +35 -0
- data/lib/rfil/fontcollection.rb +182 -0
- data/lib/rfil/helper.rb +155 -0
- data/lib/rfil/rfi.rb +472 -0
- data/lib/rfil/rfi_plugin_context.rb +90 -0
- data/lib/rfil/rfi_plugin_latex.rb +95 -0
- data/lib/rfil/version.rb +3 -0
- data/lib/tex/enc.rb +223 -0
- data/lib/tex/kpathsea.rb +63 -0
- data/lib/tex/tfm.rb +1198 -0
- data/lib/tex/vf.rb +846 -0
- metadata +86 -0
data/lib/tex/tfm.rb
ADDED
@@ -0,0 +1,1198 @@
|
|
1
|
+
# tfm.rb - Access information of a TeX font metric file.
|
2
|
+
#--
|
3
|
+
# Last Change: Tue May 16 19:12:26 2006
|
4
|
+
#++
|
5
|
+
|
6
|
+
module TeX # :nodoc:
|
7
|
+
|
8
|
+
# TFM (TeX font metric) reader/writer class
|
9
|
+
class TFM
|
10
|
+
class TFMReader
|
11
|
+
# reading a tfm file is about 10 times faster than doing
|
12
|
+
# `tftop xyz.pl` and using PL#parse. And only a bit slower than
|
13
|
+
# `tftop xyz.pl > /dev/null` alone. (1.3 secs. vs. 0.9 secs. - 10 times)
|
14
|
+
|
15
|
+
# Output more information
|
16
|
+
attr_accessor :verbose
|
17
|
+
|
18
|
+
LIGTAG=1
|
19
|
+
STOPFLAG=128
|
20
|
+
KERNFLAG=128
|
21
|
+
LIGSIZE=5000
|
22
|
+
class TFMError < Exception
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(tfmobject=nil)
|
26
|
+
# type of font: textfont (:vanilla), math symbols (:mathsy), math
|
27
|
+
# extension (:mathex)
|
28
|
+
@font_type=nil
|
29
|
+
|
30
|
+
@perfect=true
|
31
|
+
|
32
|
+
# this is where we store all our data
|
33
|
+
@tfm=if tfmobject
|
34
|
+
tfmobject
|
35
|
+
else
|
36
|
+
TFM.new
|
37
|
+
end
|
38
|
+
end # initialize
|
39
|
+
|
40
|
+
# _tfmdata_ is a string with the contents of the tfm (binary) file.
|
41
|
+
def parse(tfmdata)
|
42
|
+
@tfmdata=tfmdata.unpack("C*")
|
43
|
+
@index=0
|
44
|
+
|
45
|
+
@lf=get_dbyte
|
46
|
+
@lh=get_dbyte
|
47
|
+
@bc=get_dbyte
|
48
|
+
@ec=get_dbyte
|
49
|
+
@nw=get_dbyte
|
50
|
+
@nh=get_dbyte
|
51
|
+
@nd=get_dbyte
|
52
|
+
@ni=get_dbyte
|
53
|
+
@nl=get_dbyte
|
54
|
+
@nk=get_dbyte
|
55
|
+
@ne=get_dbyte
|
56
|
+
@np=get_dbyte
|
57
|
+
|
58
|
+
if @verbose
|
59
|
+
puts "lf=#{@lf}"
|
60
|
+
puts "lh=#{@lh}"
|
61
|
+
puts "bc=#{@bc}"
|
62
|
+
puts "ec=#{@ec}"
|
63
|
+
puts "nw=#{@nw}"
|
64
|
+
puts "nh=#{@nh}"
|
65
|
+
puts "nd=#{@nd}"
|
66
|
+
puts "ni=#{@ni}"
|
67
|
+
puts "nl=#{@nl}"
|
68
|
+
puts "nk=#{@nk}"
|
69
|
+
puts "ne=#{@ne}"
|
70
|
+
puts "np=#{@np}"
|
71
|
+
end
|
72
|
+
raise TFMError, "The following condition is not true: bc-1 <= ec and ec <= 255" unless @bc-1 <= @ec and @ec <= 255
|
73
|
+
raise TFMError, "The following condition is not true: ne <= 256" unless @ne <= 256
|
74
|
+
raise TFMError, "The following condition is not true: lf == 6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np" unless @lf == 6+@lh+(@ec-@bc+1)+@nw+@nh+@nd+@ni+@nl+@nk+@ne+@np
|
75
|
+
|
76
|
+
# � 23
|
77
|
+
@header_base = 6
|
78
|
+
@char_base = @header_base + @lh
|
79
|
+
@width_base = @char_base + (@ec - @bc) + 1
|
80
|
+
@height_base = @width_base + @nw
|
81
|
+
@depth_base = @height_base + @nh
|
82
|
+
@italic_base = @depth_base + @nd
|
83
|
+
@lig_kern_base = @italic_base + @ni
|
84
|
+
@kern_base = @lig_kern_base + @nl
|
85
|
+
@exten_base = @kern_base + @nk
|
86
|
+
@param_base = @exten_base + @ne
|
87
|
+
|
88
|
+
parse_header
|
89
|
+
parse_params
|
90
|
+
parse_char_info
|
91
|
+
parse_lig_kern
|
92
|
+
# exten?
|
93
|
+
|
94
|
+
return @tfm
|
95
|
+
end # parse
|
96
|
+
|
97
|
+
#######
|
98
|
+
private
|
99
|
+
#######
|
100
|
+
|
101
|
+
def parse_header
|
102
|
+
@index = @header_base * 4
|
103
|
+
@tfm.checksum=get_qbyte
|
104
|
+
@tfm.designsize=get_fix_word
|
105
|
+
if @lh >= 3
|
106
|
+
count = get_byte
|
107
|
+
@tfm.codingscheme=get_chars(count)
|
108
|
+
@font_type= case @tfm.codingscheme
|
109
|
+
when "TeX math symbols"
|
110
|
+
:mathsy
|
111
|
+
when "TeX math extension"
|
112
|
+
:mathex
|
113
|
+
else
|
114
|
+
:vanilla
|
115
|
+
end
|
116
|
+
end
|
117
|
+
@index = (@header_base + 12) * 4
|
118
|
+
if @lh > 12
|
119
|
+
count = get_byte
|
120
|
+
@tfm.fontfamily=get_chars(count)
|
121
|
+
end
|
122
|
+
@index = (@header_base + 17 ) * 4
|
123
|
+
if @lh >= 17
|
124
|
+
@tfm.sevenbitsafeflag=get_byte > 127
|
125
|
+
# two bytes ignored
|
126
|
+
get_byte ; get_byte
|
127
|
+
@tfm.face=get_byte
|
128
|
+
end
|
129
|
+
# let us ignore the rest of the header (TeX ignores it, so we may
|
130
|
+
# do the same)
|
131
|
+
end # parse_header
|
132
|
+
|
133
|
+
def parse_params
|
134
|
+
@index=@param_base * 4
|
135
|
+
@tfm.params << nil
|
136
|
+
@np.times {
|
137
|
+
@tfm.params << get_fix_word
|
138
|
+
}
|
139
|
+
end # parse_params
|
140
|
+
|
141
|
+
# �78 TFtoPL
|
142
|
+
def parse_char_info
|
143
|
+
@index=@char_base *4
|
144
|
+
(@bc..@ec).each { |n|
|
145
|
+
tmp=if @tfm.chars[n]
|
146
|
+
@tfm.chars[n]
|
147
|
+
else
|
148
|
+
Hash.new
|
149
|
+
end
|
150
|
+
index=get_byte
|
151
|
+
tmp[:charwd]=get_fix_word((@width_base + index)*4)
|
152
|
+
b=get_byte
|
153
|
+
tmp[:charht]=get_fix_word((@height_base + (b >> 4))*4)
|
154
|
+
tmp[:chardp]=get_fix_word((@depth_base + (b % 16))*4)
|
155
|
+
tmp[:charic]=get_fix_word((@italic_base + (get_byte >> 2))*4)
|
156
|
+
# we ignore the remainder and look it up on demand
|
157
|
+
get_byte
|
158
|
+
if index == 0
|
159
|
+
@tfm.chars[n]=nil
|
160
|
+
else
|
161
|
+
@tfm.chars[n]=tmp
|
162
|
+
end
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
# now for the ugly part in the tfm, �63 pp
|
167
|
+
# Hey, we do a more clever implementation: we do not check for any
|
168
|
+
# errors. So coding is only a few lines instead of a few sections.
|
169
|
+
# this one took me so much time (the original, not this
|
170
|
+
# implementation), I am really frustrated.
|
171
|
+
def parse_lig_kern
|
172
|
+
# array that stores 'instruction that starts at x can be found in
|
173
|
+
# @tfm.lig_kern at position y'
|
174
|
+
start_instr=[]
|
175
|
+
@bc.upto(@ec) { |c|
|
176
|
+
next unless @tfm.chars[c]
|
177
|
+
if char_tag(c) == LIGTAG
|
178
|
+
start=get_lig_starting_point(c)
|
179
|
+
if start_instr[start] != nil
|
180
|
+
# we have already stored this ligkern
|
181
|
+
@tfm.chars[c][:lig_kern]=start_instr[start]
|
182
|
+
next
|
183
|
+
end
|
184
|
+
tmp=[]
|
185
|
+
|
186
|
+
start_instr[start]=@tfm.lig_kern.size
|
187
|
+
@tfm.lig_kern.push tmp
|
188
|
+
@tfm.chars[c][:lig_kern]=start_instr[start]
|
189
|
+
|
190
|
+
begin
|
191
|
+
s=get_byte(lig_step(start))
|
192
|
+
puts "warning: skip > 128 (#{s}) I don't know what to do." if s > 128
|
193
|
+
n,op,rem=get_byte(lig_step(start)+1),get_byte(lig_step(start)+2),get_byte(lig_step(start)+3)
|
194
|
+
|
195
|
+
if op >= 128
|
196
|
+
# kern!
|
197
|
+
kernamount=get_fix_word((@kern_base + (256 * (op-128) +rem)) *4)
|
198
|
+
tmp.push [:krn, n, kernamount]
|
199
|
+
else
|
200
|
+
tmp.push [TFM::LIGOPS[op], n, rem ]
|
201
|
+
end
|
202
|
+
tmp.push [:skip, s] if s > 0 and s < 128
|
203
|
+
start += 1
|
204
|
+
end until s >= 128
|
205
|
+
end
|
206
|
+
}
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# --------------------------------------------------
|
211
|
+
def char_tag(c)
|
212
|
+
@tfmdata[((@char_base + c - @bc ) *4 + 2)] % 2
|
213
|
+
end
|
214
|
+
def char_remainder(c)
|
215
|
+
@tfmdata[((@char_base + c - @bc ) *4 + 3)]
|
216
|
+
end
|
217
|
+
def get_lig_starting_point(char)
|
218
|
+
# warning: had some wine
|
219
|
+
return nil unless char_tag(char) == LIGTAG
|
220
|
+
r = char_remainder(char)
|
221
|
+
s=get_byte(lig_step(r))
|
222
|
+
if s > 128
|
223
|
+
# it does not start here, it starts somewhere else
|
224
|
+
n,op,rem=get_byte(lig_step(r)+1),get_byte(lig_step(r)+2),get_byte(lig_step(r)+3)
|
225
|
+
|
226
|
+
256*op+rem
|
227
|
+
else
|
228
|
+
r
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def get_byte(i=nil)
|
233
|
+
global = i == nil
|
234
|
+
i = @index if global
|
235
|
+
r=@tfmdata[i]
|
236
|
+
@index += 1 if global
|
237
|
+
r
|
238
|
+
end
|
239
|
+
# 16 bit integer
|
240
|
+
def get_dbyte
|
241
|
+
r = (@tfmdata[@index] << 8) + @tfmdata[@index + 1]
|
242
|
+
@index += 2
|
243
|
+
r
|
244
|
+
end
|
245
|
+
# 32 bit integer
|
246
|
+
def get_qbyte
|
247
|
+
r = (@tfmdata[@index] << 24) + (@tfmdata[@index+1] << 16) + (@tfmdata[@index+2] << 8) + @tfmdata[@index+3]
|
248
|
+
@index += 4
|
249
|
+
r
|
250
|
+
end
|
251
|
+
def get_chars(count)
|
252
|
+
ret=""
|
253
|
+
count.times { |count|
|
254
|
+
c=@tfmdata[@index + count]
|
255
|
+
ret << c.chr if c > 0
|
256
|
+
}
|
257
|
+
@index += count
|
258
|
+
ret
|
259
|
+
end
|
260
|
+
def get_fix_word(i=nil)
|
261
|
+
global = i==nil
|
262
|
+
i = @index if global
|
263
|
+
b=@tfmdata[(i..i+3)]
|
264
|
+
@index += 4 if global
|
265
|
+
a= (b[0] * 16) + (b[1].div 16)
|
266
|
+
f= ((b[1] % 16) * 256 + b[2] ) * 256 + b[3]
|
267
|
+
|
268
|
+
str = ""
|
269
|
+
if a > 03777
|
270
|
+
str << "-"
|
271
|
+
a = 010000 - a
|
272
|
+
if f > 0
|
273
|
+
f = 04000000 - f
|
274
|
+
a -= 1
|
275
|
+
end
|
276
|
+
end
|
277
|
+
# Knuth, TFtoPL �42
|
278
|
+
|
279
|
+
delta = 10
|
280
|
+
f=10*f+5
|
281
|
+
|
282
|
+
str << a.to_s + "."
|
283
|
+
begin
|
284
|
+
if delta > 04000000
|
285
|
+
f = f + 02000000 - ( delta / 2 )
|
286
|
+
end
|
287
|
+
str << (f / 04000000).to_s
|
288
|
+
f = 10 * ( f % 04000000)
|
289
|
+
delta *= 10
|
290
|
+
end until f <= delta
|
291
|
+
str.to_f
|
292
|
+
end
|
293
|
+
def lig_step(num)
|
294
|
+
(@lig_kern_base + num )*4
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
|
300
|
+
class TFMWriter
|
301
|
+
# More output to stdout
|
302
|
+
attr_accessor :verbose
|
303
|
+
|
304
|
+
WIDTH=1
|
305
|
+
HEIGHT=2
|
306
|
+
DEPTH=3
|
307
|
+
ITALIC=4
|
308
|
+
|
309
|
+
def initialize(tfmobject)
|
310
|
+
@tfm=tfmobject
|
311
|
+
@chars=[]
|
312
|
+
@lig_kern=nil
|
313
|
+
# for the sorting
|
314
|
+
@memsize=1028 + 4
|
315
|
+
# @memory=Array.new(@memsize)
|
316
|
+
@memory=[]
|
317
|
+
@whdi_index=[]
|
318
|
+
@mem_ptr=nil
|
319
|
+
@link=Array.new(@memsize)
|
320
|
+
@index=[]
|
321
|
+
@memory[0]=017777777777
|
322
|
+
@memory[WIDTH]=0
|
323
|
+
@memory[HEIGHT]=0
|
324
|
+
@memory[DEPTH]=0
|
325
|
+
@memory[ITALIC]=0
|
326
|
+
@link[WIDTH]=0
|
327
|
+
@link[HEIGHT]=0
|
328
|
+
@link[DEPTH]=0
|
329
|
+
@link[ITALIC]=0
|
330
|
+
@mem_ptr = ITALIC
|
331
|
+
@next_d=nil
|
332
|
+
|
333
|
+
|
334
|
+
@bchar_label=077777
|
335
|
+
|
336
|
+
@data=[]
|
337
|
+
@lf = 0
|
338
|
+
@lh = 0 # ok
|
339
|
+
@bc = 0 # ok
|
340
|
+
@ec = 0 # ok
|
341
|
+
@nw = 0 # ok
|
342
|
+
@nh = 0 # ok
|
343
|
+
@nd = 0 # ok
|
344
|
+
@ni = 0 # ok
|
345
|
+
@nl = 0 # ok
|
346
|
+
@nk = 0 # ok
|
347
|
+
@ne = 0 # ingore
|
348
|
+
@np = 0 # ok
|
349
|
+
end
|
350
|
+
def to_data
|
351
|
+
update_bc_ec
|
352
|
+
calculate_header
|
353
|
+
# width,heigt,dp,ic index
|
354
|
+
update_whdi_index
|
355
|
+
# @widths, @heights, @depths, @italics finished
|
356
|
+
update_lig_kern
|
357
|
+
# @kerns finished
|
358
|
+
update_parameters
|
359
|
+
# @parameters finished
|
360
|
+
@lf = 6 + @lh + (@ec - @bc + 1) + @nw + @nh + @nd + @ni + @nl + @nk + @ne + @np
|
361
|
+
@data += out_dbyte(@lf)
|
362
|
+
@data += out_dbyte(@lh)
|
363
|
+
@data += out_dbyte(@bc)
|
364
|
+
@data += out_dbyte(@ec)
|
365
|
+
@data += out_dbyte(@nw)
|
366
|
+
@data += out_dbyte(@nh)
|
367
|
+
@data += out_dbyte(@nd)
|
368
|
+
@data += out_dbyte(@ni)
|
369
|
+
@data += out_dbyte(@nl)
|
370
|
+
@data += out_dbyte(@nk)
|
371
|
+
@data += out_dbyte(@ne)
|
372
|
+
@data += out_dbyte(@np)
|
373
|
+
@data += @header
|
374
|
+
calculate_chars
|
375
|
+
@data += @chars
|
376
|
+
@data += @widths
|
377
|
+
@data += @heights
|
378
|
+
@data += @depths
|
379
|
+
@data += @italics
|
380
|
+
@data += @lig_kern
|
381
|
+
@data += @kerns
|
382
|
+
@data += @parameters
|
383
|
+
|
384
|
+
@data.pack("C*")
|
385
|
+
end
|
386
|
+
|
387
|
+
def calculate_chars
|
388
|
+
(@bc..@ec).each { |n|
|
389
|
+
if @tfm.chars[n]
|
390
|
+
wd_idx=@index[@widths_orig[n]]
|
391
|
+
ht_idx=@index[@heights_orig[n]] ? @index[@heights_orig[n]] << 4 : 0
|
392
|
+
dp_idx=@index[@depths_orig[n]] ? @index[@depths_orig[n]] : 0
|
393
|
+
ic_idx= @index[@italics_orig[n]] ? (@index[@italics_orig[n]] << 2) : 0
|
394
|
+
tag = @tfm.chars[n][:lig_kern] ? 1 : 0
|
395
|
+
remainder= @tfm.chars[n][:lig_kern] ? @instr_index[@tfm.chars[n][:lig_kern]] : 0
|
396
|
+
@chars += [wd_idx,ht_idx + dp_idx, ic_idx + tag, remainder]
|
397
|
+
else
|
398
|
+
@chars += [0,0,0,0]
|
399
|
+
end
|
400
|
+
}
|
401
|
+
end
|
402
|
+
|
403
|
+
def update_parameters
|
404
|
+
@parameters=[]
|
405
|
+
|
406
|
+
@tfm.params.each_with_index { |p,i|
|
407
|
+
next if i==0
|
408
|
+
@parameters += out_fix_word(p)
|
409
|
+
}
|
410
|
+
@np=@parameters.size / 4
|
411
|
+
end
|
412
|
+
|
413
|
+
def update_whdi_index
|
414
|
+
@widths_orig=[]
|
415
|
+
@heights_orig=[]
|
416
|
+
@depths_orig=[]
|
417
|
+
@italics_orig=[]
|
418
|
+
|
419
|
+
(@bc..@ec).each { |c|
|
420
|
+
if @tfm.chars[c]
|
421
|
+
@widths_orig[c]= sort_in(WIDTH,@tfm.chars[c][:charwd])
|
422
|
+
@heights_orig[c] = sort_in(HEIGHT,@tfm.chars[c][:charht] || 0)
|
423
|
+
@depths_orig[c] = sort_in(DEPTH,@tfm.chars[c][:chardp] || 0 )
|
424
|
+
@italics_orig[c] = sort_in(ITALIC,@tfm.chars[c][:charic] || 0 )
|
425
|
+
else
|
426
|
+
@widths_orig[c] = 0
|
427
|
+
@depths_orig[c] = 0
|
428
|
+
@heights_orig[c] = 0
|
429
|
+
@italics_orig[c] = 0
|
430
|
+
end
|
431
|
+
|
432
|
+
}
|
433
|
+
delta=shorten(WIDTH,200)
|
434
|
+
set_indices(WIDTH,delta)
|
435
|
+
delta=shorten(HEIGHT,15)
|
436
|
+
set_indices(HEIGHT,delta)
|
437
|
+
delta=shorten(DEPTH,15)
|
438
|
+
set_indices(DEPTH,delta)
|
439
|
+
delta=shorten(ITALIC,63)
|
440
|
+
set_indices(ITALIC,delta)
|
441
|
+
|
442
|
+
|
443
|
+
@widths = fill_index(WIDTH)
|
444
|
+
@heights = fill_index(HEIGHT)
|
445
|
+
@depths = fill_index(DEPTH)
|
446
|
+
@italics = fill_index(ITALIC)
|
447
|
+
@nw= @widths.size/4
|
448
|
+
@nh= @heights.size/4
|
449
|
+
@nd= @depths.size/4
|
450
|
+
@ni= @italics.size/4
|
451
|
+
end
|
452
|
+
|
453
|
+
def update_lig_kern
|
454
|
+
kerns=[]
|
455
|
+
instructions=[]
|
456
|
+
(@bc..@ec).each { |n|
|
457
|
+
next unless @tfm.chars[n]
|
458
|
+
next unless @tfm.chars[n][:lig_kern]
|
459
|
+
# we can skip aliases
|
460
|
+
next if instructions[@tfm.chars[n][:lig_kern]]
|
461
|
+
newinstr=[]
|
462
|
+
@tfm.lig_kern[@tfm.chars[n][:lig_kern]].each { |instr,*rest|
|
463
|
+
skip=nextchar=op=remainder=0
|
464
|
+
case instr
|
465
|
+
when :krn
|
466
|
+
i=nil
|
467
|
+
unless i = kerns.index(rest[1])
|
468
|
+
kerns << rest[1]
|
469
|
+
i=kerns.size - 1
|
470
|
+
end
|
471
|
+
skip=0
|
472
|
+
nextchar=rest[0]
|
473
|
+
remainder=i % 256
|
474
|
+
op = remainder / 256 + 128
|
475
|
+
# :stopdoc:
|
476
|
+
when :lig, :"lig/", :"/lig", :"/lig/", :"lig/>", :"/lig>", :"/lig/>", :"/lig/>>"
|
477
|
+
# :startdoc:
|
478
|
+
skip=0
|
479
|
+
nextchar,remainder=rest
|
480
|
+
op=TFM::LIGOPS.index(instr)
|
481
|
+
when :skip
|
482
|
+
# todo: test for incorrect situations
|
483
|
+
newinstr[-4] = rest[0]
|
484
|
+
next
|
485
|
+
else
|
486
|
+
raise "don't know instruction #{instr}"
|
487
|
+
end
|
488
|
+
newinstr += [skip,nextchar,op,remainder]
|
489
|
+
}
|
490
|
+
newinstr[-4] = 128
|
491
|
+
instructions[@tfm.chars[n][:lig_kern]] = newinstr
|
492
|
+
}
|
493
|
+
|
494
|
+
# we have all instructions collected in an array. The problem now
|
495
|
+
# is to fill the @lig_kern array so that all start of instruction
|
496
|
+
# programs are within the first 256 words of @lig_kern. So we keep
|
497
|
+
# filling the @lig_kern array until there would not be enough room
|
498
|
+
# left for the indirect nodes for the remaining count of
|
499
|
+
# instructions. Say, we have 50 instructions left to go and there
|
500
|
+
# are 60 words free in the first 256 words of @lig_kern, but the
|
501
|
+
# current instruction would take more then 10 words, we need to
|
502
|
+
# stop and fill the @lig_kern array with the indirect nodes and
|
503
|
+
# then continue with the instructions. The following
|
504
|
+
# implementation seems to work, but I refuse to prove it and it is
|
505
|
+
# definitely not the most beautiful piece of code I have written.
|
506
|
+
|
507
|
+
@instr_index=[]
|
508
|
+
@lig_kern=[]
|
509
|
+
|
510
|
+
total_instr=instructions.size
|
511
|
+
if total_instr > 0
|
512
|
+
instr_left=total_instr
|
513
|
+
thisinstr=instructions.shift
|
514
|
+
|
515
|
+
while (256 - @lig_kern.size / 4) - instr_left - thisinstr.size / 4 > 0
|
516
|
+
@instr_index[total_instr-instr_left]=@lig_kern.size / 4
|
517
|
+
@lig_kern += thisinstr
|
518
|
+
thisinstr=instructions.shift
|
519
|
+
instr_left -= 1
|
520
|
+
break if instr_left.zero?
|
521
|
+
end
|
522
|
+
|
523
|
+
unless instr_left.zero?
|
524
|
+
# undo last changes, since these don't fit into the @lig_kern
|
525
|
+
# array (first 256 elements) (yes, this is ugly)
|
526
|
+
instructions.unshift thisinstr
|
527
|
+
|
528
|
+
|
529
|
+
|
530
|
+
pos=@lig_kern.size / 4 + instr_left
|
531
|
+
count=@instr_index.size
|
532
|
+
|
533
|
+
# now fill the indirect nodes, calculate the starting points of
|
534
|
+
# the instructions
|
535
|
+
instructions.each { |i|
|
536
|
+
@instr_index[count]=@lig_kern.size / 4
|
537
|
+
count += 1
|
538
|
+
@lig_kern += [ 129, 0, (pos / 256) , (pos % 256) ]
|
539
|
+
pos += i.size / 4
|
540
|
+
}
|
541
|
+
|
542
|
+
# now we continue with the instructions
|
543
|
+
instructions.each { |i|
|
544
|
+
@lig_kern += i
|
545
|
+
}
|
546
|
+
end
|
547
|
+
end
|
548
|
+
@nl = @lig_kern.size / 4
|
549
|
+
|
550
|
+
@kerns=[]
|
551
|
+
kerns.each { |k|
|
552
|
+
@kerns += out_fix_word(k)
|
553
|
+
}
|
554
|
+
@nk=@kerns.size / 4
|
555
|
+
end
|
556
|
+
|
557
|
+
|
558
|
+
def fill_index(start)
|
559
|
+
i=start
|
560
|
+
what=[0,0,0,0]
|
561
|
+
while (i=@link[i]) > 0
|
562
|
+
what += out_fix_word(@memory[i])
|
563
|
+
end
|
564
|
+
return what
|
565
|
+
end
|
566
|
+
|
567
|
+
def calculate_header
|
568
|
+
@header=[]
|
569
|
+
# checksum
|
570
|
+
@header += checksum
|
571
|
+
# dsize
|
572
|
+
@header += out_fix_word(@tfm.designsize)
|
573
|
+
# 2..11 coding scheme, bcpl
|
574
|
+
out_bcpl(@tfm.codingscheme,40)
|
575
|
+
# 12..16 font identifier
|
576
|
+
out_bcpl(@tfm.fontfamily,20)
|
577
|
+
# calculate 7bitflag!
|
578
|
+
# 7bitflag, byte, byte, face
|
579
|
+
if @tfm.sevenbitsafeflag
|
580
|
+
@header << 128
|
581
|
+
else
|
582
|
+
@header << 0
|
583
|
+
end
|
584
|
+
@header << 0
|
585
|
+
@header << 0
|
586
|
+
@header << @tfm.face
|
587
|
+
@lh = @header.size / 4
|
588
|
+
end
|
589
|
+
def update_bc_ec
|
590
|
+
@bc=nil
|
591
|
+
@tfm.chars.each_with_index{ |elt,i|
|
592
|
+
@bc=i if @bc==nil and elt!=nil
|
593
|
+
@ec=i if elt
|
594
|
+
}
|
595
|
+
end
|
596
|
+
def checksum
|
597
|
+
return out_qbyte(@tfm.checksum)
|
598
|
+
end
|
599
|
+
|
600
|
+
def out_bcpl(string,len)
|
601
|
+
str=string
|
602
|
+
l = str.length
|
603
|
+
if l > 39
|
604
|
+
str=string[0..38]
|
605
|
+
end
|
606
|
+
l = str.length
|
607
|
+
@header << l
|
608
|
+
count=1
|
609
|
+
str.each_byte { |x|
|
610
|
+
count += 1
|
611
|
+
@header << x
|
612
|
+
}
|
613
|
+
while len - count > 0
|
614
|
+
@header << 0
|
615
|
+
count += 1
|
616
|
+
end
|
617
|
+
end
|
618
|
+
def out_dbyte(int)
|
619
|
+
a1=int % 256
|
620
|
+
a0=int / 256
|
621
|
+
return [a0,a1]
|
622
|
+
end
|
623
|
+
def out_qbyte(int)
|
624
|
+
a3=int % 256
|
625
|
+
int = int / 256
|
626
|
+
a2 = int % 256
|
627
|
+
int = int / 256
|
628
|
+
a1=int % 256
|
629
|
+
a0=int / 256
|
630
|
+
return [a0,a1,a2,a3]
|
631
|
+
end
|
632
|
+
|
633
|
+
# looks ok
|
634
|
+
def out_fix_word(b)
|
635
|
+
# a=int part, f=after dec point
|
636
|
+
a=b.truncate
|
637
|
+
f=b-a
|
638
|
+
if b < 0
|
639
|
+
f = 1 - f.abs
|
640
|
+
a = a -1
|
641
|
+
end
|
642
|
+
x=(2**20.0*f).round
|
643
|
+
a3=x.modulo(256)
|
644
|
+
# x >>= 8
|
645
|
+
x=x/256
|
646
|
+
a2=x % 256
|
647
|
+
# x >>= 8
|
648
|
+
x = x >> 8
|
649
|
+
a1=x % 16
|
650
|
+
a1 += (a % 16) << 4
|
651
|
+
a0=b < 0 ? 256 + a / 16 : a / 16
|
652
|
+
[a0,a1, a2, a3]
|
653
|
+
end
|
654
|
+
|
655
|
+
def sort_in(h,d)
|
656
|
+
if d==0 and h!=WIDTH
|
657
|
+
return 0
|
658
|
+
end
|
659
|
+
p=h
|
660
|
+
while d >= @memory[@link[p]]
|
661
|
+
p=@link[p]
|
662
|
+
end
|
663
|
+
if d==@memory[p] and p!=h
|
664
|
+
return p
|
665
|
+
end
|
666
|
+
raise "Memory overflow: more than 1028 widths etc." if @mem_ptr==@memsize
|
667
|
+
@mem_ptr += 1
|
668
|
+
@memory[@mem_ptr]=d
|
669
|
+
@link[@mem_ptr]=@link[p]
|
670
|
+
@link[p]=@mem_ptr
|
671
|
+
@memory[h]+=1
|
672
|
+
return @mem_ptr
|
673
|
+
end
|
674
|
+
|
675
|
+
# see PLtoTF, �75pp
|
676
|
+
def min_cover(h,d)
|
677
|
+
m=0
|
678
|
+
p=@link[h]
|
679
|
+
@next_d=@memory[0] # large value
|
680
|
+
while p!=0
|
681
|
+
m += 1
|
682
|
+
l = @memory[p]
|
683
|
+
while @memory[@link[p]]<=l+d
|
684
|
+
p=@link[p]
|
685
|
+
end
|
686
|
+
p=@link[p]
|
687
|
+
if @memory[p]-l < @next_d
|
688
|
+
@next_d=@memory[p]-l
|
689
|
+
end
|
690
|
+
end
|
691
|
+
return m
|
692
|
+
end
|
693
|
+
|
694
|
+
def shorten(h,m)
|
695
|
+
if @memory[h] <= m
|
696
|
+
return 0
|
697
|
+
end
|
698
|
+
@excess=@memory[h]-m
|
699
|
+
if @excess > 0 and @verbose
|
700
|
+
puts "We need to shorten the list by #@excess"
|
701
|
+
end
|
702
|
+
k=min_cover(h,0)
|
703
|
+
d=@next_d
|
704
|
+
begin
|
705
|
+
d=d+d
|
706
|
+
k=min_cover(h,d)
|
707
|
+
end until k <= m
|
708
|
+
d = d / 2
|
709
|
+
k=min_cover(h,d)
|
710
|
+
while k > m
|
711
|
+
d=@next_d
|
712
|
+
k=min_cover(h,d)
|
713
|
+
end
|
714
|
+
return d
|
715
|
+
end
|
716
|
+
|
717
|
+
def set_indices(h,d)
|
718
|
+
q=h
|
719
|
+
p=@link[q]
|
720
|
+
m=0
|
721
|
+
while p!=0
|
722
|
+
m+=1
|
723
|
+
l=@memory[p]
|
724
|
+
@index[p]=m
|
725
|
+
while @memory[@link[p]] <= l+d
|
726
|
+
p=@link[p]
|
727
|
+
@index[p]=m
|
728
|
+
@excess -= 1
|
729
|
+
if @excess == 0
|
730
|
+
d=0
|
731
|
+
end
|
732
|
+
end
|
733
|
+
@link[q]=p
|
734
|
+
@memory[p] = l+(@memory[p]-l) / 2
|
735
|
+
q=p
|
736
|
+
p=@link[p]
|
737
|
+
end
|
738
|
+
@memory[h]=m
|
739
|
+
end
|
740
|
+
|
741
|
+
end
|
742
|
+
|
743
|
+
|
744
|
+
|
745
|
+
# Parse a pl (property list) file.
|
746
|
+
class PLParser
|
747
|
+
require 'strscan'
|
748
|
+
|
749
|
+
# _tfmobj_ is an Object of the TFM class.
|
750
|
+
def initialize(tfmobj)
|
751
|
+
@tfm=tfmobj
|
752
|
+
@s=nil
|
753
|
+
@syntax={
|
754
|
+
"COMMENT" => :get_balanced,
|
755
|
+
"FAMILY" => :get_family,
|
756
|
+
"FACE" => :get_face,
|
757
|
+
"CODINGSCHEME" => :get_codingscheme,
|
758
|
+
"DESIGNSIZE" => :get_designsize,
|
759
|
+
"CHECKSUM" => :get_checksum,
|
760
|
+
"FONTDIMEN" => :get_fontdimen,
|
761
|
+
"LIGTABLE" => :get_ligtable,
|
762
|
+
"CHARACTER" => :get_character,
|
763
|
+
}
|
764
|
+
end
|
765
|
+
|
766
|
+
# Parse the given pl file. _obj_ should be a string.
|
767
|
+
def parse (obj)
|
768
|
+
@s=StringScanner.new(obj)
|
769
|
+
@level=0
|
770
|
+
while k=keyword
|
771
|
+
if m=@syntax[k]
|
772
|
+
r=self.send(m)
|
773
|
+
else
|
774
|
+
raise "unknown property #{k}"
|
775
|
+
end
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
#######
|
780
|
+
private
|
781
|
+
#######
|
782
|
+
|
783
|
+
def get_character
|
784
|
+
thischar = @tfm.chars[get_num] ||= {}
|
785
|
+
# [:charwd, :charht, :chardp, :charic].each do |s|
|
786
|
+
# thischar[s]=0.0
|
787
|
+
# end
|
788
|
+
thislevel=@level
|
789
|
+
while @level >= thislevel
|
790
|
+
case k=keyword
|
791
|
+
when "COMMENT"
|
792
|
+
get_balanced
|
793
|
+
eat_closing_paren
|
794
|
+
when "CHARWD","CHARHT","CHARDP","CHARIC"
|
795
|
+
thischar[k.downcase.to_sym]=get_num
|
796
|
+
else
|
797
|
+
raise "Unknown property in pl file/character section: #{k}"
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
801
|
+
def get_ligtable
|
802
|
+
thislevel=@level
|
803
|
+
@tfm.lig_kern = []
|
804
|
+
instruction=[]
|
805
|
+
instrnum=[]
|
806
|
+
while @level==thislevel
|
807
|
+
case kw=keyword
|
808
|
+
when "LABEL"
|
809
|
+
instrnum.push get_num
|
810
|
+
when /LIG/
|
811
|
+
instruction << [kw.downcase.to_sym, get_num, get_num]
|
812
|
+
when "KRN"
|
813
|
+
instruction << [:krn, get_num,get_num]
|
814
|
+
when "STOP"
|
815
|
+
n=@tfm.lig_kern.size
|
816
|
+
instrnum.each { |x|
|
817
|
+
t = @tfm.chars[x] ||= {}
|
818
|
+
t[:lig_kern] = n
|
819
|
+
}
|
820
|
+
instrnum=[]
|
821
|
+
@tfm.lig_kern.push instruction
|
822
|
+
instruction=[]
|
823
|
+
eat_closing_paren
|
824
|
+
else
|
825
|
+
puts "unknown element in ligtable #{kw}, stop"
|
826
|
+
exit
|
827
|
+
end
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
def get_fontdimen
|
832
|
+
thislevel=@level
|
833
|
+
while @level==thislevel
|
834
|
+
n=case keyword
|
835
|
+
when "SLANT" then 1
|
836
|
+
when "SPACE" then 2
|
837
|
+
when "STRETCH" then 3
|
838
|
+
when "SHRINK" then 4
|
839
|
+
when "XHEIGHT" then 5
|
840
|
+
when "QUAD" then 6
|
841
|
+
when "EXTRASPACE" then 7
|
842
|
+
when "NUM1", "DEFAULT_RULE_THICKNESS" then 8
|
843
|
+
when "NUM2", "BIG_OP_SPACING1" then 9
|
844
|
+
when "NUM3", "BIG_OP_SPACING2" then 10
|
845
|
+
when "DENOM1", "BIG_OP_SPACING3" then 11
|
846
|
+
when "DENOM2", "BIG_OP_SPACING4" then 12
|
847
|
+
when "SUP1", "BIG_OP_SPACING5" then 13
|
848
|
+
when "SUP2" then 14
|
849
|
+
when "SUP3" then 15
|
850
|
+
when "SUB1" then 16
|
851
|
+
when "SUB2" then 17
|
852
|
+
when "SUPDROP" then 18
|
853
|
+
when "PARAMETER"
|
854
|
+
get_num
|
855
|
+
else
|
856
|
+
raise "unknown instruction in fontdimen"
|
857
|
+
end
|
858
|
+
@tfm.params[n]=get_num
|
859
|
+
end
|
860
|
+
end
|
861
|
+
def get_checksum
|
862
|
+
@tfm.checksum=get_num
|
863
|
+
end
|
864
|
+
def get_designsize
|
865
|
+
@tfm.designsize=get_num
|
866
|
+
end
|
867
|
+
def get_family
|
868
|
+
@tfm.fontfamily=get_string
|
869
|
+
end
|
870
|
+
def get_face
|
871
|
+
@tfm.face=get_num
|
872
|
+
end
|
873
|
+
def get_codingscheme
|
874
|
+
@tfm.codingscheme=get_balanced
|
875
|
+
eat_closing_paren
|
876
|
+
end
|
877
|
+
def eat_closing_paren
|
878
|
+
while @s.scan(/\s*\n?\)\n?/)
|
879
|
+
@level -= 1
|
880
|
+
end
|
881
|
+
end
|
882
|
+
# we are just before an open paren
|
883
|
+
def keyword
|
884
|
+
@s.skip_until(/\(/)
|
885
|
+
@level += 1
|
886
|
+
@s.skip(/\s+/)
|
887
|
+
ret= @s.scan(/[A-Za-z\/>]+/)
|
888
|
+
@s.skip(/\s+/)
|
889
|
+
return ret
|
890
|
+
end
|
891
|
+
|
892
|
+
def get_balanced
|
893
|
+
str=""
|
894
|
+
startlevel=@level
|
895
|
+
while @level >= startlevel
|
896
|
+
str << @s.scan(/[^\(\)]*/)
|
897
|
+
if (tmp = @s.scan(/(\(|\))/)) == "("
|
898
|
+
@level += 1
|
899
|
+
else
|
900
|
+
@level -= 1
|
901
|
+
end
|
902
|
+
str << tmp if @level >= startlevel
|
903
|
+
end
|
904
|
+
@s.skip(/\n/)
|
905
|
+
str
|
906
|
+
end
|
907
|
+
def get_string
|
908
|
+
@s.skip(/\s/)
|
909
|
+
s= @s.scan(/[[:alnum:]`'_\- :]+/)
|
910
|
+
@s.scan(/\)\s*\n/)
|
911
|
+
@level -= 1
|
912
|
+
return s
|
913
|
+
end
|
914
|
+
def get_num
|
915
|
+
@s.skip(/\s+/)
|
916
|
+
s=@s.scan(/(R|C|D|O|F|H)/)
|
917
|
+
@s.skip(/\s+/)
|
918
|
+
value=case s
|
919
|
+
when "R"
|
920
|
+
@s.scan(/-?\d+(\.\d+)?/).to_f
|
921
|
+
when "C"
|
922
|
+
@s.scan(/[[:alnum:]]/)[0]
|
923
|
+
when "D"
|
924
|
+
@s.scan(/\d+/).to_i
|
925
|
+
when "O"
|
926
|
+
@s.scan(/\d+/).to_i(8)
|
927
|
+
when "F"
|
928
|
+
t=@s.scan(/(M|B|L)(R|I)(R|C|E)/)
|
929
|
+
['MRR','MIR','BRR','BIR','LRR','LIR','MRC','MIC','BRC','BIC',
|
930
|
+
'LRC','LIC','MRE','MIE','BRE','BIE','LRE','LIE'].index(t)
|
931
|
+
else
|
932
|
+
raise "not implemented yet"
|
933
|
+
end
|
934
|
+
eat_closing_paren
|
935
|
+
value
|
936
|
+
end
|
937
|
+
end #class pl parser
|
938
|
+
|
939
|
+
|
940
|
+
|
941
|
+
# :stopdoc:
|
942
|
+
LIGOPS= [ :lig, :"lig/", :"/lig", :"/lig/",
|
943
|
+
nil, :"lig/>", :"/lig>", :"/lig/>",
|
944
|
+
nil, nil, nil, :"/lig/>>" ]
|
945
|
+
|
946
|
+
FACE = ['MRR','MIR','BRR','BIR','LRR','LIR','MRC','MIC','BRC','BIC',
|
947
|
+
'LRC','LIC','MRE','MIE','BRE','BIE','LRE','LIE']
|
948
|
+
|
949
|
+
NOTAG=0
|
950
|
+
LIGTAG=1
|
951
|
+
LISTTAG=2
|
952
|
+
EXTTAG=3
|
953
|
+
|
954
|
+
def self.documented_as_accessor(*args) #:nodoc:
|
955
|
+
end
|
956
|
+
def self.documented_as_reader(*args) #:nodoc:
|
957
|
+
end
|
958
|
+
# :startdoc:
|
959
|
+
|
960
|
+
# Print diagnostics
|
961
|
+
attr_accessor :verbose
|
962
|
+
|
963
|
+
# Filename sans path of the tfm file. To change this attribute, set
|
964
|
+
# pathname.
|
965
|
+
documented_as_reader :tfmfilename
|
966
|
+
|
967
|
+
# Path to the tfm file.
|
968
|
+
attr_accessor :tfmpathname
|
969
|
+
|
970
|
+
# Checksum of the tfm file.
|
971
|
+
attr_accessor :checksum
|
972
|
+
|
973
|
+
# The designsize (Float). Must be >= 1.0.
|
974
|
+
attr_accessor :designsize
|
975
|
+
|
976
|
+
# Coding scheme of the font. One of "TeX math symbols", "TeX math
|
977
|
+
# extension" or anything else. The two have special meaning (more
|
978
|
+
# parameters). Maximum length is 40
|
979
|
+
attr_accessor :codingscheme
|
980
|
+
|
981
|
+
# Font family is an arbitrary String. Default is "UNSPECIFIED".
|
982
|
+
# Maximum length is 20.
|
983
|
+
attr_accessor :fontfamily
|
984
|
+
|
985
|
+
# This boolean flag denotes if the font has chars with index > 127.
|
986
|
+
attr_accessor :sevenbitsafeflag
|
987
|
+
|
988
|
+
# Face code. 0 <= 17.
|
989
|
+
attr_accessor :face
|
990
|
+
|
991
|
+
# Array of chars. Each entry is a Hash with the following keys:
|
992
|
+
# <tt>:charwd</tt> <tt>:charht</tt>, <tt>:chardp</tt>,
|
993
|
+
# <tt>:charic</tt> and <tt>:lig_kern</tt>. The first four are in
|
994
|
+
# designsize units. The <tt>:lig_kern</tt> key is the instruction
|
995
|
+
# number pointing to the entry in the lig_kern attribute of the TFM
|
996
|
+
# class.
|
997
|
+
attr_accessor :chars
|
998
|
+
|
999
|
+
# Array of ligkern instructions. Each instruction is an Array of
|
1000
|
+
# Arrays where the first element is either <tt>:krn</tt> or one of
|
1001
|
+
# <tt>:lig</tt>, <tt>:lig/</tt>, <tt>:/lig</tt>, <tt>:/lig/</tt>,
|
1002
|
+
# <tt>:lig/></tt>, <tt>:/lig></tt>, <tt>:/lig/></tt> or
|
1003
|
+
# <tt>:/lig/>></tt>. If it is <tt>:krn</tt>, then the second
|
1004
|
+
# element is the next char and the third element must be the amount
|
1005
|
+
# of kerning in multiples of the designsize. If it is a
|
1006
|
+
# <tt>:lig</tt> (or similar), then the second element is the
|
1007
|
+
# nextchar. The third element is the resulting char.
|
1008
|
+
|
1009
|
+
# Example for an instruction:
|
1010
|
+
#
|
1011
|
+
#
|
1012
|
+
# [[:"lig/", 39, 148],
|
1013
|
+
# [:krn, 121, -0.029993],
|
1014
|
+
# [:krn, 39, -0.159998],
|
1015
|
+
# [:krn, 148, -0.13999],
|
1016
|
+
# [:krn, 89, -0.13999]]
|
1017
|
+
#
|
1018
|
+
# The complete <em>lig_kern</em> would be an Array of such instructions.
|
1019
|
+
attr_accessor :lig_kern
|
1020
|
+
|
1021
|
+
# The fontdimensions, index starts at 1.
|
1022
|
+
attr_accessor :params
|
1023
|
+
|
1024
|
+
def initialize
|
1025
|
+
@chars=[]
|
1026
|
+
@lig_kern=[]
|
1027
|
+
@params=[]
|
1028
|
+
@face=0
|
1029
|
+
@designsize=10.0
|
1030
|
+
@checksum=0
|
1031
|
+
@fontfamily="UNSPECIFIED"
|
1032
|
+
@verbose=false
|
1033
|
+
end
|
1034
|
+
def tfmfilename # :nodoc:
|
1035
|
+
File.basename(@tfmpathname)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
|
1039
|
+
# _plfile_ is a filename (String). (Future: File and String (pathname))
|
1040
|
+
def read_pl(plfilename)
|
1041
|
+
File.open(plfilename) { |f|
|
1042
|
+
parse_pl(f.read)
|
1043
|
+
}
|
1044
|
+
return self
|
1045
|
+
end
|
1046
|
+
def parse_pl(plstring)
|
1047
|
+
p=PLParser.new(self)
|
1048
|
+
p.parse(plstring)
|
1049
|
+
return self
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
# _file_ is either a File object (or something similar, it must
|
1053
|
+
# respond to :read) or a string containing the full pathname to the
|
1054
|
+
# tfm file. Returns the TFM object.
|
1055
|
+
def read_tfm(file)
|
1056
|
+
p=TFMReader.new(self)
|
1057
|
+
p.verbose=@verbose
|
1058
|
+
if file.respond_to? :read
|
1059
|
+
if file.respond_to? :path
|
1060
|
+
@tfmpathname=file.path
|
1061
|
+
end
|
1062
|
+
p.parse(file.read)
|
1063
|
+
else
|
1064
|
+
# we assume it is a string
|
1065
|
+
@tfmpathname=file
|
1066
|
+
case file
|
1067
|
+
when /\.tfm$/
|
1068
|
+
File.open(file) { |f|
|
1069
|
+
p.parse(f.read)
|
1070
|
+
}
|
1071
|
+
else
|
1072
|
+
raise ArgumentError, "unknown Filetype: #{file}"
|
1073
|
+
end
|
1074
|
+
end
|
1075
|
+
return self
|
1076
|
+
end # read_file
|
1077
|
+
|
1078
|
+
# If _overwrite_ is true, we will replace existing files without
|
1079
|
+
# raising Errno::EEXIST.
|
1080
|
+
def save(overwrite=false)
|
1081
|
+
raise Errno::EEXIST if File.exists?(@tfmpathname) and not overwrite
|
1082
|
+
puts "saving #{@tfmpathname}..." if @verbose
|
1083
|
+
File.open(@tfmpathname,"wb") { |f|
|
1084
|
+
write_file(f)
|
1085
|
+
}
|
1086
|
+
puts "saving #{@tfmpathname}...done" if @verbose
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
# _file_ is a File object (or something similar, it must
|
1090
|
+
# respond to <<).
|
1091
|
+
def write_file(file)
|
1092
|
+
tfmwriter=TFMWriter.new(self)
|
1093
|
+
tfmwriter.verbose=@verbose
|
1094
|
+
file << tfmwriter.to_data
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
# Return pltotf compatible output.
|
1098
|
+
def to_s
|
1099
|
+
indent=" "
|
1100
|
+
str=""
|
1101
|
+
str << out_head(indent)
|
1102
|
+
str << out_parameters(indent)
|
1103
|
+
str << out_ligtable(indent)
|
1104
|
+
str << out_chars(indent)
|
1105
|
+
str
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
#######
|
1109
|
+
private
|
1110
|
+
#######
|
1111
|
+
|
1112
|
+
def out_head(indent)
|
1113
|
+
str ="(FAMILY #{fontfamily.upcase})\n"
|
1114
|
+
str << "(FACE F #{FACE[face]})\n"
|
1115
|
+
str << "(CODINGSCHEME #{codingscheme.upcase})\n"
|
1116
|
+
str << "(DESIGNSIZE R #{designsize})\n"
|
1117
|
+
str << "(CHECKSUM O #{sprintf("%o",checksum)})\n"
|
1118
|
+
end
|
1119
|
+
def out_chars(indent)
|
1120
|
+
str = ""
|
1121
|
+
chars.each_with_index { |c,i|
|
1122
|
+
next unless c
|
1123
|
+
# str << "(CHARACTER O #{sprintf("%o",i)}\n"
|
1124
|
+
str << "(CHARACTER D %d\n" % i
|
1125
|
+
[:charwd,:charht,:chardp,:charic].each { |dim|
|
1126
|
+
str << indent + "(#{dim.to_s.upcase} R #{c[dim]})\n" if c[dim]!=0.0
|
1127
|
+
}
|
1128
|
+
str << indent + ")\n"
|
1129
|
+
}
|
1130
|
+
str
|
1131
|
+
end
|
1132
|
+
def out_parameters(indent)
|
1133
|
+
paramname=%w( X SLANT SPACE STRETCH SHRINK XHEIGHT QUAD EXTRASPACE )
|
1134
|
+
if codingscheme=="TeX math symbols"
|
1135
|
+
paramname += %w(NUM1 NUM2 NUM3 DENOM1 DENOM2 SUP1 SUP2 SUP3
|
1136
|
+
SUB1 SUB2 SUPDROP)
|
1137
|
+
elsif codingscheme=="TeX math extension"
|
1138
|
+
paramname += %w(DEFAULT_RULE_THICKNESS BIG_OP_SPACING1
|
1139
|
+
BIG_OP_SPACING2 BIG_OP_SPACING3 BIG_OP_SPACING4 BIG_OP_SPACING5)
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
str = "(FONTDIMEN\n"
|
1143
|
+
@params.each_with_index { |p,i|
|
1144
|
+
next if i==0
|
1145
|
+
if paramname[i]
|
1146
|
+
str << indent + "(#{paramname[i]} R #{p})\n"
|
1147
|
+
else
|
1148
|
+
str << indent + "(PARAMETER D #{i} R #{p})\n"
|
1149
|
+
end
|
1150
|
+
}
|
1151
|
+
str << indent + ")\n"
|
1152
|
+
str
|
1153
|
+
end
|
1154
|
+
def out_ligtable(indent)
|
1155
|
+
return "" if @lig_kern.size==0
|
1156
|
+
str = "(LIGTABLE\n"
|
1157
|
+
lk_char=[]
|
1158
|
+
# first appearance of a char is the index, all chars for the same
|
1159
|
+
# instructions is the value
|
1160
|
+
# e.g. firstchar_chars[8]=[8,9] if chars 8 and 9 point to the same instr.
|
1161
|
+
firstchar_chars=[]
|
1162
|
+
@chars.each_with_index {|c,i|
|
1163
|
+
next unless c
|
1164
|
+
next unless instr=c[:lig_kern]
|
1165
|
+
# we need to find duplicates
|
1166
|
+
# some chars point to the same instruction
|
1167
|
+
if lk_char[instr]
|
1168
|
+
lk_char[instr].push i
|
1169
|
+
else
|
1170
|
+
lk_char[instr] = [i]
|
1171
|
+
end
|
1172
|
+
}
|
1173
|
+
|
1174
|
+
lk_char.each{ |a|
|
1175
|
+
firstchar_chars[a[0]]=a
|
1176
|
+
}
|
1177
|
+
firstchar_chars.each { |a|
|
1178
|
+
next unless a
|
1179
|
+
a.each { |l|
|
1180
|
+
str << indent + "(LABEL D #{l})\n"
|
1181
|
+
}
|
1182
|
+
@lig_kern[@chars[a[0]][:lig_kern]].each {|la|
|
1183
|
+
case la[0]
|
1184
|
+
when :skip
|
1185
|
+
str << indent + "(SKIP D #{la[1]})\n"
|
1186
|
+
when :krn
|
1187
|
+
str << indent + "(KRN D #{la[1]} R #{la[2]})\n"
|
1188
|
+
when :lig, :"lig/", :"/lig", :"/lig/", :"lig/>", :"/lig>", :"/lig/>", :"/lig/>>"
|
1189
|
+
str << indent + "(#{la[0].to_s.upcase} O #{sprintf("%o",la[1])} O #{sprintf("%o",la[2])})\n"
|
1190
|
+
end
|
1191
|
+
}
|
1192
|
+
str << indent + "(STOP)\n"
|
1193
|
+
}
|
1194
|
+
str << indent + ")\n"
|
1195
|
+
str
|
1196
|
+
end
|
1197
|
+
end # class TFM
|
1198
|
+
end # module TeX
|