summaryrefslogtreecommitdiff
path: root/support/rfil/lib/rfil/rfi.rb
diff options
context:
space:
mode:
Diffstat (limited to 'support/rfil/lib/rfil/rfi.rb')
-rw-r--r--support/rfil/lib/rfil/rfi.rb472
1 files changed, 472 insertions, 0 deletions
diff --git a/support/rfil/lib/rfil/rfi.rb b/support/rfil/lib/rfil/rfi.rb
new file mode 100644
index 0000000000..2ea1f50bd1
--- /dev/null
+++ b/support/rfil/lib/rfil/rfi.rb
@@ -0,0 +1,472 @@
+# rfi.rb -- general use classes
+#--
+# Last Change: Tue May 16 19:21:51 2006
+#++
+
+require 'rfil/font/glyph'
+
+module RFIL # :nodoc:
+
+ # = RFI
+ # Everything that does not fit somewhere else gets included in the
+ # wrapper class RFI.
+
+ # This class contains methods and other classes that are pretty much
+ # useless of their own or are accessed in different classes.
+
+ class RFI # :nodoc:
+
+ # Super class for plugins. Just subclass this Plugin, set the name
+ # when calling Plugin#new and implement run_plugin.
+ class Plugin
+ # Name of the plugin. A Symbol or a String.
+ attr_reader :name
+
+ attr_reader :filetypes
+
+ # Create a new plugin. _name_ is the name of the plugin (it must
+ # be a Symbol or a String). _filetypes_ is a list of symbols, of
+ # what files the plugin is capable of writing.
+ def initialize(name,*filetypes)
+ @name=name
+ @filetypes=filetypes
+ end
+
+ # Return an Array of files that should be written on the user's
+ # harddrive. The Hash entries are
+ # [<tt>:type</tt>] Type of the file (<tt>:fd</tt>, <tt>:typescript</tt> etc.)
+ # [<tt>:filename</tt>] The filename (without a path) of the file.
+ # [<tt>:contents</tt>] The contents of the file.
+ def run_plugin
+ #dummy
+ end
+ end
+
+ # Some instructions to remove kerning information from digits and
+ # other things. -> sort this out
+ STDLIGKERN = ["space l =: lslash",
+ "space L =: Lslash", "question quoteleft =: questiondown",
+ "exclam quoteleft =: exclamdown", "hyphen hyphen =: endash",
+ "endash hyphen =: emdash", "quoteleft quoteleft =: quotedblleft",
+ "quoteright quoteright =: quotedblright", "space {} *", "* {} space",
+ "zero {} *", "* {} zero",
+ "one {} *", "* {} one",
+ "two {} *", "* {} two",
+ "three {} *","* {} three",
+ "four {} *", "* {} four",
+ "five {} *", "* {} five",
+ "six {} *", "* {} six",
+ "seven {} *", "* {} seven",
+ "eight {} *", "* {} eight",
+ "nine {} *", "* {} nine",
+ "comma comma =: quotedblbase",
+ "less less =: guillemotleft",
+ "greater greater =: guillemotright"]
+
+ # Metric information about a glyph. Does not contain the glyph
+ # (outlines) itself.
+ class Char < Font::Glyph
+
+ # fontnumber is used in Font class
+ attr_accessor :fontnumber
+
+ # If not nil, _mapto_ is the glyphname that should be used instead
+ # of the current one.
+ attr_accessor :mapto
+
+ # Sets the extension factor. This is used by calculations of _wx_,
+ # _llx_ and _urx_.
+ attr_accessor :efactor
+
+ # Sets the slant factor. This is used by calculations of _wx_,
+ # _llx_ and _urx_.
+ attr_accessor :slant
+
+ def wx # :nodoc:
+ transform(@wx,0)
+ end
+ def wx=(obj) # :nodoc:
+ @wx=obj
+ end
+ # Lower left x position of glyph.
+ def llx # :nodoc:
+ transform(@b[0],b[1])
+ end
+
+ # Upper right x position of glyph.
+ def urx # :nodoc:
+ transform(@b[2],ury)
+ end
+
+ private
+
+ def transform (x,y)
+ (@efactor * x + @slant * y)
+ end
+
+ end # class Char
+
+
+ # Represent the different ligatures possible in tfm.
+ class LIG
+
+ @@encligops = ["=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>"]
+ @@vpligops = ["LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/",
+ "/LIG/>", "/LIG/>>"]
+ @@symligops = [:lig, :"lig/", :"/lig", :"/lig/", :"lig/>", :"/lig>", :"/lig/>", :"/lig/>>"]
+
+ # First glyph of a two glyph sequence before it is turned into a
+ # ligature.
+ attr_accessor :left
+
+ # Second glyph of a two glyph sequence before it is turned into a
+ # ligature.
+ attr_accessor :right
+
+ # The ligature that gets inserterd instead of the left and right glyph.
+ attr_accessor :result
+
+ # <tt>[0, 1, 2, 3, 4, 5, 6, 7 ]</tt>
+ #
+ # <tt>[=: , |=: , |=:> , =:| , =:|>, |=:|, |=:|>, |=:|>> ]</tt>
+ #
+ # <tt>[LIG, /LIG, /LIG>, LIG/, LIG/>, /LIG/, /LIG/>, /LIG/>>]</tt>
+ attr_accessor :type
+
+
+
+ # call-seq:
+ # new
+ # new(left,[right,[result,[type]]])
+ # new(hash)
+ # new(otherlig)
+ #
+ # When called with left, right, result or type parameters, take
+ # these settings for the LIG object. When called with a hash as an
+ # argument, the keys should look like: :left,:right,:result,:type.
+ # When called with an existing LIG object, the values are taken
+ # from the old object.
+ def initialize(left=nil,right=nil,result=nil,type=nil)
+ case left
+ when Hash
+ [:left,:right,:result,:type].each { |sym|
+ if left.has_key?(sym)
+ self.send((sym.to_s+"=").to_sym,left[sym])
+ end
+ }
+ when LIG
+ [:left,:right,:result,:type].each { |sym|
+ self.send((sym.to_s+"=").to_sym,left.send(sym))
+ }
+ # warning!!!!! LIG accepts a String as well as Fixnum as
+ # parameters, this might have side effects!?
+ when Fixnum,nil,String
+ @left=left
+ @right=right
+ @result=result
+ @type=type
+ else
+ raise "unknown argument for new() in LIG: #{left}"
+ end
+ # test!
+ #unless @type.instance_of?(Fixnum)
+ # raise "type must be a fixnum"
+ #end
+ end
+ def ==(lig)
+ @left=lig.left and
+ @right=lig.right and
+ @result=lig.result and
+ @type=lig.type
+ end
+
+ def to_pl(encoding)
+ encoding.glyph_index[@right].sort.collect { |rightslot|
+ left=encoding.glyph_index[@left].min
+ # right=encoding.glyph_index[@right].min
+ result=encoding.glyph_index[@result].min
+ type=@@vpligops[@type]
+ LIG.new(:left=>left, :right=>rightslot, :result=>result, :type=>type)
+ }
+ end
+ # Return an array that is suitable for tfm
+ def to_tfminstr(encoding)
+ encoding.glyph_index[@right].sort.collect { |rightslot|
+ left=encoding.glyph_index[@left].min
+ # right=encoding.glyph_index[@right].min
+ result=encoding.glyph_index[@result].min
+ type=@@symligops[@type]
+ [type,rightslot,result]
+ }
+ end
+ def inspect
+ "[#{@type.to_s.upcase} #@left + #@right => #@result]"
+ end
+ end
+
+ require 'forwardable'
+
+ # Stores information about kerning and ligature information. Allows
+ # deep copy of ligature and kerning information. Obsolete. Don't use.
+ class LigKern
+ extend Forwardable
+ # Optional parameter initializes the new LigKern object.
+ def initialize(h={})
+ @h=h
+ end
+
+ def_delegators(:@h, :each, :[], :[]=,:each_key,:has_key?)
+
+ def initialize_copy(obj) # :nodoc:
+ tmp={}
+ if obj[:lig]
+ tmp[:lig]=Array.new
+ obj[:lig].each { |elt|
+ tmp[:lig].push(elt.dup)
+ }
+ end
+ if obj[:krn]
+ tmp[:krn]=Array.new
+ obj[:krn].each { |elt|
+ tmp[:krn].push(elt.dup)
+ }
+ end
+ if obj[:alias]
+ tmp[:alias]=obj[:alias].dup
+ end
+ @h=tmp
+ end
+ # Compare this object to another object of the same class.
+ def ==(obj)
+ return false unless obj.respond_to?(:each)
+ # the krn needs to be compared one by one, because they are floats
+ if obj.has_key?(:krn)
+ obj[:krn].each { |destchar,value|
+ return false unless @h[:krn].assoc(destchar)
+ return false if (value - @h[:krn].assoc(destchar)[1]).abs > 0.01
+ }
+ end
+ obj.each { |key,value|
+ next if key==:krn
+ return false unless @h[key]==value
+ }
+ true
+ end
+ end
+
+
+ # The Glyphlist is a actually a Hash with some special methods
+ # attached.
+ class Glyphlist < Hash
+ @@encligops = ["=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>"]
+ @@vpligops = ["LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/",
+ "/LIG/>", "/LIG/>>"]
+
+ # Return an array with name of glyphs that are represented by the
+ # symbol _glyphlist_.
+ # These symbols are defined: :lowercase, :uppercase, :digits
+ def get_glyphlist(glyphlist)
+ ret=[]
+ unless glyphlist.instance_of? Symbol
+ raise ArgumentError, "glyphlist must be a symbol"
+ end
+ case glyphlist
+ when :lowercase
+ update_uc_lc_list
+
+ self.each { |glyphname,char|
+ if char.uc != nil
+ ret.push glyphname
+ end
+ }
+ when :uppercase
+ update_uc_lc_list
+
+ self.each { |glyphname,char|
+ if char.lc != nil
+ ret.push glyphname
+ end
+ }
+ when :digits
+ ret=%w(one two three four five six seven eight nine zero)
+ end
+ ret
+ end
+
+ # instructions.each must yield string objects (i.e. an array of
+ # strings, an IO object, a single string, ...). Instruction is like:
+ # "space l =: lslash" or "two {} *"
+ def apply_ligkern_instructions (instructions)
+ instructions.each { |instr|
+ s = instr.split(' ')
+
+ if @@encligops.member?(s[2]) # one of =:, |=: |=:> ...
+ if self[s[0]]
+ self[s[0]].lig_data[s[1]]=LIG.new(s[0],s[1],s[3],@@encligops.index(s[2]))
+ else
+ # puts "glyphlist#apply_ligkern_instructions: char not found: #{s[0]}"
+ end
+ elsif s[1] == "{}"
+ remove_kern(s[0],s[2])
+ end
+ }
+ end
+ # _left_ and _right_ must be either a glyphname or a '*'
+ # (asterisk) which acts like a wildcard. So ('one','*') would
+ # remove all kerns of glyph 'one' where 'one' is the left glyph in
+ # a kerning pair.
+ def remove_kern(left,right)
+ raise ArgumentError, "Only one operand may be '*'" if left=='*' and right=='*'
+ if right == "*"
+ self[left].kern_data={} if self[left]
+ elsif left == "*"
+ if self[right]
+ self.each { |name,chardata|
+ chardata.kern_data.delete(right)
+ }
+ end
+ else
+ if self[right] and self[left]
+ self[left].kern_data.delete(right)
+ end
+ end
+ end
+
+ # Update all glyph entries to see what the uppercase or the
+ # lowercase variants are. Warning!! Tcaron <-> tquoteright in
+ # non-unicode fonts.
+ def update_uc_lc_list
+ # we need this list only when faking small caps (which will, of
+ # course, never happen!)
+
+ # make a list of all uppercase and lowercase glyphs. Be aware of
+ # ae<->AE, oe<->OE, germandbls<-->SS, dotlessi->I, dotlessj->J
+ # do the
+ # @upper_lower={}
+ # @lower_upper={}
+ self.each_key {|glyphname|
+ thischar=self[glyphname]
+ if glyphname =~ /^[a-z]/
+ if glyphname =~ /^(a|o)e$/ and self[glyphname.upcase]
+ thischar.uc = glyphname.upcase
+ elsif glyphname =~ /^dotless(i|j)$/
+ thischar.uc = glyphname[-1].chr.upcase
+ elsif self[glyphname.capitalize]
+ thischar.uc = glyphname.capitalize
+ end
+ else
+ if glyphname =~ /^(A|O)e$/ and self[glyphname.dowcase]
+ thischar.lc = glyphname.downcase
+ elsif self[glyphname.downcase]
+ thischar.lc = glyphname.downcase
+ end
+ end
+ }
+ if self['germandbls']
+ self['germandbls'].uc='S'
+ end
+ end
+
+ # Modify the charmetrics and the kerning/ligatures so that the
+ # lowercase chars are made from scaling uppercase chars.
+ def fake_caps (factor)
+ update_uc_lc_list
+ # we need to do the following
+ # 1. adapt kerning pairs
+ # 2. change font metrics (wd)
+ # 3. remove ligatures from sc
+ @fake_caps=true
+ @capheight=factor
+ self.each { |glyphname,char|
+ if char.is_lowercase?
+ # remove ligatures from sc
+ char.lig_data={}
+ char.kern_data={}
+ char.mapto=char.capitalize
+ self[char.uc].kern_data.each { |destglyph,kerndata|
+ unless self[destglyph].is_lowercase?
+ char.kern_data[destglyph.downcase]=[kerndata[0] * factor,0]
+ end
+ }
+ char.b = self[char.capitalize].b.clone
+ char.wx = self[char.capitalize].wx * @capheight
+ char.lly *= @capheight
+ char.urx *= @capheight
+ char.ury *= @capheight
+ else # char is something like Aring, semicolon, ...
+ # if destchar is uppercase letter (A, Aring, ...)
+ # 1. delete all kerns to lowercase letters (not e.g. semicolon)
+ # 2. duplicate all uc kerns, multiply by factor and insert this
+ # as lc kern
+ char.kern_data.delete_if { |destglyph,kerndata|
+ self[destglyph].is_lowercase?
+ }
+
+ new_kern_data={}
+ char.kern_data.each { |destglyph,kerndata|
+ if self[destglyph].is_uppercase?
+ new_kern_data[self[destglyph].downcase]=[kerndata[0]*factor,kerndata[1]]
+ end
+ new_kern_data[destglyph]=kerndata
+ }
+ char.kern_data=new_kern_data
+ end
+ # 2.
+ }
+ if self['germandbls']
+ s=self['S']
+ d=self['germandbls']
+
+ d.b = s.b.dup
+ d.wx = s.wx * 2 * @capheight
+ d.urx += s.wx
+
+
+ d.kern_data={}
+ s.kern_data.each { |destglyph,kerndata|
+ unless self[destglyph].is_lowercase?
+ # we are looking at non-lowercase chars. These might be
+ # ones that are uppercase or are 'something else', e.g.
+ # hyphen...
+ # since we only replace the lc variants, keep the uc and
+ # others intact.
+ if self[destglyph].is_uppercase?
+ d.kern_data[self[destglyph].downcase]=[kerndata[0] * @capheight,0]
+
+ else
+ d.kern_data[destglyph]=[kerndata[0] * @capheight,0]
+
+ end
+ end
+ }
+
+ d.pcc_data=[['S',0,0],['S',s.wx,0]]
+ d.lly *= @capheight
+ d.urx *= @capheight
+
+ end
+ end # fake_caps
+
+ def fix_height(xheight)
+ # this is what afm2tfm does. I am not sure if it is clever.
+ self.each { |name,data|
+
+ # xheight <= 50 -> @chars[char].ury
+ # char.size > 1 -> @chars[char].ury
+ # char+accentname (ntilde, udieresis,...) exists?
+ # then calculate else @chars[char].ury
+ # calculate := ntilde.ury - tilde.ury + xheight
+ # same as texheight in afm2tfm source
+ unless name.size>1 or xheight < 50
+ %w(acute tilde caron dieresis).each {|accent|
+ naccent=name + accent
+ next unless self[naccent]
+ data.ury = self[naccent].ury - self[accent].ury + xheight
+ break
+ }
+ end
+ }
+ end # fix_height
+ end # class Glyphlist
+ end # class RFI
+end