Permalink
Browse files

separate subset subclasses for supported encodings

  • Loading branch information...
1 parent f695434 commit 6fed25e6347b1fd5ce1bb9d367c9b1e4ad27f19b @jamis jamis committed Dec 31, 2008
View
@@ -1,143 +1,18 @@
-require 'set'
-require 'ttfunk/table/cmap'
-require 'ttfunk/table/glyf'
-require 'ttfunk/table/hmtx'
-require 'ttfunk/table/kern'
-require 'ttfunk/table/loca'
-require 'ttfunk/encoding/mac_roman'
-require 'ttfunk/encoding/windows_1252'
+require 'ttfunk/subset/unicode'
+require 'ttfunk/subset/unicode_8bit'
+require 'ttfunk/subset/mac_roman'
+require 'ttfunk/subset/windows_1252'
module TTFunk
- class Subset
- attr_reader :original
- attr_reader :encoding
-
- def initialize(original, encoding)
- @original = original
- @encoding = encoding
- @subset = Set.new
- end
-
- def use(character)
- @subset << character
- end
-
- def covers?(character)
- case @encoding
- when :unicode then true
- when :mac_roman then Encoding::MacRoman.covers?(character)
- when :windows_1252 then Encoding::Windows1252.covers?(character)
- else false
+ module Subset
+ def self.for(original, encoding)
+ case encoding.to_sym
+ when :unicode then Unicode.new(original)
+ when :unicode_8bit then Unicode8Bit.new(original)
+ when :mac_roman then MacRoman.new(original)
+ when :windows_1252 then Windows1252.new(original)
+ else raise NotImplementedError, "encoding #{encoding} is not supported"
end
end
-
- def from_unicode(character)
- case @encoding
- when :unicode then character
- when :mac_roman then Encoding::MacRoman::FROM_UNICODE[character]
- when :windows_1252 then Encoding::Windows1252::FROM_UNICODE[character]
- else nil
- end
- end
-
- def encode(options={})
- cmap = original.cmap.unicode.first
-
- # map unicode -> corresponding glyph id in original font
- charmap = @subset.inject({}) { |map, code| map[code] = cmap[code]; map }
- cmap_table = TTFunk::Table::Cmap.encode(charmap, @encoding)
-
- glyph_ids = @subset.map { |character| cmap[character] } << 0
- glyphs = collect_glyphs(glyph_ids.uniq)
-
- old2new_glyph = cmap_table[:charmap].inject({}) { |map, (code, ids)| map[ids[:old]] = ids[:new]; map }
- next_glyph_id = cmap_table[:max_glyph_id]
-
- glyphs.keys.each do |old_id|
- unless old2new_glyph.key?(old_id)
- old2new_glyph[old_id] = next_glyph_id
- next_glyph_id += 1
- end
- end
-
- new2old_glyph = old2new_glyph.invert
-
- glyf_table = TTFunk::Table::Glyf.encode(glyphs, new2old_glyph, old2new_glyph)
- loca_table = TTFunk::Table::Loca.encode(glyf_table[:offsets])
- hmtx_table = TTFunk::Table::Hmtx.encode(original.horizontal_metrics, new2old_glyph)
- hhea_table = TTFunk::Table::Hhea.encode(original.horizontal_header, hmtx_table)
- maxp_table = TTFunk::Table::Maxp.encode(original.maximum_profile, old2new_glyph)
- os2_table = original.os2.raw
- post_table = TTFunk::Table::Post.encode(original.postscript, new2old_glyph)
- name_table = TTFunk::Table::Name.encode(original.name)
- head_table = TTFunk::Table::Head.encode(original.header, loca_table)
-
- # for PDF's, the kerning info is all included in the PDF as the text is
- # drawn. Thus, the PDF readers do not actually use the kerning info in
- # embedded fonts. If the library is used for something else, the generated
- # subfont may need a kerning table... in that case, you need to opt into it.
- if options[:kerning]
- kern_table = TTFunk::Table::Kern.encode(original.kerning, old2new_glyph)
- end
-
- tables = { 'cmap' => cmap_table[:table],
- 'glyf' => glyf_table[:table],
- 'loca' => loca_table[:table],
- 'kern' => kern_table,
- 'hmtx' => hmtx_table[:table],
- 'hhea' => hhea_table,
- 'maxp' => maxp_table,
- 'OS/2' => os2_table,
- 'post' => post_table,
- 'name' => name_table,
- 'head' => head_table }
-
- tables.delete_if { |tag, table| table.nil? }
-
- search_range = (Math.log(tables.length) / Math.log(2)).to_i * 16
- entry_selector = (Math.log(search_range) / Math.log(2)).to_i
- range_shift = tables.length * 16 - search_range
-
- newfont = [original.directory.scaler_type, tables.length, search_range, entry_selector, range_shift].pack("Nn*")
-
- directory_size = tables.length * 16
- offset = newfont.length + directory_size
-
- table_data = ""
- head_offset = nil
- tables.each do |tag, data|
- newfont << [tag, checksum(data), offset, data.length].pack("A4N*")
- table_data << data
- head_offset = offset if tag == 'head'
- offset += data.length
- while offset % 4 != 0
- offset += 1
- table_data << "\0"
- end
- end
-
- newfont << table_data
- sum = checksum(newfont)
- adjustment = 0xB1B0AFBA - sum
- newfont[head_offset+8,4] = [adjustment].pack("N")
-
- return newfont
- end
-
- private
-
- def checksum(data)
- data += "\0" * (4 - data.length % 4) unless data.length % 4 == 0
- data.unpack("N*").inject(0) { |sum, dword| sum + dword } & 0xFFFF_FFFF
- end
-
- def collect_glyphs(glyph_ids)
- glyphs = glyph_ids.inject({}) { |h, id| h[id] = original.glyph_outlines.for(id); h }
- additional_ids = glyphs.values.select { |g| g && g.compound? }.map { |g| g.glyph_ids }.flatten
-
- glyphs.update(collect_glyphs(additional_ids)) if additional_ids.any?
-
- return glyphs
- end
end
end
View
@@ -0,0 +1,117 @@
+require 'ttfunk/table/cmap'
+require 'ttfunk/table/glyf'
+require 'ttfunk/table/hmtx'
+require 'ttfunk/table/kern'
+require 'ttfunk/table/loca'
+require 'ttfunk/encoding/mac_roman'
+require 'ttfunk/encoding/windows_1252'
+
+module TTFunk
+ module Subset
+ class Base
+ attr_reader :original
+
+ def initialize(original)
+ @original = original
+ end
+
+ def encode(options={})
+ cmap_table = new_cmap_table(options)
+ glyphs = collect_glyphs(original_glyph_ids)
+
+ old2new_glyph = cmap_table[:charmap].inject({}) { |map, (code, ids)| map[ids[:old]] = ids[:new]; map }
+ next_glyph_id = cmap_table[:max_glyph_id]
+
+ glyphs.keys.each do |old_id|
+ unless old2new_glyph.key?(old_id)
+ old2new_glyph[old_id] = next_glyph_id
+ next_glyph_id += 1
+ end
+ end
+
+ new2old_glyph = old2new_glyph.invert
+
+ glyf_table = TTFunk::Table::Glyf.encode(glyphs, new2old_glyph, old2new_glyph)
+ loca_table = TTFunk::Table::Loca.encode(glyf_table[:offsets])
+ hmtx_table = TTFunk::Table::Hmtx.encode(original.horizontal_metrics, new2old_glyph)
+ hhea_table = TTFunk::Table::Hhea.encode(original.horizontal_header, hmtx_table)
+ maxp_table = TTFunk::Table::Maxp.encode(original.maximum_profile, old2new_glyph)
+ os2_table = original.os2.raw
+ post_table = TTFunk::Table::Post.encode(original.postscript, new2old_glyph)
+ name_table = TTFunk::Table::Name.encode(original.name)
+ head_table = TTFunk::Table::Head.encode(original.header, loca_table)
+
+ # for PDF's, the kerning info is all included in the PDF as the text is
+ # drawn. Thus, the PDF readers do not actually use the kerning info in
+ # embedded fonts. If the library is used for something else, the generated
+ # subfont may need a kerning table... in that case, you need to opt into it.
+ if options[:kerning]
+ kern_table = TTFunk::Table::Kern.encode(original.kerning, old2new_glyph)
+ end
+
+ tables = { 'cmap' => cmap_table[:table],
+ 'glyf' => glyf_table[:table],
+ 'loca' => loca_table[:table],
+ 'kern' => kern_table,
+ 'hmtx' => hmtx_table[:table],
+ 'hhea' => hhea_table,
+ 'maxp' => maxp_table,
+ 'OS/2' => os2_table,
+ 'post' => post_table,
+ 'name' => name_table,
+ 'head' => head_table }
+
+ tables.delete_if { |tag, table| table.nil? }
+
+ search_range = (Math.log(tables.length) / Math.log(2)).to_i * 16
+ entry_selector = (Math.log(search_range) / Math.log(2)).to_i
+ range_shift = tables.length * 16 - search_range
+
+ newfont = [original.directory.scaler_type, tables.length, search_range, entry_selector, range_shift].pack("Nn*")
+
+ directory_size = tables.length * 16
+ offset = newfont.length + directory_size
+
+ table_data = ""
+ head_offset = nil
+ tables.each do |tag, data|
+ newfont << [tag, checksum(data), offset, data.length].pack("A4N*")
+ table_data << data
+ head_offset = offset if tag == 'head'
+ offset += data.length
+ while offset % 4 != 0
+ offset += 1
+ table_data << "\0"
+ end
+ end
+
+ newfont << table_data
+ sum = checksum(newfont)
+ adjustment = 0xB1B0AFBA - sum
+ newfont[head_offset+8,4] = [adjustment].pack("N")
+
+ return newfont
+ end
+
+ private
+
+ def unicode_cmap
+ @unicode_cmap ||= @original.cmap.unicode.first
+ end
+
+ def checksum(data)
+ data += "\0" * (4 - data.length % 4) unless data.length % 4 == 0
+ data.unpack("N*").inject(0) { |sum, dword| sum + dword } & 0xFFFF_FFFF
+ end
+
+ def collect_glyphs(glyph_ids)
+ glyphs = glyph_ids.inject({}) { |h, id| h[id] = original.glyph_outlines.for(id); h }
+ additional_ids = glyphs.values.select { |g| g && g.compound? }.map { |g| g.glyph_ids }.flatten
+
+ glyphs.update(collect_glyphs(additional_ids)) if additional_ids.any?
+
+ return glyphs
+ end
+ end
+ end
+end
@@ -0,0 +1,41 @@
+require 'set'
+require 'ttfunk/subset/base'
+require 'ttfunk/encoding/mac_roman'
+
+module TTFunk
+ module Subset
+ class MacRoman < Base
+ def initialize(original)
+ super
+ @subset = Array.new(256)
+ end
+
+ def use(character)
+ @subset[Encoding::MacRoman::FROM_UNICODE[character]] = character
+ end
+
+ def covers?(character)
+ Encoding::MacRoman.covers?(character)
+ end
+
+ def from_unicode(character)
+ Encoding::MacRoman::FROM_UNICODE[character]
+ end
+
+ protected
+
+ def new_cmap_table(options)
+ mapping = {}
+ @subset.each_with_index do |unicode, roman|
+ mapping[roman] = unicode_cmap[unicode] if roman
+ end
+
+ TTFunk::Table::Cmap.encode(mapping, :mac_roman)
+ end
+
+ def original_glyph_ids
+ ([0] + @subset.map { |unicode| unicode && unicode_cmap[unicode] }).compact.uniq.sort
+ end
+ end
+ end
+end
@@ -0,0 +1,36 @@
+require 'set'
+require 'ttfunk/subset/base'
+
+module TTFunk
+ module Subset
+ class Unicode < Base
+ def initialize(original)
+ super
+ @subset = Set.new
+ end
+
+ def use(character)
+ @subset << character
+ end
+
+ def covers?(character)
+ true
+ end
+
+ def from_unicode(character)
+ character
+ end
+
+ protected
+
+ def new_cmap_table(options)
+ mapping = @subset.inject({}) { |map, code| map[code] = unicode_cmap[code]; map }
+ TTFunk::Table::Cmap.encode(mapping, :unicode)
+ end
+
+ def original_glyph_ids
+ ([0] + @subset.map { |code| unicode_cmap[code] }).uniq.sort
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 6fed25e

Please sign in to comment.