1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
#--
# enc.rb - read and parse TeX's encoding files
# Last Change: Tue May 16 17:24:31 2006
#++
# See the class ENC for the api description.
require 'strscan'
require 'set'
require 'forwardable'
module RFIL
module TeX
# = ENC -- Access encoding files
#
# == General information
#
# Read a TeX encoding vector (<tt>.enc</tt>-file) and associated
# ligkern instructions. The encoding slot are accessible via <em>[]</em>
# and <em>[]=</em>, just like an Array.
#
# == Example usage
#
# === Read an encoding file
# filename = "/opt/tetex/3.0/texmf/fonts/enc/dvips/base/EC.enc"
# File.open(filename) { |encfile|
# enc=ENC.new(encfile)
# enc.encname # => "ECEncoding"
# enc # => ['grave','acute',...]
# enc.filename # => "EC.enc"
# enc.ligkern_instructions # => ["space l =: lslash","space L =: Lslash",... ]
# }
# === Create an encoding
# enc=ENC.new
# enc.encname="Exampleenc"
# enc[0]="grave"
# # all undefined slots are ".notdef"
# ....
#
# # write encoding to <tt>new.enc</tt>
# File.open("new.enc") do |f|
# f << enc.to_s
# end
# ---
# Remark: This interface is pretty much fixed.
#--
# dont't subclass Array directly, it might be a bad idea. See for
# example [ruby-talk:147327]
#++
class ENC # < DelegateClass(Array)
def self.documented_as_accessor(*args) # :nodoc:
end
extend Forwardable
def_delegators(:@encvector, :size, :[],:each, :each_with_index)
# _encname_ is the PostScript name of the encoding vector.
attr_accessor :encname
# ligkern_instructions is an array of strings (instructions) as
# found in the encoding file, such as:
# "quoteright quoteright =: quotedblright"
# "* {} space"
attr_accessor :ligkern_instructions
# Hash: key is glyph name, value is a Set of indexes.
# Example: glyph_index['hyphen']=#<Set: {45, 127}> in
# <tt>ec.enc</tt>. Automatically updated when changing the encoding
# vector via <em>[]=</em>.
attr_reader :glyph_index
# Filename of the encoding vector. Used for creating mapfile
# entries. Always ends with ".enc" if read (unless it is unset).
documented_as_accessor :filename
# Optional enc is either a File object or a string with the contents
# of a file. If set, the object is initialized with the given
# encoding vector.
def initialize (enc=nil)
@glyph_index={}
@ligkern_instructions=[]
# File, Tempfile, IO respond to read
if enc
@encvector=[]
string = enc.respond_to?(:read) ? enc.read : enc
if enc.respond_to?(:path)
self.filename= enc.path
end
parse(string)
else
@encvector=Array.new(256,".notdef")
end
end
def filename # :nodoc:
@filename
end
def filename=(fn) # :nodoc:
@filename=File.basename(fn.chomp(".enc")+".enc")
end
# Return true if the encoding name and the encoding Array are the
# same. If _obj_ is an Array, only compare the Array elements.
def ==(obj)
return false if obj==nil
if obj.instance_of?(ENC)
return false unless @encname==obj.encname
end
return false unless obj.respond_to?(:[])
0.upto(255) { |i|
return false if @encvector[i]!=obj[i]
}
true
end
# todo: document and test
def -(obj)
tmp=[]
for i in 0..255
tmp[i]=obj[i]
end
@encvector - tmp
end
# also updates the glyph_index
def []=(i,obj) # :nodoc:
if obj==nil and @encvector[i] != nil
@glyph_index.delete(@encvector[i])
return obj
end
@encvector[i]=obj
addtoindex(obj,i)
return obj
end
# Return a string representation of the encoding that is compatible
# with dvips and alike.
def to_s
str = ""
@ligkern_instructions.each { |instr|
str << "% LIGKERN #{instr} ;\n"
}
str << "%\n"
str << "/#@encname [\n"
@encvector.each_with_index { |glyphname,i|
str << "% #{i}\n" if (i % 16 == 0)
str << " " unless (i % 8 == 0)
str << "/#{glyphname}"
str << "\n" if (i % 8 == 7)
}
str << "] def\n"
str
end
#######
private
#######
# creates the glyph_index from the encvector. Use this method after
# you made changes to the encvector.
def update_glyph_index
@encvector.each_with_index { |name,i|
next if name==".notdef"
addtoindex(name,i)
}
end
# Adds position i to glyph_index for glyph _glyph_.
def addtoindex(glyph,i)
return if glyph==".notdef"
if @glyph_index[glyph]
@glyph_index[glyph].add i
else
@glyph_index[glyph]=Set.new().add(i)
end
end
# return the next postscript element (e.g. /name or [ )
def tok(s)
unless s.peek(1) == "/"
s.skip_until(/[^\/\[\]]+/) # not '/' '[' or ']'
end
s.scan(/(?:\/\.?\w+|\[|\])/)
end
# fill Array with contents of string.
def parse(str)
count=0
s=StringScanner.new(str)
ligkern=""
while s.skip_until(/^%\s+LIGKERN\s+/)
ligkern << s.scan_until(/$/)
end
ligkern.split(';').each { |instruction|
@ligkern_instructions.push instruction.strip
}
s.string=(str.gsub(/%.*/,''))
t=tok(s)
@encname=t[1,t.length-1]
loop do
t = tok(s)
case t
when "["
# ignore
when "]"
unless @encvector.size == 256
raise "Unexpected size of encoding. It should contain 256 entries, but has #{@encvector.size} entries."
end
update_glyph_index
return
else
name = t[1,t.length-1]
@encvector.push(name)
end
end
# never reached
raise "Internal ENC error"
end
end #class Enc
end
end
|