summaryrefslogtreecommitdiff
path: root/support/rfil/lib/rfil/tex/enc.rb
diff options
context:
space:
mode:
Diffstat (limited to 'support/rfil/lib/rfil/tex/enc.rb')
-rw-r--r--support/rfil/lib/rfil/tex/enc.rb225
1 files changed, 225 insertions, 0 deletions
diff --git a/support/rfil/lib/rfil/tex/enc.rb b/support/rfil/lib/rfil/tex/enc.rb
new file mode 100644
index 0000000000..d2bb21a620
--- /dev/null
+++ b/support/rfil/lib/rfil/tex/enc.rb
@@ -0,0 +1,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