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/vf.rb
ADDED
@@ -0,0 +1,846 @@
|
|
1
|
+
# vf.rb -- Class that models TeX's virtual fonts.
|
2
|
+
#--
|
3
|
+
# Last Change: Tue May 16 17:32:53 2006
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'tex/tfm'
|
7
|
+
require 'tex/kpathsea'
|
8
|
+
|
9
|
+
module TeX
|
10
|
+
|
11
|
+
# The vf (virtual font) files are described in vftovp and vptovf. They
|
12
|
+
# are always connected with a tfm file that hold the font metric. The
|
13
|
+
# vf contain some redundant information copied from the tfm file.
|
14
|
+
# Since the VF class is derived from the TFM class, there is no need
|
15
|
+
# to duplicate these pieces of information.
|
16
|
+
|
17
|
+
class VF < TFM
|
18
|
+
# This class is not meant to be used directly by the programmer. It
|
19
|
+
# is used in the VF class to read a virtual font from a file.
|
20
|
+
|
21
|
+
class VFReader
|
22
|
+
def initialize(vfobj)
|
23
|
+
@vfobj= vfobj || VF.new
|
24
|
+
@stack=[[0,0,0,0]]
|
25
|
+
push
|
26
|
+
@index=0
|
27
|
+
@dviindex=nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# _vfdata_ is a string with the contents of the vf (binary) file.
|
31
|
+
# Return a VF object filled with the information of the virtual
|
32
|
+
# font. Does not read the tfm data. It is safe to parse tfm data
|
33
|
+
# after parsing the virtual font.
|
34
|
+
def parse(vfdata)
|
35
|
+
raise ArgumentError, "I expect a string" unless vfdata.respond_to?(:unpack)
|
36
|
+
@index=0
|
37
|
+
@kpse=Kpathsea.new
|
38
|
+
@data=vfdata.unpack("C*")
|
39
|
+
|
40
|
+
raise VFError, "This does not look like a vf to me" if 247 != get_byte
|
41
|
+
raise VFError, "Unknown VF version" unless 202 == get_byte
|
42
|
+
|
43
|
+
@vfobj.vtitle=get_chars(get_byte)
|
44
|
+
|
45
|
+
tfmcksum = get_qbyte
|
46
|
+
tfmdsize = get_fix_word
|
47
|
+
|
48
|
+
while b=get_byte
|
49
|
+
case b
|
50
|
+
when 0..241
|
51
|
+
@index -= 1
|
52
|
+
parse_char(:short)
|
53
|
+
when 242
|
54
|
+
parse_char(:long)
|
55
|
+
when 243,244,245,246
|
56
|
+
# jippie, a (map)font
|
57
|
+
fontnumber=get_bytes(243-b+1,false)
|
58
|
+
# perhaps we should actually load the tfm instead of saving
|
59
|
+
# the metadata?
|
60
|
+
@vfobj.fontlist[fontnumber]={}
|
61
|
+
checksum=get_qbyte
|
62
|
+
# @vfobj.fontlist[fontnumber][:checksum]=checksum
|
63
|
+
scale=get_fix_word
|
64
|
+
@vfobj.fontlist[fontnumber][:scale]=scale
|
65
|
+
dsize = get_fix_word
|
66
|
+
# @vfobj.fontlist[fontnumber][:designsize]=dsize
|
67
|
+
a = get_byte # length of area (directory?)
|
68
|
+
l = get_byte # length of fontname
|
69
|
+
area=get_chars(a)
|
70
|
+
name=get_chars(l)
|
71
|
+
# @vfobj.fontlist[fontnumber][:name]=name
|
72
|
+
@kpse.open_file(name,'tfm') { |file|
|
73
|
+
@vfobj.fontlist[fontnumber][:tfm]=TFM.new.read_tfm(file)
|
74
|
+
}
|
75
|
+
when 248
|
76
|
+
parse_postamble
|
77
|
+
else
|
78
|
+
raise VFError, "unknown instruction number #{b.inspect}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
return @vfobj
|
82
|
+
end # parse
|
83
|
+
|
84
|
+
|
85
|
+
#######
|
86
|
+
private
|
87
|
+
#######
|
88
|
+
|
89
|
+
def parse_postamble
|
90
|
+
while get_byte
|
91
|
+
end
|
92
|
+
end
|
93
|
+
# type: :long, :short
|
94
|
+
def parse_char(type)
|
95
|
+
instructions=[]
|
96
|
+
case type
|
97
|
+
when :long
|
98
|
+
pl=get_qbyte
|
99
|
+
cc=get_qbyte
|
100
|
+
tfm=out_as_fix(get_bytes(4,true,@index))
|
101
|
+
@index+=4
|
102
|
+
when :short
|
103
|
+
pl=get_byte
|
104
|
+
cc=get_byte
|
105
|
+
tfm=out_as_fix(get_bytes(3,true,@index))
|
106
|
+
@index+=3
|
107
|
+
else
|
108
|
+
raise ArgumentError,"unknown type: #{type}"
|
109
|
+
end
|
110
|
+
dvi=@data[(@index..@index+pl-1)]
|
111
|
+
@dviindex=@index
|
112
|
+
@index += pl
|
113
|
+
while i = get_byte(@dviindex) and @dviindex < @index
|
114
|
+
case i
|
115
|
+
when 0
|
116
|
+
# setchar 0
|
117
|
+
raise "not implementd"
|
118
|
+
when 1..127
|
119
|
+
instructions << [:setchar, i]
|
120
|
+
when 128..131
|
121
|
+
c=4-(131-i)
|
122
|
+
instructions << [:setchar, get_bytes(c,false,@dviindex+1)]
|
123
|
+
@dviindex += c
|
124
|
+
when 132,137
|
125
|
+
x=out_as_fix(get_bytes(4,true,@dviindex+1))
|
126
|
+
y=out_as_fix(get_bytes(4,true,@dviindex+5))
|
127
|
+
instructions << [:setrule,x,y]
|
128
|
+
@dviindex += 8
|
129
|
+
when 133..136
|
130
|
+
# are these ever used?
|
131
|
+
c=4-(136-i)
|
132
|
+
instructions << [:setchar, get_bytes(c,false,@dviindex+1)]
|
133
|
+
@dviindex += c
|
134
|
+
when 138
|
135
|
+
# nop
|
136
|
+
when 139,140
|
137
|
+
raise VFError, "illegal instruction in VF: #{i}"
|
138
|
+
when 141
|
139
|
+
instructions << [:push]
|
140
|
+
push
|
141
|
+
when 142
|
142
|
+
instructions << [:pop]
|
143
|
+
pop
|
144
|
+
when 143..146
|
145
|
+
c=4-(146-i)
|
146
|
+
b=out_as_fix(get_bytes(c,true,@dviindex+1))
|
147
|
+
instructions << [:moveright, b]
|
148
|
+
@dviindex += c
|
149
|
+
when 147
|
150
|
+
instructions << [:moveright, _w]
|
151
|
+
when 148..151
|
152
|
+
c=4-(151-i)
|
153
|
+
self._w=out_as_fix(get_bytes(c,true,@dviindex+1))
|
154
|
+
instructions << [:moveright,_w]
|
155
|
+
@dviindex += c
|
156
|
+
when 152
|
157
|
+
instructions << [:moveright, _x]
|
158
|
+
when 153..156
|
159
|
+
c=4-(156-i)
|
160
|
+
x=out_as_fix(get_bytes(c,true,@dviindex+1))
|
161
|
+
self._x=x
|
162
|
+
instructions << [:moveright,x]
|
163
|
+
@dviindex += c
|
164
|
+
when 157..160
|
165
|
+
# are these really used?
|
166
|
+
c=i-157+1
|
167
|
+
v=out_as_fix(get_bytes(c,true,@dviindex+1))
|
168
|
+
instructions << [:movedown,v]
|
169
|
+
@dviindex += c
|
170
|
+
when 161
|
171
|
+
instructions << [:movedown, _y]
|
172
|
+
when 162..165
|
173
|
+
c=i-162+1
|
174
|
+
self._y = out_as_fix(get_bytes(c,true,@dviindex+1))
|
175
|
+
instructions << [:movedown,_y]
|
176
|
+
@dviindex += c
|
177
|
+
# puts "#{i} movedown y #{_y}"
|
178
|
+
when 166
|
179
|
+
instructions << [:movedown, _z]
|
180
|
+
when 167..170
|
181
|
+
c=i-167+1
|
182
|
+
self._z = out_as_fix(get_bytes(c,true,@dviindex+1))
|
183
|
+
instructions << [:movedown,_z]
|
184
|
+
@dviindex += c
|
185
|
+
# puts "#{i} movedown z #{_z}"
|
186
|
+
when 171..234
|
187
|
+
instructions << [:selectfont, 63-234+i]
|
188
|
+
when 235..238
|
189
|
+
c=i-235+1
|
190
|
+
instructions << [:selectfont, get_bytes(c,true,@dviindex+1)]
|
191
|
+
@dviindex += c
|
192
|
+
when 239..242
|
193
|
+
c=i-239+1
|
194
|
+
k=get_bytes(c,true,@dviindex+1)
|
195
|
+
if k < 0
|
196
|
+
raise VFError, "length of special is negative"
|
197
|
+
end
|
198
|
+
instructions << [:special, get_chars(k,@dviindex+2)]
|
199
|
+
@dviindex += 1+k
|
200
|
+
when 243..255
|
201
|
+
raise VFError, "illegal instruction in VF: #{i}"
|
202
|
+
else
|
203
|
+
raise "not implemented: #{i}"
|
204
|
+
end
|
205
|
+
@dviindex += 1
|
206
|
+
end
|
207
|
+
# puts "charcode=#{cc} (octal #{sprintf("%o",cc)})"
|
208
|
+
tmp=if @vfobj.chars[cc]
|
209
|
+
@vfobj.chars[cc]
|
210
|
+
else
|
211
|
+
Hash.new
|
212
|
+
end
|
213
|
+
@vfobj.chars[cc]=tmp
|
214
|
+
tmp[:dvi]=instructions
|
215
|
+
end
|
216
|
+
def push
|
217
|
+
top=@stack[-1]
|
218
|
+
@stack.push top.dup
|
219
|
+
end
|
220
|
+
def pop
|
221
|
+
if @stack.size < 2
|
222
|
+
raise VFError, "more pop then push on stack"
|
223
|
+
end
|
224
|
+
return @stack.pop
|
225
|
+
end
|
226
|
+
def _w=(value)
|
227
|
+
@stack[-1][0]=value
|
228
|
+
end
|
229
|
+
def _w
|
230
|
+
@stack[-1][0]
|
231
|
+
end
|
232
|
+
def _x=(value)
|
233
|
+
@stack[-1][1]=value
|
234
|
+
end
|
235
|
+
def _x
|
236
|
+
@stack[-1][1]
|
237
|
+
end
|
238
|
+
|
239
|
+
def _y=(value)
|
240
|
+
@stack[-1][2]=value
|
241
|
+
end
|
242
|
+
def _y
|
243
|
+
@stack[-1][2]
|
244
|
+
end
|
245
|
+
|
246
|
+
def _z=(value)
|
247
|
+
@stack[-1][3]=value
|
248
|
+
end
|
249
|
+
def _z
|
250
|
+
@stack[-1][3]
|
251
|
+
end
|
252
|
+
|
253
|
+
def get_byte(i=nil)
|
254
|
+
global = i==nil
|
255
|
+
i = @index if global
|
256
|
+
r=@data[i]
|
257
|
+
@index += 1 if global
|
258
|
+
r
|
259
|
+
end
|
260
|
+
# 16 bit integer
|
261
|
+
def get_dbyte(i=nil)
|
262
|
+
global = i == nil
|
263
|
+
i = @index if global
|
264
|
+
r = (@data[i] << 8) + @data[i + 1]
|
265
|
+
@index += 2 if global
|
266
|
+
r
|
267
|
+
end
|
268
|
+
# 24 bit int
|
269
|
+
def get_tbyte(i=nil)
|
270
|
+
global = i == nil
|
271
|
+
i = @index if global
|
272
|
+
r = (@data[i] << 16) + (@data[i] << 8) + @data[i + 1]
|
273
|
+
@index += 3 if global
|
274
|
+
r
|
275
|
+
end
|
276
|
+
# signed 24 bit int
|
277
|
+
def get_stbyte(i=nil)
|
278
|
+
global = i == nil
|
279
|
+
i = @index if global
|
280
|
+
r = if @data[i] < 128
|
281
|
+
(@data[i] << 16) + (@data[i] << 8) + @data[i + 1]
|
282
|
+
else
|
283
|
+
((256 - @data[i]) << 16) + (@data[i] << 8) + @data[i + 1]
|
284
|
+
end
|
285
|
+
@index += 3 if global
|
286
|
+
r
|
287
|
+
end
|
288
|
+
|
289
|
+
# 32 bit integer
|
290
|
+
def get_qbyte
|
291
|
+
r = (@data[@index] << 24) + (@data[@index+1] << 16) + (@data[@index+2] << 8) + @data[@index+3]
|
292
|
+
@index += 4
|
293
|
+
r
|
294
|
+
end
|
295
|
+
# Read a string with at most count bytes. Does not add \0 to the string.
|
296
|
+
def get_chars(count,i=nil)
|
297
|
+
ret=""
|
298
|
+
global = i==nil
|
299
|
+
i = @index if global
|
300
|
+
|
301
|
+
count.times { |coumt|
|
302
|
+
c=@data[i + coumt]
|
303
|
+
ret << c.chr if c > 0
|
304
|
+
}
|
305
|
+
@index += count if global
|
306
|
+
return ret.size==0 ? nil : ret
|
307
|
+
end
|
308
|
+
def get_bytes(count,signed,i=nil)
|
309
|
+
global = i==nil
|
310
|
+
i=@index if global
|
311
|
+
a=@data[i]
|
312
|
+
if (count==4) or signed
|
313
|
+
if a >= 128
|
314
|
+
a -= 256
|
315
|
+
end
|
316
|
+
end
|
317
|
+
i +=1
|
318
|
+
while count > 1
|
319
|
+
a = a * 256 + @data[i]
|
320
|
+
i +=1
|
321
|
+
count -=1
|
322
|
+
end
|
323
|
+
@index += count if global
|
324
|
+
return a
|
325
|
+
end
|
326
|
+
def out_as_fix(x)
|
327
|
+
raise VFError if x.abs >= 0100000000
|
328
|
+
# let's misuse @data -> change
|
329
|
+
if x>=0 then @data[0]=0
|
330
|
+
else
|
331
|
+
@data[0]=255
|
332
|
+
x += 0100000000
|
333
|
+
end
|
334
|
+
3.downto(1) { |k|
|
335
|
+
@data[k]=x % 256
|
336
|
+
x = x.div(256)
|
337
|
+
}
|
338
|
+
get_fix_word(0)
|
339
|
+
end
|
340
|
+
def get_fix_word(i=nil)
|
341
|
+
global = i==nil
|
342
|
+
i = @index if global
|
343
|
+
b=@data[(i..i+3)]
|
344
|
+
@index += 4 if global
|
345
|
+
a= (b[0] * 16) + (b[1].div 16)
|
346
|
+
f= ((b[1] % 16) * 0400 + b[2] ) * 0400 + b[3]
|
347
|
+
|
348
|
+
str = ""
|
349
|
+
if a > 03777
|
350
|
+
str << "-"
|
351
|
+
a = 010000 - a
|
352
|
+
if f > 0
|
353
|
+
f = 04000000 - f
|
354
|
+
a -= 1
|
355
|
+
end
|
356
|
+
end
|
357
|
+
# Knuth, TFtoPL �42
|
358
|
+
|
359
|
+
delta = 10
|
360
|
+
f=10*f+5
|
361
|
+
|
362
|
+
str << a.to_s + "."
|
363
|
+
begin
|
364
|
+
if delta > 04000000
|
365
|
+
f = f + 02000000 - ( delta / 2 )
|
366
|
+
end
|
367
|
+
str << (f / 04000000).to_s
|
368
|
+
f = 10 * ( f % 04000000)
|
369
|
+
delta *= 10
|
370
|
+
end until f <= delta
|
371
|
+
str.to_f
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
class VFWriter
|
378
|
+
attr_accessor :verbose
|
379
|
+
def initialize(vfobject)
|
380
|
+
@vf=vfobject
|
381
|
+
end
|
382
|
+
|
383
|
+
def to_data
|
384
|
+
# preamble
|
385
|
+
@data=[247,202]
|
386
|
+
@data += out_string(@vf.vtitle)
|
387
|
+
@data += out_qbyte(@vf.checksum)
|
388
|
+
@data += out_fix_word(@vf.designsize)
|
389
|
+
|
390
|
+
# fonts
|
391
|
+
@vf.fontlist.each_with_index { |f,i|
|
392
|
+
count,*bytes=out_n_bytes(i)
|
393
|
+
@data += [242+count]
|
394
|
+
@data += bytes
|
395
|
+
|
396
|
+
@data+=out_qbyte(f[:tfm].checksum)
|
397
|
+
@data+=out_fix_word(f[:scale])
|
398
|
+
@data+=out_fix_word(f[:tfm].designsize)
|
399
|
+
@data+=[0]
|
400
|
+
@data += out_string(f[:tfm].tfmfilename.chomp('.tfm'))
|
401
|
+
}
|
402
|
+
|
403
|
+
# now for the chars
|
404
|
+
@vf.chars.each_with_index { |c,i|
|
405
|
+
next unless c
|
406
|
+
dvi=out_instructions(c[:dvi])
|
407
|
+
pl=dvi.length
|
408
|
+
tfm=c[:charwd]
|
409
|
+
if pl < 242 and tfm < 16.0 and tfm > 0 and i < 256
|
410
|
+
@data << pl
|
411
|
+
@data << i
|
412
|
+
@data += out_fix_word(tfm,3)
|
413
|
+
else
|
414
|
+
@data << 242
|
415
|
+
@data += out_qbyte(pl)
|
416
|
+
@data += out_qbyte(i)
|
417
|
+
@data += out_fix_word(tfm)
|
418
|
+
end
|
419
|
+
@data += dvi
|
420
|
+
}
|
421
|
+
@data << 248
|
422
|
+
while @data.size % 4 != 0
|
423
|
+
@data << 248
|
424
|
+
end
|
425
|
+
return @data.pack("C*")
|
426
|
+
end
|
427
|
+
|
428
|
+
#######
|
429
|
+
private
|
430
|
+
#######
|
431
|
+
|
432
|
+
def out_instructions(instructionlist)
|
433
|
+
ret=[]
|
434
|
+
instructionlist.each { |i|
|
435
|
+
case i[0]
|
436
|
+
when :setchar
|
437
|
+
charnum=i[1]
|
438
|
+
if charnum < 128
|
439
|
+
ret << charnum
|
440
|
+
elsif charnum > 255
|
441
|
+
raise VFError, "TeX does not know about chars > 8bit"
|
442
|
+
else
|
443
|
+
ret << 128
|
444
|
+
ret << charnum
|
445
|
+
end
|
446
|
+
when :setrule
|
447
|
+
ret << 132
|
448
|
+
ret += out_fix_word(i[1])
|
449
|
+
ret += out_fix_word(i[2])
|
450
|
+
when :noop
|
451
|
+
ret << 138
|
452
|
+
when :push
|
453
|
+
ret << 141
|
454
|
+
when :pop
|
455
|
+
ret << 142
|
456
|
+
when :moveright
|
457
|
+
# should we choose another moveright? --pg
|
458
|
+
ret << 156
|
459
|
+
ret += out_fix_word(i[1])
|
460
|
+
when :movedown
|
461
|
+
ret << 165
|
462
|
+
ret += out_fix_word(i[1])
|
463
|
+
when :special
|
464
|
+
len,*data=out_string(i[1])
|
465
|
+
blen,bytes = out_n_bytes(len)
|
466
|
+
ret << blen+238
|
467
|
+
ret << bytes
|
468
|
+
ret += data
|
469
|
+
else
|
470
|
+
raise VFError, "not implemented"
|
471
|
+
end
|
472
|
+
}
|
473
|
+
ret
|
474
|
+
end
|
475
|
+
|
476
|
+
def out_n_bytes(int)
|
477
|
+
case
|
478
|
+
when (int < 0), (int >= 0100000000)
|
479
|
+
[4] + out_sqbyte(int)
|
480
|
+
when int >= 0200000
|
481
|
+
[3] + out_tbyte(int)
|
482
|
+
when int >= 0400
|
483
|
+
[2] + out_dbyte(int)
|
484
|
+
else
|
485
|
+
[1,int]
|
486
|
+
end
|
487
|
+
end
|
488
|
+
def out_dbyte(int)
|
489
|
+
a1=int % 256
|
490
|
+
a0=int / 256
|
491
|
+
return [a0,a1]
|
492
|
+
end
|
493
|
+
def out_tbyte(int)
|
494
|
+
a2 = int % 256
|
495
|
+
int = int / 256
|
496
|
+
a1=int % 256
|
497
|
+
a0=int / 256
|
498
|
+
return [a0,a1,a2]
|
499
|
+
end
|
500
|
+
def out_qbyte(int)
|
501
|
+
a3=int % 256
|
502
|
+
int = int / 256
|
503
|
+
a2 = int % 256
|
504
|
+
int = int / 256
|
505
|
+
a1=int % 256
|
506
|
+
a0=int / 256
|
507
|
+
return [a0,a1,a2,a3]
|
508
|
+
end
|
509
|
+
# signed four bytes
|
510
|
+
def out_sqbyte(int)
|
511
|
+
a3=int % 256
|
512
|
+
int = int / 256
|
513
|
+
a2 = int % 256
|
514
|
+
int = int / 256
|
515
|
+
a1=int % 256
|
516
|
+
a0=int / 256
|
517
|
+
if int < 0
|
518
|
+
a0 = 256 + a0
|
519
|
+
end
|
520
|
+
return [a0,a1,a2,a3]
|
521
|
+
end
|
522
|
+
def out_fix_word(b,bytes=4)
|
523
|
+
# a=int part, f=after dec point
|
524
|
+
a=b.truncate
|
525
|
+
f=b-a
|
526
|
+
if b < 0
|
527
|
+
f = 1 - f.abs
|
528
|
+
a = a -1
|
529
|
+
end
|
530
|
+
x=(2**20.0*f).round
|
531
|
+
a3=x.modulo(256)
|
532
|
+
# x >>= 8
|
533
|
+
x=x/256
|
534
|
+
a2=x % 256
|
535
|
+
# x >>= 8
|
536
|
+
x = x >> 8
|
537
|
+
a1=x % 16
|
538
|
+
a1 += (a % 16) << 4
|
539
|
+
a0=b < 0 ? 256 + a / 16 : a / 16
|
540
|
+
if bytes == 3
|
541
|
+
[a1, a2, a3]
|
542
|
+
else
|
543
|
+
[a0,a1, a2, a3]
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
def out_string(string)
|
548
|
+
unless string
|
549
|
+
return [0]
|
550
|
+
end
|
551
|
+
ret=[string.length]
|
552
|
+
string.each_byte { |s|
|
553
|
+
ret << s
|
554
|
+
}
|
555
|
+
return ret
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
|
560
|
+
# Parse a vpl (virtual property list) file. See also TFM::PLParser.
|
561
|
+
class VPLParser < PLParser
|
562
|
+
# _vfobj_ is an initialized object of the VF class. Call
|
563
|
+
# parse(fileobj) to fill the VF object.
|
564
|
+
def initialize(vfobj)
|
565
|
+
@vf=vfobj
|
566
|
+
super
|
567
|
+
@syntax["VTITLE"] =:get_vtitle
|
568
|
+
@syntax["MAPFONT"]=:get_mapfont
|
569
|
+
end
|
570
|
+
|
571
|
+
#######
|
572
|
+
private
|
573
|
+
#######
|
574
|
+
|
575
|
+
def get_vtitle
|
576
|
+
@vf.vtitle=get_string
|
577
|
+
end
|
578
|
+
def get_mapfont
|
579
|
+
@vf.fontlist=[]
|
580
|
+
t = @vf.fontlist[get_num] = {}
|
581
|
+
t[:tfm]=TFM.new
|
582
|
+
thislevel=@level
|
583
|
+
while @level >= thislevel
|
584
|
+
case k=keyword
|
585
|
+
when "FONTNAME"
|
586
|
+
t[:tfm].tfmpathname=get_string
|
587
|
+
when "FONTCHECKSUM"
|
588
|
+
t[:tfm].checksum=get_num
|
589
|
+
when "FONTAT"
|
590
|
+
t[:scale]=get_num
|
591
|
+
when "FONTDSIZE"
|
592
|
+
t[:tfm].designsize=get_num
|
593
|
+
else
|
594
|
+
raise "Unknown property in MAPFONT section: #{k}"
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end # get_mapfont
|
598
|
+
|
599
|
+
# we copy this from tfm.rb, because now MAP is also allowed
|
600
|
+
def get_character
|
601
|
+
thischar = @tfm.chars[get_num] ||= {}
|
602
|
+
thislevel=@level
|
603
|
+
while @level >= thislevel
|
604
|
+
case k=keyword
|
605
|
+
when "COMMENT"
|
606
|
+
get_balanced
|
607
|
+
eat_closing_paren
|
608
|
+
when "CHARWD","CHARHT","CHARDP","CHARIC"
|
609
|
+
thischar[k.downcase.to_sym]=get_num
|
610
|
+
when "MAP"
|
611
|
+
instr=thischar[:dvi]=[]
|
612
|
+
maplevel=@level
|
613
|
+
while @level >= maplevel
|
614
|
+
case ik=keyword
|
615
|
+
when "SELECTFONT"
|
616
|
+
instr << [:selectfont, get_num]
|
617
|
+
when "SETCHAR"
|
618
|
+
instr << [:setchar, get_num]
|
619
|
+
when "SETRULE"
|
620
|
+
instr << [:setrule, get_num, get_num]
|
621
|
+
when "MOVEDOWN"
|
622
|
+
instr << [:movedown, get_num]
|
623
|
+
when "MOVERIGHT"
|
624
|
+
instr << [:moveright, get_num]
|
625
|
+
when "PUSH"
|
626
|
+
instr << [:push]
|
627
|
+
eat_closing_paren
|
628
|
+
when "POP"
|
629
|
+
instr << [:pop]
|
630
|
+
eat_closing_paren
|
631
|
+
when "SPECIAL"
|
632
|
+
instr << [:special, get_balanced]
|
633
|
+
# puts "special, #{get_balanced}"
|
634
|
+
eat_closing_paren
|
635
|
+
else
|
636
|
+
raise "Unknown instruction in character/map section: #{ik}"
|
637
|
+
end
|
638
|
+
end
|
639
|
+
else
|
640
|
+
raise "Unknown property in pl file/character section: #{k}"
|
641
|
+
end
|
642
|
+
end
|
643
|
+
end # get_character
|
644
|
+
end
|
645
|
+
|
646
|
+
|
647
|
+
|
648
|
+
# VF class
|
649
|
+
|
650
|
+
# Raise this exception if an error related to the virtual font is
|
651
|
+
# encountered. Don't expect this library to be too clever at the beginning.
|
652
|
+
class VFError < Exception
|
653
|
+
end
|
654
|
+
|
655
|
+
def self.documented_as_accessor(*args) #:nodoc:
|
656
|
+
end
|
657
|
+
def self.documented_as_reader(*args) #:nodoc:
|
658
|
+
end
|
659
|
+
|
660
|
+
# Filename sans path of the vf file. To change this attribute, set
|
661
|
+
# vfpathname.
|
662
|
+
documented_as_reader :vffilename
|
663
|
+
|
664
|
+
# Path to the vf file.
|
665
|
+
attr_accessor :vfpathname
|
666
|
+
|
667
|
+
|
668
|
+
# fontlist is an array of Hashes with the following keys:
|
669
|
+
# [<tt>:scale</tt>] Relative size of the font
|
670
|
+
# [<tt>:tfm</tt>] TFM object.
|
671
|
+
attr_accessor :fontlist
|
672
|
+
|
673
|
+
# This is the same Array as in TFM. Besides the keys <tt>:charwd</tt>,
|
674
|
+
# <tt>:charht</tt>, <tt>:chardp</tt> and <tt>:charic</tt>, we now have a key
|
675
|
+
# <tt>:dvi</tt> that holds all vf instructions.
|
676
|
+
documented_as_accessor :chars
|
677
|
+
|
678
|
+
# Comment at the beginning of the vf file. Must be < 256 chars.
|
679
|
+
attr_accessor :vtitle
|
680
|
+
|
681
|
+
# Return an empty VF object
|
682
|
+
def initialize
|
683
|
+
super
|
684
|
+
@vtitle=""
|
685
|
+
@fontlist=[]
|
686
|
+
end
|
687
|
+
|
688
|
+
def vffilename # :nodoc:
|
689
|
+
File.basename(@vfpathname)
|
690
|
+
end
|
691
|
+
|
692
|
+
# _vplfile_ is a filename (String). (Future: File and String (pathname))
|
693
|
+
def read_vpl(vplfilename)
|
694
|
+
File.open(vplfilename) { |f|
|
695
|
+
parse_vpl(f.read)
|
696
|
+
}
|
697
|
+
return self
|
698
|
+
end
|
699
|
+
def parse_vpl(vplstring)
|
700
|
+
v=VPLParser.new(self)
|
701
|
+
v.parse(vplstring)
|
702
|
+
return self
|
703
|
+
end
|
704
|
+
|
705
|
+
# _file_ is either a string (pathname) of a File object (must
|
706
|
+
# respond to read)
|
707
|
+
def read_vf(file)
|
708
|
+
p=VFReader.new(self)
|
709
|
+
if file.respond_to? :read
|
710
|
+
if file.respond_to? :path
|
711
|
+
@vfpathname=file.path
|
712
|
+
end
|
713
|
+
p.parse(file.read)
|
714
|
+
else
|
715
|
+
# we assume it is a string
|
716
|
+
@vfpathname=file
|
717
|
+
case file
|
718
|
+
when /\.vf$/
|
719
|
+
File.open(file) { |f|
|
720
|
+
p.parse(f.read)
|
721
|
+
}
|
722
|
+
else
|
723
|
+
raise ArgumentError, "unknown Filetype: #{file}"
|
724
|
+
end
|
725
|
+
end
|
726
|
+
t=TFMReader.new(self)
|
727
|
+
@tfmpathname=@vfpathname.chomp(".vf")+".tfm"
|
728
|
+
File.open(@tfmpathname){ |f|
|
729
|
+
t.parse(f.read)
|
730
|
+
}
|
731
|
+
return self
|
732
|
+
end #read_vf
|
733
|
+
|
734
|
+
# If _overwrite_ is true, we will replace existing files without
|
735
|
+
# raising Errno::EEXIST.
|
736
|
+
def save(overwrite=false)
|
737
|
+
# tfmpathname=@vfpathname.chomp(".vf")+".tfm"
|
738
|
+
raise "tfmpathname not set" unless @tfmpathname
|
739
|
+
raise Errno::EEXIST if File.exists?(@vfpathname) and not overwrite
|
740
|
+
raise Errno::EEXIST if File.exists?(@tfmpathname) and not overwrite
|
741
|
+
puts "saving #{@vfpathname}..." if @verbose
|
742
|
+
File.open(@vfpathname,"wb") { |f|
|
743
|
+
write_vf_file(f)
|
744
|
+
}
|
745
|
+
puts "saving #{@vfpathname}...done" if @verbose
|
746
|
+
puts "saving #{@tfmpathname}..." if @verbose
|
747
|
+
File.open(@tfmpathname,"wb") { |f|
|
748
|
+
write_tfm_file(f)
|
749
|
+
}
|
750
|
+
puts "saving #{@tfmpathname}...done" if @verbose
|
751
|
+
|
752
|
+
end
|
753
|
+
|
754
|
+
|
755
|
+
# _file_ is a File object (or something similar, it must
|
756
|
+
# respond to <<). Will be moved.
|
757
|
+
def write_vf_file(file)
|
758
|
+
vfwriter=VFWriter.new(self)
|
759
|
+
vfwriter.verbose=@verbose
|
760
|
+
file << vfwriter.to_data
|
761
|
+
end
|
762
|
+
|
763
|
+
# _file_ is a File object (or something similar, it must
|
764
|
+
# respond to <<). Will be moved.
|
765
|
+
def write_tfm_file(file)
|
766
|
+
tfmwriter=TFMWriter.new(self)
|
767
|
+
tfmwriter.verbose=@verbose
|
768
|
+
file << tfmwriter.to_data
|
769
|
+
end
|
770
|
+
# Return vptovf compatible output
|
771
|
+
def to_s
|
772
|
+
indent=" "
|
773
|
+
str=""
|
774
|
+
str << out_head(indent)
|
775
|
+
str << "(VTITLE #{vtitle})\n"
|
776
|
+
str << out_parameters(indent)
|
777
|
+
str << out_mapfont(indent)
|
778
|
+
str << out_ligtable(indent)
|
779
|
+
str << out_chars(indent)
|
780
|
+
str
|
781
|
+
end
|
782
|
+
|
783
|
+
#######
|
784
|
+
private
|
785
|
+
#######
|
786
|
+
|
787
|
+
def out_chars(indent)
|
788
|
+
str = ""
|
789
|
+
chars.each_with_index { |c,i|
|
790
|
+
next unless c
|
791
|
+
# str << "(CHARACTER O #{sprintf("%o",i)}\n"
|
792
|
+
str << "(CHARACTER D %d\n" % i
|
793
|
+
[:charwd,:charht,:chardp,:charic].each { |dim|
|
794
|
+
str << indent + "(#{dim.to_s.upcase} R #{c[dim]})\n" if c[dim]!=0.0
|
795
|
+
}
|
796
|
+
if c[:dvi]
|
797
|
+
str << indent + "(MAP\n"
|
798
|
+
c[:dvi].each { |instr,*rest|
|
799
|
+
|
800
|
+
case instr
|
801
|
+
when :setchar
|
802
|
+
str << indent*2 + "(SETCHAR D %d)\n" % rest[0].to_i
|
803
|
+
when :setrule
|
804
|
+
str << indent*2 + "(SETRULE R #{rest[0]} R #{rest[1]})\n"
|
805
|
+
when :noop
|
806
|
+
# ignore
|
807
|
+
when :push
|
808
|
+
str << indent*2 + "(PUSH)\n"
|
809
|
+
when :pop
|
810
|
+
str << indent*2 + "(POP)\n"
|
811
|
+
when :moveright
|
812
|
+
str << indent*2 + "(MOVERIGHT R #{rest[0]})\n"
|
813
|
+
when :movedown
|
814
|
+
str << indent*2 + "(MOVEDOWN R #{rest[0]})\n"
|
815
|
+
when :selectfont
|
816
|
+
str << indent*2 + "(SELECTFONT D #{rest[0]})\n"
|
817
|
+
when :special
|
818
|
+
str << indent*2 + "(SPECIAL #{rest[0]})\n"
|
819
|
+
else
|
820
|
+
raise "unknown dvi instruction #{instr}"
|
821
|
+
end
|
822
|
+
}
|
823
|
+
str << indent*2 + ")\n"
|
824
|
+
end
|
825
|
+
str << indent + ")\n"
|
826
|
+
}
|
827
|
+
str
|
828
|
+
end
|
829
|
+
|
830
|
+
def out_mapfont(indent)
|
831
|
+
return "" if fontlist.size == 0
|
832
|
+
str=""
|
833
|
+
|
834
|
+
fontlist.each_with_index { |f,i|
|
835
|
+
str << "(MAPFONT D %d\n" % i
|
836
|
+
str << indent + "(FONTNAME %s)\n" % f[:tfm].tfmfilename
|
837
|
+
str << indent + "(FONTCHECKSUM O %o)\n" % f[:tfm].checksum
|
838
|
+
str << indent + "(FONTAT R %f)\n" % (f[:scale] ? f[:scale].to_f : 1.0)
|
839
|
+
str << indent + "(FONTDSIZE R %f)\n" % f[:tfm].designsize
|
840
|
+
str << indent + ")\n"
|
841
|
+
}
|
842
|
+
str
|
843
|
+
end
|
844
|
+
end #class VF
|
845
|
+
|
846
|
+
end #module TeX
|