Permalink
Browse files

we really don't care that every glyph is fully decomposed

Just parse enough of each glyph to be able to write it back out
in a font subset
  • Loading branch information...
1 parent 6fe89cb commit e80ad0ebbecb1afccbc0895819e5e4e8452955b6 @jamis jamis committed Dec 26, 2008
Showing with 84 additions and 116 deletions.
  1. +23 −16 example.rb
  2. +1 −1 lib/ttfunk/reader.rb
  3. +16 −7 lib/ttfunk/table/glyf.rb
  4. +31 −33 lib/ttfunk/table/glyf/compound.rb
  5. +13 −59 lib/ttfunk/table/glyf/simple.rb
View
39 example.rb
@@ -1,6 +1,25 @@
$LOAD_PATH << "#{File.dirname(__FILE__)}/lib"
require "ttfunk"
+def character_lookup(file, character)
+ puts "character : #{character}"
+
+ character_code = character.unpack("U*").first
+ puts "character code: #{character_code}"
+
+ glyph_id = file.cmap.unicode.first[character_code]
+ puts "glyph id : #{glyph_id}"
+
+ glyph = file.glyph_outlines.for(glyph_id)
+ puts "glyph type : %s" % glyph.class.name.split(/::/).last.downcase
+ puts "glyph size : %db" % glyph.raw.length
+ puts "glyph bbox : (%d,%d)-(%d,%d)" % [glyph.x_min, glyph.y_min, glyph.x_max, glyph.y_max]
+
+ if glyph.compound?
+ puts "components : %d %s" % [glyph.glyph_ids.length, glyph.glyph_ids.inspect]
+ end
+end
+
file = TTFunk::File.new("data/fonts/DejaVuSans.ttf")
puts "-- FONT ------------------------------------"
@@ -19,20 +38,8 @@
puts "line gap : #{file.line_gap}"
puts "bbox : (%d,%d)-(%d,%d)" % file.bbox
-puts "-- CHARACTER -> GLYPH LOOKUP ---------------"
-
-character = "\xE2\x98\x9C"
-puts "character : #{character}"
-
-character_code = character.unpack("U*").first
-puts "character code: #{character_code}"
-
-glyph_id = file.cmap.unicode.first[character_code]
-puts "glyph id : #{glyph_id}"
-
-glyph_index = file.glyph_locations.index_of(glyph_id)
-glyph_size = file.glyph_locations.size_of(glyph_id)
-puts "glyph index : %d (%db)" % [glyph_index, glyph_size]
+puts "-- SIMPLE CHARACTER -> GLYPH LOOKUP --------"
+character_lookup(file, "\xE2\x98\x9C")
-glyph = file.glyph_outlines.at(glyph_index)
-puts "glyph : (%d,%d)-(%d,%d) (%s)" % [glyph.x_min, glyph.y_min, glyph.x_max, glyph.y_max, glyph.class.name.split(/::/).last.downcase]
+puts "-- COMPOUND CHARACTER -> GLYPH LOOKUP ------"
+character_lookup(file, "ë")
View
2 lib/ttfunk/reader.rb
@@ -20,7 +20,7 @@ def to_signed(n)
def parse_from(position)
saved, io.pos = io.pos, position
- result = yield
+ result = yield position
io.pos = saved
return result
end
View
23 lib/ttfunk/table/glyf.rb
@@ -3,16 +3,25 @@
module TTFunk
class Table
class Glyf < Table
- def at(glyph_offset)
- return @cache[glyph_offset] if @cache.key?(glyph_offset)
+ def for(glyph_id)
+ return @cache[glyph_id] if @cache.key?(glyph_id)
- parse_from(offset + glyph_offset) do
- number_of_contours, x_min, y_min, x_max, y_max = read_signed(5)
+ index = file.glyph_locations.index_of(glyph_id)
+ size = file.glyph_locations.size_of(glyph_id)
- @cache[glyph_offset] = if number_of_contours == -1
- Compound.new(io, x_min, y_min, x_max, y_max)
+ if size.zero? # blank glyph, e.g. space character
+ @cache[glyph_id] = nil
+ return nil
+ end
+
+ parse_from(offset + index) do
+ raw = io.read(size)
+ number_of_contours, x_min, y_min, x_max, y_max = raw.unpack("n5").map { |i| to_signed(i) }
+
+ @cache[glyph_id] = if number_of_contours == -1
+ Compound.new(raw, x_min, y_min, x_max, y_max)
else
- Simple.new(io, number_of_contours, x_min, y_min, x_max, y_max)
+ Simple.new(raw, number_of_contours, x_min, y_min, x_max, y_max)
end
end
end
View
64 lib/ttfunk/table/glyf/compound.rb
@@ -13,58 +13,56 @@ class Compound
WE_HAVE_A_TWO_BY_TWO = 0x0080
WE_HAVE_INSTRUCTIONS = 0x0100
+ attr_reader :raw
attr_reader :x_min, :y_min, :x_max, :y_max
- attr_reader :components
- attr_reader :instructions
+ attr_reader :glyph_ids
Component = Struct.new(:flags, :glyph_index, :arg1, :arg2, :transform)
- def initialize(io, x_min, y_min, x_max, y_max)
- @io = io
+ def initialize(raw, x_min, y_min, x_max, y_max)
+ @raw = raw
@x_min, @y_min, @x_max, @y_max = x_min, y_min, x_max, y_max
- @components = []
- instr_requests = 0
+ # Because TTFunk only cares about glyphs insofar as they (1) provide
+ # a bounding box for each glyph, and (2) can be rewritten into a
+ # font subset, we don't really care about the rest of the glyph data
+ # except as a whole. Thus, we don't actually decompose the glyph
+ # into it's parts--all we really care about are the locations within
+ # the raw string where the component glyph ids are stored, so that
+ # when we rewrite this glyph into a subset we can rewrite the
+ # component glyph-ids so they are correct for the subset.
+
+ @glyph_ids = []
+ @glyph_id_offsets = []
+ offset = 10 # 2 bytes for each of num-contours, min x/y, max x/y
loop do
- flags, glyph_index = read(4, "n*")
+ flags, glyph_id = @raw[offset, 4].unpack("n*")
+ @glyph_ids << glyph_id
+ @glyph_id_offsets << offset + 2
+
+ break unless flags & MORE_COMPONENTS != 0
+ offset += 4
+
if flags & ARG_1_AND_2_ARE_WORDS != 0
- arg1, arg2 = read(4, "n*")
+ offset += 4
else
- arg1, arg2 = read(2, "C*")
+ offset += 2
end
if flags & WE_HAVE_A_TWO_BY_TWO != 0
- transform = read(8, "n*")
+ offset += 8
elsif flags & WE_HAVE_AN_X_AND_Y_SCALE != 0
- transform = read(4, "n*")
+ offset += 4
elsif flags & WE_HAVE_A_SCALE != 0
- transform = read(2, "n")
- else
- transform = []
+ offset += 2
end
-
- instr_requests += 1 if flags & WE_HAVE_INSTRUCTIONS != 0
-
- @components << Component.new(flags, glyph_index, arg1, arg2, transform)
- break unless flags & MORE_COMPONENTS != 0
- end
-
- # The docs are a bit vague on how instructions are to be parsed from
- # a compound glyph. This seems to work for the glyphs I've tried, but...
- @instructions = ""
- while instr_requests > 0
- length = read(2, "n").first
- @instructions << io.read(length)
- instr_requests -= 1
end
end
- private
-
- def io
- @io
- end
+ def compound?
+ true
+ end
end
end
end
View
72 lib/ttfunk/table/glyf/simple.rb
@@ -4,74 +4,28 @@ module TTFunk
class Table
class Glyf
class Simple
- include Reader
-
- ON_CURVE = 0x01
- X_SHORT = 0x02
- Y_SHORT = 0x04
- REPEAT = 0x08
- X_SAME = 0x10
- X_POSITIVE = 0x10
- Y_SAME = 0x20
- Y_POSITIVE = 0x20
-
+ attr_reader :raw
attr_reader :number_of_contours
attr_reader :x_min, :y_min, :x_max, :y_max
- attr_reader :end_points, :instructions, :flags
- attr_reader :xs, :ys
- def initialize(io, number_of_contours, x_min, y_min, x_max, y_max)
- @io = io
+ def initialize(raw, number_of_contours, x_min, y_min, x_max, y_max)
+ @raw = raw
@number_of_contours = number_of_contours
@x_min, @y_min = x_min, y_min
@x_max, @y_max = x_max, y_max
- @end_points = read(number_of_contours * 2, "n*")
- point_count = @end_points.last || 0
-
- instr_len = read(2, "n").first
- @instructions = io.read(instr_len)
-
- @flags = []
- while @flags.length < point_count
- flag = read(1, "C").first
- @flags << flag
-
- if flag & REPEAT != 0
- count = read(1, "C").first
- @flags.concat([flag] * count)
- end
- end
-
- @xs = []
- read_coords(@xs, point_count, X_SHORT, X_POSITIVE, X_SAME)
-
- @ys = []
- read_coords(@ys, point_count, Y_SHORT, Y_POSITIVE, Y_SAME)
+ # Because TTFunk is, at this time, a library for simply pulling
+ # metrics out of font files, or for writing font subsets, we don't
+ # really care what the contours are for simple glyphs. We just
+ # care that we've got an entire glyph's definition. Also, a
+ # bounding box could be nice to know. Since we've got all that
+ # at this point, we don't need to worry about parsing the full
+ # contents of the glyph.
end
- private
-
- def read_coords(array, count, short_flag, positive_flag, same_flag)
- while array.length < count
- flag = @flags[array.length]
-
- if flag & short_flag != 0
- coord = read(1, "C").first
- coord = -coord if flag & positive_flag == 0
- elsif flag & same_flag != 0
- coord = 0
- else
- coord = read_signed(1).first
- end
-
- array << coord
- end
- end
-
- def io
- @io
- end
+ def compound?
+ false
+ end
end
end
end

0 comments on commit e80ad0e

Please sign in to comment.