rfil 0.2

Sign up to get free protection for your applications and to get access to all the features.
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