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/rfil/rfi.rb
ADDED
@@ -0,0 +1,472 @@
|
|
1
|
+
# rfi.rb -- general use classes
|
2
|
+
#--
|
3
|
+
# Last Change: Tue May 16 19:21:51 2006
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'rfil/font/glyph'
|
7
|
+
|
8
|
+
module RFIL # :nodoc:
|
9
|
+
|
10
|
+
# = RFI
|
11
|
+
# Everything that does not fit somewhere else gets included in the
|
12
|
+
# wrapper class RFI.
|
13
|
+
|
14
|
+
# This class contains methods and other classes that are pretty much
|
15
|
+
# useless of their own or are accessed in different classes.
|
16
|
+
|
17
|
+
class RFI # :nodoc:
|
18
|
+
|
19
|
+
# Super class for plugins. Just subclass this Plugin, set the name
|
20
|
+
# when calling Plugin#new and implement run_plugin.
|
21
|
+
class Plugin
|
22
|
+
# Name of the plugin. A Symbol or a String.
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
attr_reader :filetypes
|
26
|
+
|
27
|
+
# Create a new plugin. _name_ is the name of the plugin (it must
|
28
|
+
# be a Symbol or a String). _filetypes_ is a list of symbols, of
|
29
|
+
# what files the plugin is capable of writing.
|
30
|
+
def initialize(name,*filetypes)
|
31
|
+
@name=name
|
32
|
+
@filetypes=filetypes
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return an Array of files that should be written on the user's
|
36
|
+
# harddrive. The Hash entries are
|
37
|
+
# [<tt>:type</tt>] Type of the file (<tt>:fd</tt>, <tt>:typescript</tt> etc.)
|
38
|
+
# [<tt>:filename</tt>] The filename (without a path) of the file.
|
39
|
+
# [<tt>:contents</tt>] The contents of the file.
|
40
|
+
def run_plugin
|
41
|
+
#dummy
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Some instructions to remove kerning information from digits and
|
46
|
+
# other things. -> sort this out
|
47
|
+
STDLIGKERN = ["space l =: lslash",
|
48
|
+
"space L =: Lslash", "question quoteleft =: questiondown",
|
49
|
+
"exclam quoteleft =: exclamdown", "hyphen hyphen =: endash",
|
50
|
+
"endash hyphen =: emdash", "quoteleft quoteleft =: quotedblleft",
|
51
|
+
"quoteright quoteright =: quotedblright", "space {} *", "* {} space",
|
52
|
+
"zero {} *", "* {} zero",
|
53
|
+
"one {} *", "* {} one",
|
54
|
+
"two {} *", "* {} two",
|
55
|
+
"three {} *","* {} three",
|
56
|
+
"four {} *", "* {} four",
|
57
|
+
"five {} *", "* {} five",
|
58
|
+
"six {} *", "* {} six",
|
59
|
+
"seven {} *", "* {} seven",
|
60
|
+
"eight {} *", "* {} eight",
|
61
|
+
"nine {} *", "* {} nine",
|
62
|
+
"comma comma =: quotedblbase",
|
63
|
+
"less less =: guillemotleft",
|
64
|
+
"greater greater =: guillemotright"]
|
65
|
+
|
66
|
+
# Metric information about a glyph. Does not contain the glyph
|
67
|
+
# (outlines) itself.
|
68
|
+
class Char < Font::Glyph
|
69
|
+
|
70
|
+
# fontnumber is used in Font class
|
71
|
+
attr_accessor :fontnumber
|
72
|
+
|
73
|
+
# If not nil, _mapto_ is the glyphname that should be used instead
|
74
|
+
# of the current one.
|
75
|
+
attr_accessor :mapto
|
76
|
+
|
77
|
+
# Sets the extension factor. This is used by calculations of _wx_,
|
78
|
+
# _llx_ and _urx_.
|
79
|
+
attr_accessor :efactor
|
80
|
+
|
81
|
+
# Sets the slant factor. This is used by calculations of _wx_,
|
82
|
+
# _llx_ and _urx_.
|
83
|
+
attr_accessor :slant
|
84
|
+
|
85
|
+
def wx # :nodoc:
|
86
|
+
transform(@wx,0)
|
87
|
+
end
|
88
|
+
def wx=(obj) # :nodoc:
|
89
|
+
@wx=obj
|
90
|
+
end
|
91
|
+
# Lower left x position of glyph.
|
92
|
+
def llx # :nodoc:
|
93
|
+
transform(@b[0],b[1])
|
94
|
+
end
|
95
|
+
|
96
|
+
# Upper right x position of glyph.
|
97
|
+
def urx # :nodoc:
|
98
|
+
transform(@b[2],ury)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def transform (x,y)
|
104
|
+
(@efactor * x + @slant * y)
|
105
|
+
end
|
106
|
+
|
107
|
+
end # class Char
|
108
|
+
|
109
|
+
|
110
|
+
# Represent the different ligatures possible in tfm.
|
111
|
+
class LIG
|
112
|
+
|
113
|
+
@@encligops = ["=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>"]
|
114
|
+
@@vpligops = ["LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/",
|
115
|
+
"/LIG/>", "/LIG/>>"]
|
116
|
+
@@symligops = [:lig, :"lig/", :"/lig", :"/lig/", :"lig/>", :"/lig>", :"/lig/>", :"/lig/>>"]
|
117
|
+
|
118
|
+
# First glyph of a two glyph sequence before it is turned into a
|
119
|
+
# ligature.
|
120
|
+
attr_accessor :left
|
121
|
+
|
122
|
+
# Second glyph of a two glyph sequence before it is turned into a
|
123
|
+
# ligature.
|
124
|
+
attr_accessor :right
|
125
|
+
|
126
|
+
# The ligature that gets inserterd instead of the left and right glyph.
|
127
|
+
attr_accessor :result
|
128
|
+
|
129
|
+
# <tt>[0, 1, 2, 3, 4, 5, 6, 7 ]</tt>
|
130
|
+
#
|
131
|
+
# <tt>[=: , |=: , |=:> , =:| , =:|>, |=:|, |=:|>, |=:|>> ]</tt>
|
132
|
+
#
|
133
|
+
# <tt>[LIG, /LIG, /LIG>, LIG/, LIG/>, /LIG/, /LIG/>, /LIG/>>]</tt>
|
134
|
+
attr_accessor :type
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
# call-seq:
|
139
|
+
# new
|
140
|
+
# new(left,[right,[result,[type]]])
|
141
|
+
# new(hash)
|
142
|
+
# new(otherlig)
|
143
|
+
#
|
144
|
+
# When called with left, right, result or type parameters, take
|
145
|
+
# these settings for the LIG object. When called with a hash as an
|
146
|
+
# argument, the keys should look like: :left,:right,:result,:type.
|
147
|
+
# When called with an existing LIG object, the values are taken
|
148
|
+
# from the old object.
|
149
|
+
def initialize(left=nil,right=nil,result=nil,type=nil)
|
150
|
+
case left
|
151
|
+
when Hash
|
152
|
+
[:left,:right,:result,:type].each { |sym|
|
153
|
+
if left.has_key?(sym)
|
154
|
+
self.send((sym.to_s+"=").to_sym,left[sym])
|
155
|
+
end
|
156
|
+
}
|
157
|
+
when LIG
|
158
|
+
[:left,:right,:result,:type].each { |sym|
|
159
|
+
self.send((sym.to_s+"=").to_sym,left.send(sym))
|
160
|
+
}
|
161
|
+
# warning!!!!! LIG accepts a String as well as Fixnum as
|
162
|
+
# parameters, this might have side effects!?
|
163
|
+
when Fixnum,nil,String
|
164
|
+
@left=left
|
165
|
+
@right=right
|
166
|
+
@result=result
|
167
|
+
@type=type
|
168
|
+
else
|
169
|
+
raise "unknown argument for new() in LIG: #{left}"
|
170
|
+
end
|
171
|
+
# test!
|
172
|
+
#unless @type.instance_of?(Fixnum)
|
173
|
+
# raise "type must be a fixnum"
|
174
|
+
#end
|
175
|
+
end
|
176
|
+
def ==(lig)
|
177
|
+
@left=lig.left and
|
178
|
+
@right=lig.right and
|
179
|
+
@result=lig.result and
|
180
|
+
@type=lig.type
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_pl(encoding)
|
184
|
+
encoding.glyph_index[@right].sort.collect { |rightslot|
|
185
|
+
left=encoding.glyph_index[@left].min
|
186
|
+
# right=encoding.glyph_index[@right].min
|
187
|
+
result=encoding.glyph_index[@result].min
|
188
|
+
type=@@vpligops[@type]
|
189
|
+
LIG.new(:left=>left, :right=>rightslot, :result=>result, :type=>type)
|
190
|
+
}
|
191
|
+
end
|
192
|
+
# Return an array that is suitable for tfm
|
193
|
+
def to_tfminstr(encoding)
|
194
|
+
encoding.glyph_index[@right].sort.collect { |rightslot|
|
195
|
+
left=encoding.glyph_index[@left].min
|
196
|
+
# right=encoding.glyph_index[@right].min
|
197
|
+
result=encoding.glyph_index[@result].min
|
198
|
+
type=@@symligops[@type]
|
199
|
+
[type,rightslot,result]
|
200
|
+
}
|
201
|
+
end
|
202
|
+
def inspect
|
203
|
+
"[#{@type.to_s.upcase} #@left + #@right => #@result]"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
require 'forwardable'
|
208
|
+
|
209
|
+
# Stores information about kerning and ligature information. Allows
|
210
|
+
# deep copy of ligature and kerning information. Obsolete. Don't use.
|
211
|
+
class LigKern
|
212
|
+
extend Forwardable
|
213
|
+
# Optional parameter initializes the new LigKern object.
|
214
|
+
def initialize(h={})
|
215
|
+
@h=h
|
216
|
+
end
|
217
|
+
|
218
|
+
def_delegators(:@h, :each, :[], :[]=,:each_key,:has_key?)
|
219
|
+
|
220
|
+
def initialize_copy(obj) # :nodoc:
|
221
|
+
tmp={}
|
222
|
+
if obj[:lig]
|
223
|
+
tmp[:lig]=Array.new
|
224
|
+
obj[:lig].each { |elt|
|
225
|
+
tmp[:lig].push(elt.dup)
|
226
|
+
}
|
227
|
+
end
|
228
|
+
if obj[:krn]
|
229
|
+
tmp[:krn]=Array.new
|
230
|
+
obj[:krn].each { |elt|
|
231
|
+
tmp[:krn].push(elt.dup)
|
232
|
+
}
|
233
|
+
end
|
234
|
+
if obj[:alias]
|
235
|
+
tmp[:alias]=obj[:alias].dup
|
236
|
+
end
|
237
|
+
@h=tmp
|
238
|
+
end
|
239
|
+
# Compare this object to another object of the same class.
|
240
|
+
def ==(obj)
|
241
|
+
return false unless obj.respond_to?(:each)
|
242
|
+
# the krn needs to be compared one by one, because they are floats
|
243
|
+
if obj.has_key?(:krn)
|
244
|
+
obj[:krn].each { |destchar,value|
|
245
|
+
return false unless @h[:krn].assoc(destchar)
|
246
|
+
return false if (value - @h[:krn].assoc(destchar)[1]).abs > 0.01
|
247
|
+
}
|
248
|
+
end
|
249
|
+
obj.each { |key,value|
|
250
|
+
next if key==:krn
|
251
|
+
return false unless @h[key]==value
|
252
|
+
}
|
253
|
+
true
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
# The Glyphlist is a actually a Hash with some special methods
|
259
|
+
# attached.
|
260
|
+
class Glyphlist < Hash
|
261
|
+
@@encligops = ["=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>"]
|
262
|
+
@@vpligops = ["LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/",
|
263
|
+
"/LIG/>", "/LIG/>>"]
|
264
|
+
|
265
|
+
# Return an array with name of glyphs that are represented by the
|
266
|
+
# symbol _glyphlist_.
|
267
|
+
# These symbols are defined: :lowercase, :uppercase, :digits
|
268
|
+
def get_glyphlist(glyphlist)
|
269
|
+
ret=[]
|
270
|
+
unless glyphlist.instance_of? Symbol
|
271
|
+
raise ArgumentError, "glyphlist must be a symbol"
|
272
|
+
end
|
273
|
+
case glyphlist
|
274
|
+
when :lowercase
|
275
|
+
update_uc_lc_list
|
276
|
+
|
277
|
+
self.each { |glyphname,char|
|
278
|
+
if char.uc != nil
|
279
|
+
ret.push glyphname
|
280
|
+
end
|
281
|
+
}
|
282
|
+
when :uppercase
|
283
|
+
update_uc_lc_list
|
284
|
+
|
285
|
+
self.each { |glyphname,char|
|
286
|
+
if char.lc != nil
|
287
|
+
ret.push glyphname
|
288
|
+
end
|
289
|
+
}
|
290
|
+
when :digits
|
291
|
+
ret=%w(one two three four five six seven eight nine zero)
|
292
|
+
end
|
293
|
+
ret
|
294
|
+
end
|
295
|
+
|
296
|
+
# instructions.each must yield string objects (i.e. an array of
|
297
|
+
# strings, an IO object, a single string, ...). Instruction is like:
|
298
|
+
# "space l =: lslash" or "two {} *"
|
299
|
+
def apply_ligkern_instructions (instructions)
|
300
|
+
instructions.each { |instr|
|
301
|
+
s = instr.split(' ')
|
302
|
+
|
303
|
+
if @@encligops.member?(s[2]) # one of =:, |=: |=:> ...
|
304
|
+
if self[s[0]]
|
305
|
+
self[s[0]].lig_data[s[1]]=LIG.new(s[0],s[1],s[3],@@encligops.index(s[2]))
|
306
|
+
else
|
307
|
+
# puts "glyphlist#apply_ligkern_instructions: char not found: #{s[0]}"
|
308
|
+
end
|
309
|
+
elsif s[1] == "{}"
|
310
|
+
remove_kern(s[0],s[2])
|
311
|
+
end
|
312
|
+
}
|
313
|
+
end
|
314
|
+
# _left_ and _right_ must be either a glyphname or a '*'
|
315
|
+
# (asterisk) which acts like a wildcard. So ('one','*') would
|
316
|
+
# remove all kerns of glyph 'one' where 'one' is the left glyph in
|
317
|
+
# a kerning pair.
|
318
|
+
def remove_kern(left,right)
|
319
|
+
raise ArgumentError, "Only one operand may be '*'" if left=='*' and right=='*'
|
320
|
+
if right == "*"
|
321
|
+
self[left].kern_data={} if self[left]
|
322
|
+
elsif left == "*"
|
323
|
+
if self[right]
|
324
|
+
self.each { |name,chardata|
|
325
|
+
chardata.kern_data.delete(right)
|
326
|
+
}
|
327
|
+
end
|
328
|
+
else
|
329
|
+
if self[right] and self[left]
|
330
|
+
self[left].kern_data.delete(right)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# Update all glyph entries to see what the uppercase or the
|
336
|
+
# lowercase variants are. Warning!! Tcaron <-> tquoteright in
|
337
|
+
# non-unicode fonts.
|
338
|
+
def update_uc_lc_list
|
339
|
+
# we need this list only when faking small caps (which will, of
|
340
|
+
# course, never happen!)
|
341
|
+
|
342
|
+
# make a list of all uppercase and lowercase glyphs. Be aware of
|
343
|
+
# ae<->AE, oe<->OE, germandbls<-->SS, dotlessi->I, dotlessj->J
|
344
|
+
# do the
|
345
|
+
# @upper_lower={}
|
346
|
+
# @lower_upper={}
|
347
|
+
self.each_key {|glyphname|
|
348
|
+
thischar=self[glyphname]
|
349
|
+
if glyphname =~ /^[a-z]/
|
350
|
+
if glyphname =~ /^(a|o)e$/ and self[glyphname.upcase]
|
351
|
+
thischar.uc = glyphname.upcase
|
352
|
+
elsif glyphname =~ /^dotless(i|j)$/
|
353
|
+
thischar.uc = glyphname[-1].chr.upcase
|
354
|
+
elsif self[glyphname.capitalize]
|
355
|
+
thischar.uc = glyphname.capitalize
|
356
|
+
end
|
357
|
+
else
|
358
|
+
if glyphname =~ /^(A|O)e$/ and self[glyphname.dowcase]
|
359
|
+
thischar.lc = glyphname.downcase
|
360
|
+
elsif self[glyphname.downcase]
|
361
|
+
thischar.lc = glyphname.downcase
|
362
|
+
end
|
363
|
+
end
|
364
|
+
}
|
365
|
+
if self['germandbls']
|
366
|
+
self['germandbls'].uc='S'
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
# Modify the charmetrics and the kerning/ligatures so that the
|
371
|
+
# lowercase chars are made from scaling uppercase chars.
|
372
|
+
def fake_caps (factor)
|
373
|
+
update_uc_lc_list
|
374
|
+
# we need to do the following
|
375
|
+
# 1. adapt kerning pairs
|
376
|
+
# 2. change font metrics (wd)
|
377
|
+
# 3. remove ligatures from sc
|
378
|
+
@fake_caps=true
|
379
|
+
@capheight=factor
|
380
|
+
self.each { |glyphname,char|
|
381
|
+
if char.is_lowercase?
|
382
|
+
# remove ligatures from sc
|
383
|
+
char.lig_data={}
|
384
|
+
char.kern_data={}
|
385
|
+
char.mapto=char.capitalize
|
386
|
+
self[char.uc].kern_data.each { |destglyph,kerndata|
|
387
|
+
unless self[destglyph].is_lowercase?
|
388
|
+
char.kern_data[destglyph.downcase]=[kerndata[0] * factor,0]
|
389
|
+
end
|
390
|
+
}
|
391
|
+
char.b = self[char.capitalize].b.clone
|
392
|
+
char.wx = self[char.capitalize].wx * @capheight
|
393
|
+
char.lly *= @capheight
|
394
|
+
char.urx *= @capheight
|
395
|
+
char.ury *= @capheight
|
396
|
+
else # char is something like Aring, semicolon, ...
|
397
|
+
# if destchar is uppercase letter (A, Aring, ...)
|
398
|
+
# 1. delete all kerns to lowercase letters (not e.g. semicolon)
|
399
|
+
# 2. duplicate all uc kerns, multiply by factor and insert this
|
400
|
+
# as lc kern
|
401
|
+
char.kern_data.delete_if { |destglyph,kerndata|
|
402
|
+
self[destglyph].is_lowercase?
|
403
|
+
}
|
404
|
+
|
405
|
+
new_kern_data={}
|
406
|
+
char.kern_data.each { |destglyph,kerndata|
|
407
|
+
if self[destglyph].is_uppercase?
|
408
|
+
new_kern_data[self[destglyph].downcase]=[kerndata[0]*factor,kerndata[1]]
|
409
|
+
end
|
410
|
+
new_kern_data[destglyph]=kerndata
|
411
|
+
}
|
412
|
+
char.kern_data=new_kern_data
|
413
|
+
end
|
414
|
+
# 2.
|
415
|
+
}
|
416
|
+
if self['germandbls']
|
417
|
+
s=self['S']
|
418
|
+
d=self['germandbls']
|
419
|
+
|
420
|
+
d.b = s.b.dup
|
421
|
+
d.wx = s.wx * 2 * @capheight
|
422
|
+
d.urx += s.wx
|
423
|
+
|
424
|
+
|
425
|
+
d.kern_data={}
|
426
|
+
s.kern_data.each { |destglyph,kerndata|
|
427
|
+
unless self[destglyph].is_lowercase?
|
428
|
+
# we are looking at non-lowercase chars. These might be
|
429
|
+
# ones that are uppercase or are 'something else', e.g.
|
430
|
+
# hyphen...
|
431
|
+
# since we only replace the lc variants, keep the uc and
|
432
|
+
# others intact.
|
433
|
+
if self[destglyph].is_uppercase?
|
434
|
+
d.kern_data[self[destglyph].downcase]=[kerndata[0] * @capheight,0]
|
435
|
+
|
436
|
+
else
|
437
|
+
d.kern_data[destglyph]=[kerndata[0] * @capheight,0]
|
438
|
+
|
439
|
+
end
|
440
|
+
end
|
441
|
+
}
|
442
|
+
|
443
|
+
d.pcc_data=[['S',0,0],['S',s.wx,0]]
|
444
|
+
d.lly *= @capheight
|
445
|
+
d.urx *= @capheight
|
446
|
+
|
447
|
+
end
|
448
|
+
end # fake_caps
|
449
|
+
|
450
|
+
def fix_height(xheight)
|
451
|
+
# this is what afm2tfm does. I am not sure if it is clever.
|
452
|
+
self.each { |name,data|
|
453
|
+
|
454
|
+
# xheight <= 50 -> @chars[char].ury
|
455
|
+
# char.size > 1 -> @chars[char].ury
|
456
|
+
# char+accentname (ntilde, udieresis,...) exists?
|
457
|
+
# then calculate else @chars[char].ury
|
458
|
+
# calculate := ntilde.ury - tilde.ury + xheight
|
459
|
+
# same as texheight in afm2tfm source
|
460
|
+
unless name.size>1 or xheight < 50
|
461
|
+
%w(acute tilde caron dieresis).each {|accent|
|
462
|
+
naccent=name + accent
|
463
|
+
next unless self[naccent]
|
464
|
+
data.ury = self[naccent].ury - self[accent].ury + xheight
|
465
|
+
break
|
466
|
+
}
|
467
|
+
end
|
468
|
+
}
|
469
|
+
end # fix_height
|
470
|
+
end # class Glyphlist
|
471
|
+
end # class RFI
|
472
|
+
end
|