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