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
@@ -0,0 +1,414 @@
|
|
1
|
+
# Last Change: Mi 24 Mai 2006 16:42:25 CEST
|
2
|
+
|
3
|
+
# require 'rfi'
|
4
|
+
|
5
|
+
require 'strscan'
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
require 'rfil/font/metric'
|
9
|
+
|
10
|
+
module RFIL # :nodoc:
|
11
|
+
module Font # :nodoc:
|
12
|
+
# = AFM -- Access type1 font metric files
|
13
|
+
#
|
14
|
+
# == General information
|
15
|
+
#
|
16
|
+
# Read and parse a (type1) afm file. The afm file must be compliant to
|
17
|
+
# the afm specification as described in 'Adobe Font Metrics File
|
18
|
+
# Format Specification' Version 4.1, dated October 7 1998.
|
19
|
+
#
|
20
|
+
# == Example usage
|
21
|
+
#
|
22
|
+
# === Read an afm file
|
23
|
+
# filename = "/opt/tetex/3.0/texmf/fonts/afm/urw/palatino/uplb8a.afm"
|
24
|
+
# afm=AFM.new
|
25
|
+
# afm.read(filename)
|
26
|
+
# afm.filename # => "/opt/..../uplb8a.afm"
|
27
|
+
# afm.count_charmetrics # => 316
|
28
|
+
# afm.encodingscheme # => "AdobeStandardEncoding"
|
29
|
+
# # ....
|
30
|
+
#
|
31
|
+
class AFM < Metric
|
32
|
+
|
33
|
+
# This is set to true if there is something wrong in the afm file.
|
34
|
+
# Diagnostics can be turned on with <tt>:verbose</tt> set to true
|
35
|
+
# when creating the object.
|
36
|
+
attr_reader :something_strange
|
37
|
+
|
38
|
+
# Number of characters found in the afm file.
|
39
|
+
attr_accessor :count_charmetrics
|
40
|
+
|
41
|
+
# Number of encoded character found in the afm file.
|
42
|
+
attr_accessor :count_charmetrics_encoded
|
43
|
+
|
44
|
+
# Number of unencoded character found in the afm file.
|
45
|
+
attr_accessor :count_charmetrics_unencoded
|
46
|
+
|
47
|
+
# The default encoding of the font.
|
48
|
+
attr_accessor :encodingscheme
|
49
|
+
|
50
|
+
# Boundingbox of the font. Array of for elements.
|
51
|
+
attr_accessor :fontbbox
|
52
|
+
|
53
|
+
# Underline position of the font.
|
54
|
+
attr_accessor :underlineposition
|
55
|
+
|
56
|
+
# Underline thickness.
|
57
|
+
attr_accessor :underlinethickness
|
58
|
+
|
59
|
+
# Height of caps.
|
60
|
+
attr_accessor :capheight
|
61
|
+
|
62
|
+
# Height of ascender.
|
63
|
+
attr_accessor :ascender
|
64
|
+
|
65
|
+
# Height of descender.
|
66
|
+
attr_accessor :descender
|
67
|
+
|
68
|
+
|
69
|
+
# Create an empty afm file. If _afm_ is set, use this to initialize
|
70
|
+
# the object. _afm_ is either a string with the contents of an afm
|
71
|
+
# file or a File object that points to the afm file. _options_
|
72
|
+
# currently only accepts <tt>:verbose</tt> (true/false), that prints
|
73
|
+
# out some diagnostic information on STDERR.
|
74
|
+
def initialize(options={})
|
75
|
+
@something_strange = false
|
76
|
+
super()
|
77
|
+
@outlinetype=:type1
|
78
|
+
@comment = ""
|
79
|
+
@verbose=options[:verbose]==true
|
80
|
+
end
|
81
|
+
|
82
|
+
# return a Hash of all relevant filenames. Keys are outlinetypes,
|
83
|
+
def fontfilenames
|
84
|
+
f=File.basename(@fontfilename || filename)
|
85
|
+
sans_ext=case f
|
86
|
+
when /\.afm$/
|
87
|
+
f.chomp(".afm")
|
88
|
+
when /\.pfb$/
|
89
|
+
f.chomp("pfb")
|
90
|
+
end
|
91
|
+
return {:afm => sans_ext + ".afm", :type1 => sans_ext + ".pfb"}
|
92
|
+
end
|
93
|
+
|
94
|
+
# Read the afm file given with _filename_. _filename_ must be full
|
95
|
+
# path to the afm file, it does not perform any lookups. Returns self.
|
96
|
+
def read (filename)
|
97
|
+
@filename=File.basename(filename)
|
98
|
+
@name=@filename.chomp(".afm")
|
99
|
+
self.pathname=Pathname.new(filename).realpath.to_s
|
100
|
+
parse(File.read(filename))
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return a string representation of the afm file that is compliant
|
104
|
+
# with the afm spec.
|
105
|
+
def to_s
|
106
|
+
s ="StartFontMetrics 2.0\n"
|
107
|
+
s << "Comment Generated using the RFI Library\n"
|
108
|
+
%w( FontName FullName FamilyName Weight Notice ItalicAngle
|
109
|
+
IsFixedPitch UnderlinePosition UnderlineTickness Version
|
110
|
+
EncodingScheme CapHeight XHeight Descender Ascender ).each {|kw|
|
111
|
+
|
112
|
+
meth=kw.downcase.to_sym
|
113
|
+
value=self.send(meth) if self.respond_to?(meth)
|
114
|
+
if value
|
115
|
+
s << kw << " " << value.to_s << "\n"
|
116
|
+
end
|
117
|
+
}
|
118
|
+
s << "FontBBox " << @fontbbox.join(" ") << "\n"
|
119
|
+
s << "StartCharMetrics #@count_charmetrics\n"
|
120
|
+
@chars.sort{ |a,b|
|
121
|
+
# puts "a=#{a[1].c}, b=#{b[1].c}"
|
122
|
+
if a[1].c == -1
|
123
|
+
b[1].c == -1 ? 0 : 1
|
124
|
+
else
|
125
|
+
b[1].c == -1 ? -1 : a[1].c <=> b[1].c
|
126
|
+
end
|
127
|
+
}.each { |a,b|
|
128
|
+
s << "C #{b.c} ; WX #{b.wx} ; N #{a} ; B #{b.b.join(" ")}\n"
|
129
|
+
}
|
130
|
+
s << "EndCharMetrics\nStartKernData\nStartKernPairs"
|
131
|
+
count=0
|
132
|
+
@chars.each_value { |c|
|
133
|
+
count += c.kern_data.size
|
134
|
+
}
|
135
|
+
s << " #{count}\n"
|
136
|
+
@chars.sort{ |a,b| a[0] <=> b[0] }.each { |name,char|
|
137
|
+
char.kern_data.each { |destname, value|
|
138
|
+
s << "KPX #{name} #{destname} #{value[0]}\n"
|
139
|
+
}
|
140
|
+
}
|
141
|
+
s << "EndKernPairs\nEndKernData\nEndFontMetrics\n"
|
142
|
+
s
|
143
|
+
end
|
144
|
+
|
145
|
+
# Parse the contents of the String _txt_. Returns self.
|
146
|
+
def parse(txt)
|
147
|
+
@chars ||= Hash.new
|
148
|
+
@s=StringScanner.new(txt.gsub(/\r\n/,"\n"))
|
149
|
+
@s.scan(/StartFontMetrics/)
|
150
|
+
get_fontmetrics
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
#######
|
155
|
+
private
|
156
|
+
#######
|
157
|
+
|
158
|
+
def get_keyword
|
159
|
+
@s.skip_until(/\s+/)
|
160
|
+
@s.scan(/[A-Z][A-Za-z0-9]+/)
|
161
|
+
end
|
162
|
+
|
163
|
+
def get_integer
|
164
|
+
@s.skip(/\s+/)
|
165
|
+
@s.scan(/-?\d+/).to_i
|
166
|
+
end
|
167
|
+
|
168
|
+
def get_number
|
169
|
+
@s.skip(/\s+/)
|
170
|
+
@s.scan(/-?\d+(?:\.\d+)?/).to_f
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_boolean
|
174
|
+
@s.skip(/\s+/)
|
175
|
+
@s.scan(/(true|false)/) == 'true'
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_name
|
179
|
+
@s.skip(/\s+/)
|
180
|
+
@s.scan(/[^\s]+/)
|
181
|
+
end
|
182
|
+
|
183
|
+
def get_string
|
184
|
+
@s.skip(/\s+/)
|
185
|
+
@s.scan(/.*/)
|
186
|
+
end
|
187
|
+
|
188
|
+
def get_fontmetrics
|
189
|
+
@version = get_number
|
190
|
+
loop do
|
191
|
+
kw=get_keyword
|
192
|
+
STDERR.puts "KW: " + kw if @verbose
|
193
|
+
case kw
|
194
|
+
when "FontName"
|
195
|
+
@fontname=get_string
|
196
|
+
when "FamilyName"
|
197
|
+
@familyname = get_string
|
198
|
+
when "FullName"
|
199
|
+
@fullname = get_string
|
200
|
+
when "EncodingScheme"
|
201
|
+
@encodingscheme = get_string
|
202
|
+
when "ItalicAngle"
|
203
|
+
@italicangle = get_number
|
204
|
+
when "IsFixedPitch"
|
205
|
+
@isfixedpitch = get_boolean
|
206
|
+
when "Weight"
|
207
|
+
@weight = get_string
|
208
|
+
when "XHeight"
|
209
|
+
@xheight= get_number
|
210
|
+
when "Comment"
|
211
|
+
@comment << get_string << "\n"
|
212
|
+
when "FontBBox"
|
213
|
+
@fontbbox = [get_number,get_number, get_number, get_number]
|
214
|
+
when "Version"
|
215
|
+
@version = get_string
|
216
|
+
when "Notice"
|
217
|
+
@notice = get_string
|
218
|
+
when "MappingScheme"
|
219
|
+
@mappingscheme = get_integer
|
220
|
+
when "EscChar"
|
221
|
+
@escchar = get_integer
|
222
|
+
when "CharacterSet"
|
223
|
+
@characterset = get_string
|
224
|
+
when "Characters"
|
225
|
+
@characters = get_integer
|
226
|
+
when "IsBaseFont"
|
227
|
+
@isbasefont = get_boolean
|
228
|
+
when "VVector"
|
229
|
+
@vvector = [get_number,get_number]
|
230
|
+
when "IsFixedV"
|
231
|
+
@isfixedv = get_boolean
|
232
|
+
when "CapHeight"
|
233
|
+
@capheight = get_number
|
234
|
+
when "Ascender"
|
235
|
+
@ascender = get_number
|
236
|
+
when "Descender"
|
237
|
+
@descender = get_number
|
238
|
+
when "UnderlinePosition"
|
239
|
+
@underlineposition = get_number
|
240
|
+
when "UnderlineThickness"
|
241
|
+
@underlinethickness = get_number
|
242
|
+
when "StartDirection"
|
243
|
+
get_direction
|
244
|
+
when "StartCharMetrics"
|
245
|
+
get_charmetrics
|
246
|
+
when "StartKernData"
|
247
|
+
get_kerndata
|
248
|
+
when "StartComposites"
|
249
|
+
get_composites
|
250
|
+
when "EndFontMetrics"
|
251
|
+
break
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
def get_direction
|
256
|
+
# ignored
|
257
|
+
end
|
258
|
+
def get_charmetrics
|
259
|
+
@count_charmetrics = get_integer
|
260
|
+
@count_charmetrics_encoded = 0
|
261
|
+
@count_charmetrics_unencoded = 0
|
262
|
+
loop do
|
263
|
+
@s.skip_until(/\n/)
|
264
|
+
nextstring = @s.scan_until(/(?:StopCharMetrics|.*)/)
|
265
|
+
return if nextstring=="EndCharMetrics"
|
266
|
+
a=nextstring.split(';')
|
267
|
+
# ["C 32 ", " WX 250 ", " N space ", " B 125 0 125 0 "]
|
268
|
+
a.collect! { |elt|
|
269
|
+
elt.strip.split(/ /,2)
|
270
|
+
}
|
271
|
+
# [["C", "32"], ["WX", "250"], ["N", "space"], ["B", "125 0 125 0"]]
|
272
|
+
char=new_glyph
|
273
|
+
a.each { |elt|
|
274
|
+
key,value = elt
|
275
|
+
case key
|
276
|
+
when "N"
|
277
|
+
char.name=value
|
278
|
+
when "B"
|
279
|
+
#special treatment for bounding box
|
280
|
+
char.b = value.split.collect { |e| e.to_i }
|
281
|
+
char.llx = char.llx
|
282
|
+
char.urx = char.urx
|
283
|
+
# We need to avoid negative heights or depths. They break
|
284
|
+
# accents in math mode, among other things.
|
285
|
+
char.lly = 0 if char.lly > 0
|
286
|
+
char.ury = 0 if char.ury < 0
|
287
|
+
when "C"
|
288
|
+
char.c = value.to_i
|
289
|
+
when "CH"
|
290
|
+
# hex: '<20>' -> '0x20' -> .to_i -> 32
|
291
|
+
char.c = value.sub(/</,'0x').sub(/>/,'').to_i(16)
|
292
|
+
when "WX"
|
293
|
+
char.wx = value.to_i
|
294
|
+
# for "L", check /var/www/mirror/system/tex/texmf-local/fonts/afm/jmn/hans/hans.afm
|
295
|
+
when "L", nil
|
296
|
+
#ignore
|
297
|
+
else
|
298
|
+
char.send((key.downcase + "=").to_sym,value.to_i)
|
299
|
+
end
|
300
|
+
}
|
301
|
+
|
302
|
+
@chars[char.name]=char
|
303
|
+
# update information about encoded/unencoded
|
304
|
+
if char.c > -1
|
305
|
+
@count_charmetrics_encoded += 1
|
306
|
+
else
|
307
|
+
@count_charmetrics_unencoded += 1
|
308
|
+
end
|
309
|
+
end
|
310
|
+
raise "never reached"
|
311
|
+
end
|
312
|
+
def get_kerndata
|
313
|
+
loop do
|
314
|
+
kw = get_keyword
|
315
|
+
STDERR.puts "kw=" + kw if @verbose
|
316
|
+
case kw
|
317
|
+
when "EndKernData"
|
318
|
+
return
|
319
|
+
when "StartKernPairs"
|
320
|
+
get_kernpairs
|
321
|
+
when "StartTrackKern"
|
322
|
+
# TrackKern
|
323
|
+
get_trackkern
|
324
|
+
else
|
325
|
+
# KernPairs0
|
326
|
+
# KernPairs1
|
327
|
+
raise "not implemented"
|
328
|
+
end
|
329
|
+
end
|
330
|
+
raise "never reached"
|
331
|
+
end
|
332
|
+
def get_composites
|
333
|
+
count = get_integer
|
334
|
+
loop do
|
335
|
+
kw = get_keyword
|
336
|
+
STDERR.puts "get_composites keyword = '" + kw + "'" if @verbose
|
337
|
+
case kw
|
338
|
+
when "CC"
|
339
|
+
get_composite
|
340
|
+
when "EndComposites"
|
341
|
+
return
|
342
|
+
else
|
343
|
+
STDERR.puts "next to read = " + @s.string[@s.pos,40]
|
344
|
+
raise "AFM error"
|
345
|
+
end
|
346
|
+
end
|
347
|
+
raise "never reached"
|
348
|
+
end
|
349
|
+
def get_composite
|
350
|
+
glyphname = get_name
|
351
|
+
count = get_integer
|
352
|
+
@s.skip_until(/;\s+/)
|
353
|
+
count.times do
|
354
|
+
nextstring = get_name
|
355
|
+
raise "AFM Error" unless nextstring == "PCC"
|
356
|
+
[get_number,get_number]
|
357
|
+
@s.skip_until(/;/)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def get_trackkern
|
362
|
+
count = get_integer
|
363
|
+
loop do
|
364
|
+
case get_keyword
|
365
|
+
when "EndTrackKern"
|
366
|
+
return
|
367
|
+
when "TrackKern"
|
368
|
+
# TrackKern degree min-ptsize min-kern max-ptsize max-kern
|
369
|
+
[get_integer,get_number,get_number,get_number,get_number]
|
370
|
+
else
|
371
|
+
raise "afm error"
|
372
|
+
end
|
373
|
+
end
|
374
|
+
raise "never reached"
|
375
|
+
end
|
376
|
+
|
377
|
+
def get_kernpairs
|
378
|
+
count = get_integer
|
379
|
+
loop do
|
380
|
+
case get_keyword
|
381
|
+
when "KPX" # y is 0
|
382
|
+
name=get_name
|
383
|
+
# if @info['chars'][name]
|
384
|
+
if @chars[name]
|
385
|
+
# array is [x,y] kerning
|
386
|
+
destname,num=get_name,get_number
|
387
|
+
# somethimes something stupid like
|
388
|
+
# KPX .notdef y -26
|
389
|
+
# KPX A .notdef -43
|
390
|
+
# is in the afm data... :-( -> reject those entries
|
391
|
+
# if @info['chars'][destname]
|
392
|
+
if @chars[destname]
|
393
|
+
@chars[name].kern_data[destname] = [num,0]
|
394
|
+
else
|
395
|
+
STDERR.puts "info: unused kern data for " + name if @verbose
|
396
|
+
end
|
397
|
+
else
|
398
|
+
# ignore this entry, print a message
|
399
|
+
STDERR.puts "info: unused kern data for " + name if @verbose
|
400
|
+
@something_strange=true
|
401
|
+
[get_name,get_number] # ignored
|
402
|
+
end
|
403
|
+
when "EndKernPairs"
|
404
|
+
return
|
405
|
+
else
|
406
|
+
STDERR.puts @s.pos
|
407
|
+
raise "not implmented"
|
408
|
+
end
|
409
|
+
end
|
410
|
+
raise "never reached"
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
|
2
|
+
module RFIL
|
3
|
+
module Font
|
4
|
+
class Glyph
|
5
|
+
|
6
|
+
# to make Rdoc and Ruby happy: [ruby-talk:147778]
|
7
|
+
def self.documented_as_accessor(*args) #:nodoc:
|
8
|
+
end
|
9
|
+
|
10
|
+
# Glyphname
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
# Advance width
|
14
|
+
attr_accessor :wx
|
15
|
+
|
16
|
+
# Standard code slot (0-255 or -1 for unencoded)
|
17
|
+
attr_accessor :c
|
18
|
+
|
19
|
+
# bounding box (llx, lly, urx, ury). Array of size 4.
|
20
|
+
# You should use the methods llx, lly, urx, ury to access the
|
21
|
+
# bounding box.
|
22
|
+
attr_accessor :b
|
23
|
+
|
24
|
+
# Kern_data (Hash). The key is the glyph name, the entries are
|
25
|
+
# _[x,y]_ arrays. For ltr and rtl typesetting only the x entry
|
26
|
+
# should be interesting. This is raw information from the font
|
27
|
+
# metric file. Does not change when efactor et al. are set in any
|
28
|
+
# way.
|
29
|
+
attr_accessor :kern_data
|
30
|
+
|
31
|
+
# Information about ligatures - unknown datatype yet
|
32
|
+
attr_accessor :lig_data
|
33
|
+
|
34
|
+
# Composite characters. Array [['glyph1',xshift,yshift],...]
|
35
|
+
attr_accessor :pcc_data
|
36
|
+
|
37
|
+
# Upper right x value of glyph.
|
38
|
+
documented_as_accessor :urx
|
39
|
+
|
40
|
+
# Upper right y value of glyph.
|
41
|
+
documented_as_accessor :ury
|
42
|
+
|
43
|
+
# Lower left x value of glyph.
|
44
|
+
documented_as_accessor :llx
|
45
|
+
|
46
|
+
# Lower left y value of glyph.
|
47
|
+
documented_as_accessor :lly
|
48
|
+
|
49
|
+
# the name of the uppercase glyph (nil if there is no uppercase glyph)
|
50
|
+
attr_accessor :uc
|
51
|
+
|
52
|
+
# the name of the lowercase glyph (nil if there is no lowercase glyph)
|
53
|
+
attr_accessor :lc
|
54
|
+
|
55
|
+
# Optional argument sets the name of the glyph.
|
56
|
+
def initialize (glyphname=nil)
|
57
|
+
@name=glyphname
|
58
|
+
@lig_data={}
|
59
|
+
@kern_data={}
|
60
|
+
@wx=0
|
61
|
+
@b=[0,0,0,0]
|
62
|
+
@efactor=1.0
|
63
|
+
@slant=0.0
|
64
|
+
end
|
65
|
+
|
66
|
+
# Lower left x position of glyph.
|
67
|
+
def llx # :nodoc:
|
68
|
+
@b[0]
|
69
|
+
end
|
70
|
+
def llx=(value) # :nodoc:
|
71
|
+
@b[0]=value
|
72
|
+
end
|
73
|
+
|
74
|
+
# Lower left y position of glyph.
|
75
|
+
def lly # :nodoc:
|
76
|
+
@b[1]
|
77
|
+
end
|
78
|
+
def lly=(value) # :nodoc:
|
79
|
+
@b[1]=value
|
80
|
+
end
|
81
|
+
# Upper right x position of glyph.
|
82
|
+
def urx # :nodoc:
|
83
|
+
@b[2]
|
84
|
+
end
|
85
|
+
def urx=(value) # :nodoc:
|
86
|
+
@b[2]=value
|
87
|
+
end
|
88
|
+
|
89
|
+
# Upper right y position of glyph.
|
90
|
+
def ury # :nodoc:
|
91
|
+
@b[3]
|
92
|
+
end
|
93
|
+
def ury=(value) # :nodoc:
|
94
|
+
@b[3]=value
|
95
|
+
end
|
96
|
+
|
97
|
+
# Return height of the char used for tfm file.
|
98
|
+
def charht
|
99
|
+
ury
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return width of the char used for tfm file.
|
103
|
+
def charwd
|
104
|
+
wx
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return depth of the char.
|
108
|
+
def chardp
|
109
|
+
lly >= 0 ? 0 : -lly
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return italic correction of the char.
|
113
|
+
def charic
|
114
|
+
(urx - wx) > 0 ? (urx - wx) : 0
|
115
|
+
end
|
116
|
+
# Return an array with all kerning information (x-direction only)
|
117
|
+
# of this glyph. Kerning information is an Array where first
|
118
|
+
# element is the destchar, the second element is the kerning amount.
|
119
|
+
def kerns_x
|
120
|
+
ret=[]
|
121
|
+
@kern_data.each { |destchar,kern|
|
122
|
+
ret.push([destchar,kern[0]])
|
123
|
+
}
|
124
|
+
ret
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return an array with all ligature information (LIG objects) of
|
128
|
+
# this glyph.
|
129
|
+
def ligs
|
130
|
+
ret=[]
|
131
|
+
@lig_data.each { |destchar,lig|
|
132
|
+
ret.push(lig)
|
133
|
+
}
|
134
|
+
ret
|
135
|
+
end
|
136
|
+
|
137
|
+
# Return true if this char has ligature or kerning information. If
|
138
|
+
# glyphindex is supplied, only return true if relevant. This means
|
139
|
+
# that the second parameter of a kerning information or the second
|
140
|
+
# parameter and the result of a ligature information must be in
|
141
|
+
# the glyphindex. glyphindex must respond to <em>include?</em>.
|
142
|
+
def has_ligkern?(glyphindex=nil)
|
143
|
+
if glyphindex and not glyphindex.respond_to? :include?
|
144
|
+
raise ArgumentError, "glyphindex does not respod to include?"
|
145
|
+
end
|
146
|
+
return false if (lig_data == {} and kern_data=={})
|
147
|
+
# this one is easy, just look at lig_data and kern_data
|
148
|
+
# more complicated, we have to take glyphindex into account
|
149
|
+
if glyphindex
|
150
|
+
return false unless glyphindex.include? self.name
|
151
|
+
# right kerningpair not in glyphindex? -> false
|
152
|
+
# right lig not in glyphindex? -> false
|
153
|
+
# result lig not in glyphindex? -> false
|
154
|
+
if lig_data
|
155
|
+
lig_data.each { |otherchar,lig|
|
156
|
+
if (glyphindex.include?(lig.right) and glyphindex.include?(lig.result))
|
157
|
+
return true
|
158
|
+
end
|
159
|
+
}
|
160
|
+
end
|
161
|
+
if kern_data
|
162
|
+
kern_data.each { |otherchar,krn|
|
163
|
+
return true if glyphindex.include?(otherchar)
|
164
|
+
}
|
165
|
+
end
|
166
|
+
return false
|
167
|
+
else
|
168
|
+
# no glyphindex
|
169
|
+
return true
|
170
|
+
end
|
171
|
+
raise "never reached"
|
172
|
+
end # has_ligkern?
|
173
|
+
|
174
|
+
# Return true if glyph is an uppercase char, such as AE.
|
175
|
+
def is_uppercase?
|
176
|
+
return @lc != nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# Return true if glyph is a lowercase char, such as germandbls,
|
180
|
+
# but not hyphen.
|
181
|
+
def is_lowercase?
|
182
|
+
return @uc != nil
|
183
|
+
end
|
184
|
+
|
185
|
+
# Return the uppercase variant of the glyph. Undefined behaviour if
|
186
|
+
# glyph cannot be uppercased.
|
187
|
+
def capitalize
|
188
|
+
@uc
|
189
|
+
end
|
190
|
+
|
191
|
+
# Return the lowercase variant of the glyph. Undefined behaviour if
|
192
|
+
# glyph cannot be lowercased.
|
193
|
+
def downcase
|
194
|
+
@lc
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|