class TTFunk::Table::OS2
Constants
- CODEPOINT_SPACE
- CODE_PAGE_BITS
- LOWERCASE_COUNT
- LOWERCASE_END
- LOWERCASE_START
- SPACE_GLYPH_MISSING_ERROR
- UNICODE_BLOCKS
- UNICODE_MAX
- UNICODE_RANGES
- WEIGHT_LOWERCASE
- WEIGHT_SPACE
Used to calculate the xAvgCharWidth field. From docs.microsoft.com/en-us/typography/opentype/spec/os2:
“When first defined, the specification was biased toward Basic Latin characters, and it was thought that the xAvgCharWidth value could be used to estimate the average length of lines of text. A formula for calculating xAvgCharWidth was provided using frequency-of-use weighting factors for lowercase letters a - z.”
The array below contains 26 weight values which correspond to the 26 letters in the Latin alphabet. Each weight is the relative frequency of that letter in the English language.
Attributes
ascent[R]
ave_char_width[R]
break_char[R]
cap_height[R]
char_range[R]
code_page_range[R]
default_char[R]
descent[R]
family_class[R]
first_char_index[R]
last_char_index[R]
line_gap[R]
max_context[R]
panose[R]
selection[R]
type[R]
vendor_id[R]
version[R]
weight_class[R]
width_class[R]
win_ascent[R]
win_descent[R]
x_height[R]
y_strikeout_position[R]
y_strikeout_size[R]
y_subscript_x_offset[R]
y_subscript_x_size[R]
y_subscript_y_offset[R]
y_subscript_y_size[R]
y_superscript_x_offset[R]
y_superscript_x_size[R]
y_superscript_y_offset[R]
y_superscript_y_size[R]
Public Class Methods
encode(os2, subset)
click to toggle source
# File lib/ttfunk/table/os2.rb, line 285 def encode(os2, subset) result = ''.b result << [ os2.version, avg_char_width_for(os2, subset), os2.weight_class, os2.width_class, os2.type, os2.y_subscript_x_size, os2.y_subscript_y_size, os2.y_subscript_x_offset, os2.y_subscript_y_offset, os2.y_superscript_x_size, os2.y_superscript_y_size, os2.y_superscript_x_offset, os2.y_superscript_y_offset, os2.y_strikeout_size, os2.y_strikeout_position, os2.family_class ].pack('n*') result << os2.panose new_char_range = unicode_blocks_for(os2, os2.char_range, subset) result << BinUtils .slice_int( new_char_range.value, bit_width: 32, slice_count: 4 ) .pack('N*') result << os2.vendor_id new_cmap_table = subset.new_cmap_table[:charmap] code_points = new_cmap_table .keys .select { |k| (new_cmap_table[k][:new]).positive? } .sort # "This value depends on which character sets the font supports. # This field cannot represent supplementary character values # (codepoints greater than 0xFFFF). Fonts that support # supplementary characters should set the value in this field # to 0xFFFF." first_char_index = [code_points.first || 0, UNICODE_MAX].min last_char_index = [code_points.last || 0, UNICODE_MAX].min result << [ os2.selection, first_char_index, last_char_index ].pack('n*') if os2.version.positive? result << [ os2.ascent, os2.descent, os2.line_gap, os2.win_ascent, os2.win_descent ].pack('n*') result << BinUtils .slice_int( code_pages_for(subset).value, bit_width: 32, slice_count: 2 ) .pack('N*') if os2.version > 1 result << [ os2.x_height, os2.cap_height, os2.default_char, os2.break_char, os2.max_context ].pack('n*') end end result end
Private Class Methods
avg_char_width_for(os2, subset)
click to toggle source
# File lib/ttfunk/table/os2.rb, line 394 def avg_char_width_for(os2, subset) if subset.microsoft_symbol? avg_ms_symbol_char_width_for(os2, subset) else avg_weighted_char_width_for(os2, subset) end end
avg_ms_symbol_char_width_for(os2, subset)
click to toggle source
# File lib/ttfunk/table/os2.rb, line 402 def avg_ms_symbol_char_width_for(os2, subset) total_width = 0 num_glyphs = 0 # use new -> old glyph mapping in order to include compound glyphs # in the calculation subset.new_to_old_glyph.each do |_, old_gid| if (metric = os2.file.horizontal_metrics.for(old_gid)) total_width += metric.advance_width num_glyphs += 1 if metric.advance_width.positive? end end return 0 if num_glyphs.zero? total_width / num_glyphs # this should be a whole number end
avg_weighted_char_width_for(os2, subset)
click to toggle source
# File lib/ttfunk/table/os2.rb, line 420 def avg_weighted_char_width_for(os2, subset) # make sure the subset includes the space char unless subset.to_unicode_map[CODEPOINT_SPACE] raise SPACE_GLYPH_MISSING_ERROR end space_gid = os2.file.cmap.unicode.first[CODEPOINT_SPACE] space_hm = os2.file.horizontal_metrics.for(space_gid) return 0 unless space_hm total_weight = space_hm.advance_width * WEIGHT_SPACE num_lowercase = 0 # calculate the weighted sum of all the lowercase widths in # the subset LOWERCASE_START.upto(LOWERCASE_END) do |lowercase_cp| # make sure the subset includes the character next unless subset.to_unicode_map[lowercase_cp] lowercase_gid = os2.file.cmap.unicode.first[lowercase_cp] lowercase_hm = os2.file.horizontal_metrics.for(lowercase_gid) num_lowercase += 1 total_weight += lowercase_hm.advance_width * WEIGHT_LOWERCASE[lowercase_cp - 'a'.ord] end # return if all lowercase characters are present in the subset return total_weight / 1000 if num_lowercase == LOWERCASE_COUNT # If not all lowercase characters are present in the subset, take # the average width of all the subsetted characters. This differs # from avg_ms_char_width_for in that it includes zero-width glyphs # in the calculation. total_width = 0 num_glyphs = subset.new_to_old_glyph.size # use new -> old glyph mapping in order to include compound glyphs # in the calculation subset.new_to_old_glyph.each do |_, old_gid| if (metric = os2.file.horizontal_metrics.for(old_gid)) total_width += metric.advance_width end end return 0 if num_glyphs.zero? total_width / num_glyphs # this should be a whole number end
code_pages_for(subset)
click to toggle source
# File lib/ttfunk/table/os2.rb, line 355 def code_pages_for(subset) field = BitField.new(0) return field if subset.unicode? field.on(CODE_PAGE_BITS[subset.code_page]) field end
group_original_code_points_by_bit(os2)
click to toggle source
# File lib/ttfunk/table/os2.rb, line 381 def group_original_code_points_by_bit(os2) Hash.new { |h, k| h[k] = [] }.tap do |result| os2.file.cmap.unicode.first.code_map.each_key do |code_point| # find corresponding bit range = UNICODE_RANGES.find { |r| r.cover?(code_point) } if (bit = UNICODE_BLOCKS[range]) result[bit] << code_point end end end end
unicode_blocks_for(os2, original_field, subset)
click to toggle source
# File lib/ttfunk/table/os2.rb, line 363 def unicode_blocks_for(os2, original_field, subset) field = BitField.new(0) return field unless subset.unicode? subset_code_points = Set.new(subset.new_cmap_table[:charmap].keys) original_code_point_groups = group_original_code_points_by_bit(os2) original_code_point_groups.each do |bit, code_points| next if original_field.off?(bit) if code_points.any? { |cp| subset_code_points.include?(cp) } field.on(bit) end end field end
Public Instance Methods
tag()
click to toggle source
# File lib/ttfunk/table/os2.rb, line 280 def tag 'OS/2' end
Private Instance Methods
parse!()
click to toggle source
# File lib/ttfunk/table/os2.rb, line 473 def parse! @version = read(2, 'n').first @ave_char_width = read_signed(1).first @weight_class, @width_class = read(4, 'nn') @type, @y_subscript_x_size, @y_subscript_y_size, @y_subscript_x_offset, @y_subscript_y_offset, @y_superscript_x_size, @y_superscript_y_size, @y_superscript_x_offset, @y_superscript_y_offset, @y_strikeout_size, @y_strikeout_position, @family_class = read_signed(12) @panose = io.read(10) @char_range = BitField.new( BinUtils.stitch_int(read(16, 'N*'), bit_width: 32) ) @vendor_id = io.read(4) @selection, @first_char_index, @last_char_index = read(6, 'n*') if @version.positive? @ascent, @descent, @line_gap = read_signed(3) @win_ascent, @win_descent = read(4, 'nn') @code_page_range = BitField.new( BinUtils.stitch_int(read(8, 'N*'), bit_width: 32) ) if @version > 1 @x_height, @cap_height = read_signed(2) @default_char, @break_char, @max_context = read(6, 'nnn') # Set this to zero until GSUB/GPOS support has been implemented. # This value is calculated via those tables, and should be set to # zero if the data is not available. @max_context = 0 end end end