diff --git a/lib/ttfunk.rb b/lib/ttfunk.rb index 27177179..1a4f5e48 100644 --- a/lib/ttfunk.rb +++ b/lib/ttfunk.rb @@ -106,7 +106,11 @@ def glyph_locations def glyph_outlines @glyph_outlines ||= TTFunk::Table::Glyf.new(self) end - end + + def sbix + @sbix ||= TTFunk::Table::Sbix.new(self) + end + end end require_relative "ttfunk/table/cmap" @@ -120,3 +124,4 @@ def glyph_outlines require_relative "ttfunk/table/name" require_relative "ttfunk/table/os2" require_relative "ttfunk/table/post" +require_relative "ttfunk/table/sbix" diff --git a/lib/ttfunk/table/sbix.rb b/lib/ttfunk/table/sbix.rb new file mode 100644 index 00000000..c5f16ff3 --- /dev/null +++ b/lib/ttfunk/table/sbix.rb @@ -0,0 +1,54 @@ +require_relative '../table' + +module TTFunk + class Table + class Sbix < Table + attr_reader :version + attr_reader :flags + attr_reader :num_strikes + attr_reader :strikes + + BitmapData = Struct.new(:x, :y, :type, :data, :ppem, :resolution) + + def bitmap_data_for(glyph_id, strike_index) + strike = strikes[strike_index] + return if strike.nil? + + glyph_offset = strike[:glyph_data_offset][glyph_id] + next_glyph_offset = strike[:glyph_data_offset][glyph_id + 1] + + if glyph_offset && next_glyph_offset + bytes = next_glyph_offset - glyph_offset + if bytes > 0 + parse_from(offset + strike[:offset] + glyph_offset) do + x, y, type = read(8, "s2A4") + data = StringIO.new(io.read(bytes - 8)) + BitmapData.new(x, y, type, data, strike[:ppem], strike[:resolution]) + end + end + end + end + + def all_bitmap_data_for(glyph_id) + strikes.each_index.map do |strike_index| + bitmap_data_for(glyph_id, strike_index) + end.compact + end + + private + + def parse! + @version, @flags, @num_strikes = read(8, "n2N") + strike_offsets = num_strikes.times.map { read(4, "N").first } + + @strikes = strike_offsets.map do |strike_offset| + parse_from(offset + strike_offset) do + ppem, resolution = read(4, "n2") + data_offsets = (file.maximum_profile.num_glyphs + 1).times.map { read(4, "N").first } + { ppem: ppem, resolution: resolution, offset: strike_offset, glyph_data_offset: data_offsets } + end + end + end + end + end +end diff --git a/spec/fonts/ColorTestSbix.ttf b/spec/fonts/ColorTestSbix.ttf new file mode 100755 index 00000000..75f9cba2 Binary files /dev/null and b/spec/fonts/ColorTestSbix.ttf differ diff --git a/spec/integration/file_spec.rb b/spec/integration/file_spec.rb index 1a2bded7..cf850ebe 100644 --- a/spec/integration/file_spec.rb +++ b/spec/integration/file_spec.rb @@ -115,3 +115,33 @@ it "should extract the correct value" end end + +describe TTFunk::File, "#sbix" do + + context "with ColorTestSbix" do + # Thank you http://typophile.com/node/103268 for ColorTestSbix.ttf + let!(:file) { TTFunk::File.open(test_font("ColorTestSbix"))} + + it "should should extract headers" do + expect(file.sbix.version).to eq(1) + expect(file.sbix.flags).to eq(1) + expect(file.sbix.num_strikes).to eq(1) + end + + it "should extract bitmap data given a glyph id and strike index" do + bitmap = file.sbix.bitmap_data_for(4, 0) + expect(bitmap.x).to eq(0) + expect(bitmap.y).to eq(0) + expect(bitmap.type).to eq("png") + expect(bitmap.data.read).to match(/IHDR.*ImageReady.*IEND/m) + expect(bitmap.ppem).to eq(150) + expect(bitmap.resolution).to eq(72) + end + + it "should extract an array of all bitmap data given a glyph id" do + all_bitmaps = file.sbix.all_bitmap_data_for(4) + expect(all_bitmaps.size).to eq(1) + expect(all_bitmaps[0].ppem).to eq(150) + end + end +end