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,90 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Plugin for RFIL to create a typescript usable for ConTeXt.
|
3
|
+
=end
|
4
|
+
|
5
|
+
# :enddoc:
|
6
|
+
|
7
|
+
class TypescriptWriterConTeXt < RFIL::RFI::Plugin
|
8
|
+
|
9
|
+
def initialize(fontcollection)
|
10
|
+
@fc=fontcollection
|
11
|
+
super(:context,:typescript)
|
12
|
+
end
|
13
|
+
|
14
|
+
STOPTYPESCRIPT="\\stoptypescript\n\n"
|
15
|
+
|
16
|
+
def run_plugin
|
17
|
+
ret=[]
|
18
|
+
str=""
|
19
|
+
puts "running context plugin" if @fc.options[:verbose]
|
20
|
+
@fc.texenc.each { |e|
|
21
|
+
str << typescript(e)
|
22
|
+
str << "\n"
|
23
|
+
}
|
24
|
+
h={}
|
25
|
+
h[:type]=:typescript
|
26
|
+
h[:filename],h[:contents]=["type-#{@fc.name}.tex",str]
|
27
|
+
ret.push(h)
|
28
|
+
ret
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns hash: Style, font
|
32
|
+
def find_fonts
|
33
|
+
ret={}
|
34
|
+
@fc.fonts.each { |font|
|
35
|
+
ret[""]=font if font.variant==:regular and font.weight==:regular
|
36
|
+
# ret[""]=font if font.variant==:regular and font.weight==:regular and font.style!=:sans
|
37
|
+
ret["Bold"]=font if font.variant==:regular and font.weight==:bold
|
38
|
+
ret["Italic"]=font if font.variant==:italic and font.weight==:regular
|
39
|
+
ret["Caps"]=font if font.variant==:smallcaps and font.weight==:regular
|
40
|
+
}
|
41
|
+
ret
|
42
|
+
end
|
43
|
+
def typescript(e)
|
44
|
+
contextenc=case e.encname
|
45
|
+
when "ECEncoding"
|
46
|
+
"ec"
|
47
|
+
when "TS1Encoding"
|
48
|
+
"ts1"
|
49
|
+
when "T1Encoding"
|
50
|
+
"tex256"
|
51
|
+
when "TeXBase1Encoding"
|
52
|
+
"8r"
|
53
|
+
else
|
54
|
+
raise "unknown context encoding: #{e.encname}"
|
55
|
+
end
|
56
|
+
# i know that this is crap, it's just a start
|
57
|
+
contextstyle=case @fc.style
|
58
|
+
when :sans
|
59
|
+
"Sans"
|
60
|
+
when :roman, :serif
|
61
|
+
"Serif"
|
62
|
+
when :typewriter
|
63
|
+
"Typewriter"
|
64
|
+
else
|
65
|
+
raise "unknown style found: #{@fc.style}"
|
66
|
+
end
|
67
|
+
tmp = ""
|
68
|
+
fontname=@fc.name
|
69
|
+
tmp << "\\starttypescript[#{@fc.style}][#{fontname}][name]\n"
|
70
|
+
find_fonts.sort{ |a,b| a[0] <=> b[0]}.each { |style,font|
|
71
|
+
tmp << "\\definefontsynonym [#{contextstyle}"
|
72
|
+
p style
|
73
|
+
tmp << "#{style}" if style.length > 0
|
74
|
+
tmp << "] [#{fontname}"
|
75
|
+
tmp << "-#{style}" if style.length > 0
|
76
|
+
tmp << "]\n"
|
77
|
+
}
|
78
|
+
tmp << STOPTYPESCRIPT
|
79
|
+
|
80
|
+
tmp << "\\starttypescript[#{@fc.style}][#{fontname}][#{contextenc}]\n"
|
81
|
+
find_fonts.sort{ |a,b| a[0] <=> b[0]}.each { |style,font|
|
82
|
+
tmp << "\\definefontsynonym [#{fontname}"
|
83
|
+
tmp << "-#{style}" if style.length > 0
|
84
|
+
tmp << "][#{font.tex_fontname(e)}]\n"
|
85
|
+
}
|
86
|
+
tmp << STOPTYPESCRIPT
|
87
|
+
|
88
|
+
return tmp
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Plugin for RFIL to create a fontdefinition file (<tt>.fd</tt>) for LaTeX
|
3
|
+
=end
|
4
|
+
|
5
|
+
# :enddoc:
|
6
|
+
|
7
|
+
class FDWriterLaTeX < RFIL::RFI::Plugin
|
8
|
+
|
9
|
+
def initialize(fontcollection)
|
10
|
+
@fc=fontcollection
|
11
|
+
super(:latex,:sty)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_plugin
|
15
|
+
ret=[]
|
16
|
+
@fc.texenc.each { |e|
|
17
|
+
h={}
|
18
|
+
h[:type]=:fd
|
19
|
+
h[:filename],h[:contents]=latex_fd(e)
|
20
|
+
ret.push(h)
|
21
|
+
}
|
22
|
+
ret
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# example, should be an extra plugin
|
27
|
+
def latex_fd(e)
|
28
|
+
latexenc=case e.encname
|
29
|
+
when "ECEncoding","T1Encoding"
|
30
|
+
"T1"
|
31
|
+
when "TeXBase1Encoding"
|
32
|
+
"8r"
|
33
|
+
when "TS1Encoding"
|
34
|
+
"TS1"
|
35
|
+
when "OT2AdobeEncoding"
|
36
|
+
"OT2"
|
37
|
+
else
|
38
|
+
raise "unknown latex encoding: #{e.encname}"
|
39
|
+
end
|
40
|
+
filename="#{latexenc.downcase}#{@fc.name}.fd"
|
41
|
+
|
42
|
+
fd="\\ProvidesFile{#{filename}}
|
43
|
+
\\DeclareFontFamily{#{latexenc}}{#{@fc.name}}{}
|
44
|
+
"
|
45
|
+
weight=[:m,:b,:bx]
|
46
|
+
variant=[:n,:sc,:sl,:it]
|
47
|
+
for i in 0..11
|
48
|
+
w=weight[i/4]
|
49
|
+
v=variant[i % 4]
|
50
|
+
f=find_font(w,v)
|
51
|
+
if f
|
52
|
+
name = f.tex_fontname(e)
|
53
|
+
else
|
54
|
+
if i < 4
|
55
|
+
name = "ssub * #{@fc.name}/m/n"
|
56
|
+
else
|
57
|
+
name = "ssub * #{@fc.name}/#{weight[i/4 - 1]}/#{v}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# [[:m,:n],[:m,:sc],[:m,:sl],[:m,:it],
|
62
|
+
# [:b,:n],[:b,:sc],[:b,:sl],[:b,:it],
|
63
|
+
# [:bx,:n],[:bx,:sc],[:bx,:sl],[:bx,:it]].each{ |w,v|
|
64
|
+
# f=find_font(w,v)
|
65
|
+
|
66
|
+
# name = f ? f.tex_fontname(e) : "<->ssub * #{@fc.name}/m/n"
|
67
|
+
fd << "\\DeclareFontShape{#{latexenc}}{#{@fc.name}}{#{w}}{#{v}}{
|
68
|
+
<-> #{name}
|
69
|
+
}{}
|
70
|
+
"
|
71
|
+
end
|
72
|
+
return [filename,fd]
|
73
|
+
end
|
74
|
+
def find_font(w,v)
|
75
|
+
weight={}
|
76
|
+
variant={}
|
77
|
+
weight[:m]=:regular
|
78
|
+
weight[:b]=:bold
|
79
|
+
variant[:n]=:regular
|
80
|
+
variant[:it]=:italic
|
81
|
+
variant[:sl]=:slanted
|
82
|
+
variant[:sc]=:smallcaps
|
83
|
+
|
84
|
+
# w is one of :m, :b, :bx
|
85
|
+
# v is one of :n, :sc, :sl, :it
|
86
|
+
@fc.fonts.each { |font|
|
87
|
+
#p b[:weight]==weight[w]
|
88
|
+
if font.variant ==variant[v] and font.weight==weight[w]
|
89
|
+
return font
|
90
|
+
end
|
91
|
+
}
|
92
|
+
return nil
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
data/lib/rfil/version.rb
ADDED
data/lib/tex/enc.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
#--
|
2
|
+
# enc.rb - read and parse TeX's encoding files
|
3
|
+
# Last Change: Tue May 16 17:24:31 2006
|
4
|
+
#++
|
5
|
+
# See the class ENC for the api description.
|
6
|
+
|
7
|
+
require 'strscan'
|
8
|
+
require 'set'
|
9
|
+
require 'forwardable'
|
10
|
+
|
11
|
+
module TeX
|
12
|
+
|
13
|
+
# = ENC -- Access encoding files
|
14
|
+
#
|
15
|
+
# == General information
|
16
|
+
#
|
17
|
+
# Read a TeX encoding vector (<tt>.enc</tt>-file) and associated
|
18
|
+
# ligkern instructions. The encoding slot are accessible via <em>[]</em>
|
19
|
+
# and <em>[]=</em>, just like an Array.
|
20
|
+
#
|
21
|
+
# == Example usage
|
22
|
+
#
|
23
|
+
# === Read an encoding file
|
24
|
+
# filename = "/opt/tetex/3.0/texmf/fonts/enc/dvips/base/EC.enc"
|
25
|
+
# File.open(filename) { |encfile|
|
26
|
+
# enc=ENC.new(encfile)
|
27
|
+
# enc.encname # => "ECEncoding"
|
28
|
+
# enc # => ['grave','acute',...]
|
29
|
+
# enc.filename # => "EC.enc"
|
30
|
+
# enc.ligkern_instructions # => ["space l =: lslash","space L =: Lslash",... ]
|
31
|
+
# }
|
32
|
+
# === Create an encoding
|
33
|
+
# enc=ENC.new
|
34
|
+
# enc.encname="Exampleenc"
|
35
|
+
# enc[0]="grave"
|
36
|
+
# # all undefined slots are ".notdef"
|
37
|
+
# ....
|
38
|
+
#
|
39
|
+
# # write encoding to <tt>new.enc</tt>
|
40
|
+
# File.open("new.enc") do |f|
|
41
|
+
# f << enc.to_s
|
42
|
+
# end
|
43
|
+
# ---
|
44
|
+
# Remark: This interface is pretty much fixed.
|
45
|
+
#--
|
46
|
+
# dont't subclass Array directly, it might be a bad idea. See for
|
47
|
+
# example [ruby-talk:147327]
|
48
|
+
#++
|
49
|
+
|
50
|
+
class ENC # < DelegateClass(Array)
|
51
|
+
def self.documented_as_accessor(*args) # :nodoc:
|
52
|
+
end
|
53
|
+
|
54
|
+
extend Forwardable
|
55
|
+
def_delegators(:@encvector, :size, :[],:each, :each_with_index)
|
56
|
+
|
57
|
+
# _encname_ is the PostScript name of the encoding vector.
|
58
|
+
attr_accessor :encname
|
59
|
+
|
60
|
+
# ligkern_instructions is an array of strings (instructions) as
|
61
|
+
# found in the encoding file, such as:
|
62
|
+
# "quoteright quoteright =: quotedblright"
|
63
|
+
# "* {} space"
|
64
|
+
attr_accessor :ligkern_instructions
|
65
|
+
|
66
|
+
# Hash: key is glyph name, value is a Set of indexes.
|
67
|
+
# Example: glyph_index['hyphen']=#<Set: {45, 127}> in
|
68
|
+
# <tt>ec.enc</tt>. Automatically updated when changing the encoding
|
69
|
+
# vector via <em>[]=</em>.
|
70
|
+
attr_reader :glyph_index
|
71
|
+
|
72
|
+
# Filename of the encoding vector. Used for creating mapfile
|
73
|
+
# entries. Always ends with ".enc" if read (unless it is unset).
|
74
|
+
documented_as_accessor :filename
|
75
|
+
|
76
|
+
# Optional enc is either a File object or a string with the contents
|
77
|
+
# of a file. If set, the object is initialized with the given
|
78
|
+
# encoding vector.
|
79
|
+
def initialize (enc=nil)
|
80
|
+
@glyph_index={}
|
81
|
+
@ligkern_instructions=[]
|
82
|
+
# File, Tempfile, IO respond to read
|
83
|
+
if enc
|
84
|
+
@encvector=[]
|
85
|
+
string = enc.respond_to?(:read) ? enc.read : enc
|
86
|
+
if enc.respond_to?(:path)
|
87
|
+
self.filename= enc.path
|
88
|
+
end
|
89
|
+
parse(string)
|
90
|
+
else
|
91
|
+
@encvector=Array.new(256,".notdef")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def filename # :nodoc:
|
96
|
+
@filename
|
97
|
+
end
|
98
|
+
|
99
|
+
def filename=(fn) # :nodoc:
|
100
|
+
@filename=File.basename(fn.chomp(".enc")+".enc")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return true if the encoding name and the encoding Array are the
|
104
|
+
# same. If _obj_ is an Array, only compare the Array elements.
|
105
|
+
def ==(obj)
|
106
|
+
return false if obj==nil
|
107
|
+
if obj.instance_of?(ENC)
|
108
|
+
return false unless @encname==obj.encname
|
109
|
+
end
|
110
|
+
|
111
|
+
return false unless obj.respond_to?(:[])
|
112
|
+
0.upto(255) { |i|
|
113
|
+
return false if @encvector[i]!=obj[i]
|
114
|
+
}
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
118
|
+
# todo: document and test
|
119
|
+
def -(obj)
|
120
|
+
tmp=[]
|
121
|
+
for i in 0..255
|
122
|
+
tmp[i]=obj[i]
|
123
|
+
end
|
124
|
+
@encvector - tmp
|
125
|
+
end
|
126
|
+
|
127
|
+
# also updates the glyph_index
|
128
|
+
def []=(i,obj) # :nodoc:
|
129
|
+
if obj==nil and @encvector[i] != nil
|
130
|
+
@glyph_index.delete(@encvector[i])
|
131
|
+
return obj
|
132
|
+
end
|
133
|
+
|
134
|
+
@encvector[i]=obj
|
135
|
+
addtoindex(obj,i)
|
136
|
+
return obj
|
137
|
+
end
|
138
|
+
|
139
|
+
# Return a string representation of the encoding that is compatible
|
140
|
+
# with dvips and alike.
|
141
|
+
def to_s
|
142
|
+
str = ""
|
143
|
+
@ligkern_instructions.each { |instr|
|
144
|
+
str << "% LIGKERN #{instr} ;\n"
|
145
|
+
}
|
146
|
+
str << "%\n"
|
147
|
+
str << "/#@encname [\n"
|
148
|
+
@encvector.each_with_index { |glyphname,i|
|
149
|
+
str << "% #{i}\n" if (i % 16 == 0)
|
150
|
+
str << " " unless (i % 8 == 0)
|
151
|
+
str << "/#{glyphname}"
|
152
|
+
str << "\n" if (i % 8 == 7)
|
153
|
+
}
|
154
|
+
str << "] def\n"
|
155
|
+
str
|
156
|
+
end
|
157
|
+
|
158
|
+
#######
|
159
|
+
private
|
160
|
+
#######
|
161
|
+
|
162
|
+
# creates the glyph_index from the encvector. Use this method after
|
163
|
+
# you made changes to the encvector.
|
164
|
+
def update_glyph_index
|
165
|
+
@encvector.each_with_index { |name,i|
|
166
|
+
next if name==".notdef"
|
167
|
+
addtoindex(name,i)
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
# Adds position i to glyph_index for glyph _glyph_.
|
172
|
+
def addtoindex(glyph,i)
|
173
|
+
return if glyph==".notdef"
|
174
|
+
if @glyph_index[glyph]
|
175
|
+
@glyph_index[glyph].add i
|
176
|
+
else
|
177
|
+
@glyph_index[glyph]=Set.new().add(i)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# return the next postscript element (e.g. /name or [ )
|
182
|
+
def tok(s)
|
183
|
+
unless s.peek(1) == "/"
|
184
|
+
s.skip_until(/[^\/\[\]]+/) # not '/' '[' or ']'
|
185
|
+
end
|
186
|
+
s.scan(/(?:\/\.?\w+|\[|\])/)
|
187
|
+
end
|
188
|
+
|
189
|
+
# fill Array with contents of string.
|
190
|
+
def parse(str)
|
191
|
+
count=0
|
192
|
+
s=StringScanner.new(str)
|
193
|
+
ligkern=""
|
194
|
+
while s.skip_until(/^%\s+LIGKERN\s+/)
|
195
|
+
ligkern << s.scan_until(/$/)
|
196
|
+
end
|
197
|
+
ligkern.split(';').each { |instruction|
|
198
|
+
@ligkern_instructions.push instruction.strip
|
199
|
+
}
|
200
|
+
s.string=(str.gsub(/%.*/,''))
|
201
|
+
t=tok(s)
|
202
|
+
@encname=t[1,t.length-1]
|
203
|
+
loop do
|
204
|
+
t = tok(s)
|
205
|
+
case t
|
206
|
+
when "["
|
207
|
+
# ignore
|
208
|
+
when "]"
|
209
|
+
unless @encvector.size == 256
|
210
|
+
raise "Unexpected size of encoding. It should contain 256 entries, but has #{@encvector.size} entries."
|
211
|
+
end
|
212
|
+
update_glyph_index
|
213
|
+
return
|
214
|
+
else
|
215
|
+
name = t[1,t.length-1]
|
216
|
+
@encvector.push(name)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
# never reached
|
220
|
+
raise "Internal ENC error"
|
221
|
+
end
|
222
|
+
end #class Enc
|
223
|
+
end
|
data/lib/tex/kpathsea.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#--
|
2
|
+
# kpathsea.rb - libkpathsea access for ruby
|
3
|
+
# Last Change: Tue May 16 17:23:14 2006
|
4
|
+
#++
|
5
|
+
|
6
|
+
|
7
|
+
module TeX
|
8
|
+
|
9
|
+
# Find TeX related files with help of the 'kpsewhich' program.
|
10
|
+
class Kpathsea
|
11
|
+
# _progname_ defaults to the name of the main Ruby script.
|
12
|
+
# _progname_ is used to find program specific files as in
|
13
|
+
# <tt>TEXINPUT.progname</tt> in the <tt>texmf.cnf</tt>.
|
14
|
+
def initialize (progname=File.basename($0))
|
15
|
+
raise ArgumentError if progname.match(/('|")/)
|
16
|
+
@progname=progname
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset_program_name(suffix)
|
20
|
+
@progname=suffix
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the complete path of the file _name_. _name_ must not
|
24
|
+
# contain single or double quotes.
|
25
|
+
def find_file(name,fmt="tex",mustexist=false)
|
26
|
+
raise ArgumentError if name.match(/('|")/)
|
27
|
+
raise ArgumentError if fmt.match(/('|")/)
|
28
|
+
runkpsewhich(name,fmt,mustexist)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return a File object. Raise Errno::ENOENT if file is not found. If
|
32
|
+
# block is given, a File object is passed into the block and the
|
33
|
+
# file gets closed when leaving the block. It behaves exactly as
|
34
|
+
# the File.open method.
|
35
|
+
def open_file(name,fmt="tex")
|
36
|
+
loc=self.find_file(name,fmt)
|
37
|
+
raise Errno::ENOENT, "#{name}" unless loc
|
38
|
+
if block_given?
|
39
|
+
File.open(loc) { |file|
|
40
|
+
yield file
|
41
|
+
}
|
42
|
+
else
|
43
|
+
File.open(loc)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def runkpsewhich(name,fmt,mustexist)
|
50
|
+
fmt.untaint
|
51
|
+
name.untaint
|
52
|
+
@progname.untaint
|
53
|
+
# path or amok XXX
|
54
|
+
cmdline= "kpsewhich -progname=\"#{@progname}\" -format=\"#{fmt}\" #{name}"
|
55
|
+
# puts cmdline
|
56
|
+
lines = ""
|
57
|
+
IO.popen(cmdline) do |io|
|
58
|
+
lines = io.readlines
|
59
|
+
end
|
60
|
+
return $? == 0 ? lines.to_s.chomp.untaint : nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|