class TTFunk::Table::OS2
OS/2 and Windows Metrics (‘OS/2`) table
Constants
- CODEPOINT_SPACE
-
Space character code point.
- CODE_PAGE_BITS
-
Code page bits.
- LOWERCASE_COUNT
-
Number of chracters for average character width calculation.
- LOWERCASE_END
-
End chracter for average character width calculation.
- LOWERCASE_START
-
Start chracter for average character width calculation.
- SPACE_GLYPH_MISSING_ERROR
-
Error
message for missing space character. - UNICODE_BLOCKS
-
Unicode blocks.
- UNICODE_MAX
-
Indicates that font supports supplementary characters.
- UNICODE_RANGES
-
Unicode block ranges.
- WEIGHT_LOWERCASE
-
chracter weights for average character width calculation.
- 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
The typographic ascender for this font. @return [Integer]
Average weighted escapement. @return [Integer]
The Unicode code point, in UTF-16 encoding, of a character that can be used as a default break character. @return [Integer]
The distance between the baseline and the approximate height of uppercase letters. @return [Integer]
Unicode Character Range. @return [Integer]
Code Page Character Range. @return [Integer]
The Unicode code point, in UTF-16 encoding, of a character that can be used for a default glyph if a requested character is not supported in the font. @return [Integer]
The typographic descender for this font. @return [Integer]
Font-family class and subclass. @return [Integer]
The minimum Unicode index (character code) in this font. @return [Integer]
The maximum Unicode index (character code) in this font. @return [Integer]
The typographic line gap for this font. @return [Integer]
The maximum length of a target glyph context for any feature in this font. @return [Integer]
PANOSE classification number. @return [Integer]
Font selection flags. @return [Integer]
Type flags. @return [Integer]
Font Vendor Identification. @return [String]
Table
version. @return [Integer]
Weight class. @return [Integer]
Width class. @return [Integer]
The “Windows ascender” metric. @return [Integer]
The “Windows descender” metric. @return [Integer]
The distance between the baseline and the approximate height of non-ascending lowercase letters. @return [Integer]
Strikeout position. @return [Integer]
Strikeout size. @return [Integer]
Subscript x offset. @return [Integer]
Subscript horizontal font size. @return [Integer]
Subscript y offset. @return [Integer]
Subscript vertical font size. @return [Integer]
Superscript x offset. @return [Integer]
Superscript horizontal font size. @return [Integer]
Superscript y offset. @return [Integer]
Superscript vertical font size. @return [Integer]
Public Class Methods
Source
# File lib/ttfunk/table/os2.rb, line 411 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
Encode table.
@param os2 [TTFunk::Table::OS2] @param subset [TTFunk::Subset::MacRoman, TTFunk::Subset::Windows1252
,
TTFunk::Subset::Unicode, TTFunk::Subset::Unicode8Bit]
@return [String]
Private Class Methods
Source
# File lib/ttfunk/table/os2.rb, line 522 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
Source
# File lib/ttfunk/table/os2.rb, line 530 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_value 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
Source
# File lib/ttfunk/table/os2.rb, line 548 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_value 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
Source
# File lib/ttfunk/table/os2.rb, line 481 def code_pages_for(subset) field = BitField.new(0) return field if subset.unicode? field.on(CODE_PAGE_BITS[subset.code_page]) field end
Source
# File lib/ttfunk/table/os2.rb, line 507 def group_original_code_points_by_bit(os2) Hash.new { |h, k| h[k] = [] }.tap do |result| code_points = os2.file.cmap.unicode.first.code_map.keys.sort UNICODE_RANGES.each do |r| code_points = code_points.drop_while { |p| p < r.min } code_points.take_while { |p| p <= r.max }.each do |code_point| if (bit = UNICODE_BLOCKS[r]) result[bit] << code_point end end code_points = code_points.drop_while { |p| p <= r.max } end end end
Source
# File lib/ttfunk/table/os2.rb, line 489 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
Private Instance Methods
Source
# File lib/ttfunk/table/os2.rb, line 601 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