ttfunk 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +74 -0
  4. data/README.md +17 -15
  5. data/lib/ttfunk/aggregate.rb +5 -0
  6. data/lib/ttfunk/bin_utils.rb +27 -8
  7. data/lib/ttfunk/bit_field.rb +25 -2
  8. data/lib/ttfunk/collection.rb +27 -3
  9. data/lib/ttfunk/directory.rb +7 -1
  10. data/lib/ttfunk/encoded_string.rb +58 -4
  11. data/lib/ttfunk/max.rb +14 -0
  12. data/lib/ttfunk/min.rb +14 -0
  13. data/lib/ttfunk/one_based_array.rb +20 -0
  14. data/lib/ttfunk/otf_encoder.rb +5 -14
  15. data/lib/ttfunk/placeholder.rb +15 -1
  16. data/lib/ttfunk/reader.rb +6 -4
  17. data/lib/ttfunk/resource_file.rb +29 -5
  18. data/lib/ttfunk/sci_form.rb +20 -3
  19. data/lib/ttfunk/sub_table.rb +29 -4
  20. data/lib/ttfunk/subset/base.rb +48 -0
  21. data/lib/ttfunk/subset/code_page.rb +49 -2
  22. data/lib/ttfunk/subset/mac_roman.rb +2 -0
  23. data/lib/ttfunk/subset/unicode.rb +32 -0
  24. data/lib/ttfunk/subset/unicode_8bit.rb +32 -0
  25. data/lib/ttfunk/subset/windows_1252.rb +2 -0
  26. data/lib/ttfunk/subset.rb +8 -0
  27. data/lib/ttfunk/subset_collection.rb +39 -14
  28. data/lib/ttfunk/sum.rb +13 -0
  29. data/lib/ttfunk/table/cff/charset.rb +96 -18
  30. data/lib/ttfunk/table/cff/charsets/expert.rb +3 -2
  31. data/lib/ttfunk/table/cff/charsets/expert_subset.rb +3 -2
  32. data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +3 -2
  33. data/lib/ttfunk/table/cff/charsets/standard_strings.rb +3 -2
  34. data/lib/ttfunk/table/cff/charsets.rb +1 -0
  35. data/lib/ttfunk/table/cff/charstring.rb +33 -12
  36. data/lib/ttfunk/table/cff/charstrings_index.rb +17 -11
  37. data/lib/ttfunk/table/cff/dict.rb +53 -23
  38. data/lib/ttfunk/table/cff/encoding.rb +82 -24
  39. data/lib/ttfunk/table/cff/encodings/expert.rb +3 -2
  40. data/lib/ttfunk/table/cff/encodings/standard.rb +3 -2
  41. data/lib/ttfunk/table/cff/encodings.rb +1 -0
  42. data/lib/ttfunk/table/cff/fd_selector.rb +61 -21
  43. data/lib/ttfunk/table/cff/font_dict.rb +30 -18
  44. data/lib/ttfunk/table/cff/font_index.rb +22 -10
  45. data/lib/ttfunk/table/cff/header.rb +16 -3
  46. data/lib/ttfunk/table/cff/index.rb +97 -65
  47. data/lib/ttfunk/table/cff/one_based_index.rb +11 -1
  48. data/lib/ttfunk/table/cff/path.rb +43 -4
  49. data/lib/ttfunk/table/cff/private_dict.rb +31 -11
  50. data/lib/ttfunk/table/cff/subr_index.rb +7 -2
  51. data/lib/ttfunk/table/cff/top_dict.rb +82 -59
  52. data/lib/ttfunk/table/cff/top_index.rb +10 -6
  53. data/lib/ttfunk/table/cff.rb +41 -21
  54. data/lib/ttfunk/table/cmap/format00.rb +27 -6
  55. data/lib/ttfunk/table/cmap/format04.rb +34 -14
  56. data/lib/ttfunk/table/cmap/format06.rb +28 -1
  57. data/lib/ttfunk/table/cmap/format10.rb +29 -2
  58. data/lib/ttfunk/table/cmap/format12.rb +29 -2
  59. data/lib/ttfunk/table/cmap/subtable.rb +50 -6
  60. data/lib/ttfunk/table/cmap.rb +21 -0
  61. data/lib/ttfunk/table/dsig.rb +47 -6
  62. data/lib/ttfunk/table/glyf/compound.rb +73 -6
  63. data/lib/ttfunk/table/glyf/path_based.rb +40 -3
  64. data/lib/ttfunk/table/glyf/simple.rb +50 -5
  65. data/lib/ttfunk/table/glyf.rb +15 -7
  66. data/lib/ttfunk/table/head.rb +84 -6
  67. data/lib/ttfunk/table/hhea.rb +71 -10
  68. data/lib/ttfunk/table/hmtx.rb +32 -5
  69. data/lib/ttfunk/table/kern/format0.rb +25 -7
  70. data/lib/ttfunk/table/kern.rb +16 -4
  71. data/lib/ttfunk/table/loca.rb +21 -8
  72. data/lib/ttfunk/table/maxp.rb +195 -10
  73. data/lib/ttfunk/table/name.rb +126 -9
  74. data/lib/ttfunk/table/os2.rb +150 -26
  75. data/lib/ttfunk/table/post/format10.rb +7 -0
  76. data/lib/ttfunk/table/post/format20.rb +9 -0
  77. data/lib/ttfunk/table/post/format30.rb +6 -0
  78. data/lib/ttfunk/table/post/format40.rb +5 -0
  79. data/lib/ttfunk/table/post.rb +63 -7
  80. data/lib/ttfunk/table/sbix.rb +50 -14
  81. data/lib/ttfunk/table/simple.rb +5 -0
  82. data/lib/ttfunk/table/vorg.rb +31 -3
  83. data/lib/ttfunk/table.rb +20 -1
  84. data/lib/ttfunk/ttf_encoder.rb +39 -41
  85. data/lib/ttfunk.rb +154 -1
  86. data.tar.gz.sig +0 -0
  87. metadata +50 -28
  88. metadata.gz.sig +0 -0
@@ -3,26 +3,38 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Font Dict Index.
6
7
  class FontIndex < TTFunk::Table::Cff::Index
8
+ # Top dict.
9
+ # @return [TTFunk::Table::Cff::TopDict]
7
10
  attr_reader :top_dict
8
11
 
12
+ # @param top_dict [TTFunk::Table:Cff::TopDict]
13
+ # @param file [TTFunk::File]
14
+ # @param offset [Integer]
15
+ # @param length [Integer]
9
16
  def initialize(top_dict, file, offset, length = nil)
10
17
  super(file, offset, length)
11
18
  @top_dict = top_dict
12
19
  end
13
20
 
14
- def [](index)
15
- entry_cache[index] ||=
16
- begin
17
- start, finish = absolute_offsets_for(index)
18
- TTFunk::Table::Cff::FontDict.new(
19
- top_dict, file, start, (finish - start) + 1
20
- )
21
- end
21
+ # Finalize index.
22
+ #
23
+ # @param new_cff_data [TTFunk::EncodedString]
24
+ # @return [void]
25
+ def finalize(new_cff_data)
26
+ each { |font_dict| font_dict.finalize(new_cff_data) }
22
27
  end
23
28
 
24
- def finalize(new_cff_data, mapping)
25
- each { |font_dict| font_dict.finalize(new_cff_data, mapping) }
29
+ private
30
+
31
+ def decode_item(_index, offset, length)
32
+ TTFunk::Table::Cff::FontDict.new(top_dict, file, offset, length)
33
+ end
34
+
35
+ def encode_items(*)
36
+ # Re-encode font dicts
37
+ map(&:encode)
26
38
  end
27
39
  end
28
40
  end
@@ -3,21 +3,34 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Header.
6
7
  class Header < TTFunk::SubTable
7
- # cff format version numbers
8
+ # CFF table major version.
9
+ # @return [Integer]
8
10
  attr_reader :major
11
+
12
+ # CFF table minor version.
13
+ # @return [Integer]
9
14
  attr_reader :minor
10
15
 
11
- # size of the header itself
16
+ # Size of the header itself.
17
+ # @return [Integer]
12
18
  attr_reader :header_size
13
19
 
14
- # size of all offsets from beginning of table
20
+ # Size of all offsets from beginning of table.
21
+ # @return [Integer]
15
22
  attr_reader :absolute_offset_size
16
23
 
24
+ # Length of header.
25
+ #
26
+ # @return [Integer]
17
27
  def length
18
28
  4
19
29
  end
20
30
 
31
+ # Encode header.
32
+ #
33
+ # @return [String]
21
34
  def encode
22
35
  [major, minor, header_size, absolute_offset_size].pack('C*')
23
36
  end
@@ -3,75 +3,104 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Index.
6
7
  class Index < TTFunk::SubTable
7
8
  include Enumerable
8
9
 
9
- # number of objects in the index
10
- attr_reader :count
11
-
12
- # offset array element size
13
- attr_reader :offset_size
14
-
15
- attr_reader :raw_offset_length, :offsets, :raw_data
16
- attr_reader :data_start_pos
17
-
10
+ # Get value by index.
11
+ #
12
+ # @param index [Integer]
13
+ # @return [any]
18
14
  def [](index)
19
- entry_cache[index] ||= raw_data[
20
- offsets[index]...offsets[index + 1]
21
- ]
15
+ return if index >= items_count
16
+
17
+ entry_cache[index] ||=
18
+ decode_item(
19
+ index,
20
+ data_reference_offset + offsets[index],
21
+ offsets[index + 1] - offsets[index],
22
+ )
22
23
  end
23
24
 
24
- def each
25
- return to_enum(__method__) unless block_given?
25
+ # Iterate over index items.
26
+ #
27
+ # @overload each()
28
+ # @yieldparam item [any]
29
+ # @return [void]
30
+ # @overload each()
31
+ # @return [Enumerator]
32
+ def each(&block)
33
+ return to_enum(__method__) unless block
34
+
35
+ items_count.times do |i|
36
+ yield(self[i])
37
+ end
38
+ end
26
39
 
27
- count.times { |i| yield self[i] }
40
+ # Numer of items in this index.
41
+ #
42
+ # @return [Integer]
43
+ def items_count
44
+ items.length
28
45
  end
29
46
 
30
- def encode
31
- result = EncodedString.new
47
+ # Encode index.
48
+ #
49
+ # @param args all arguments are passed to `encode_item` method.
50
+ # @return [TTFunk::EncodedString]
51
+ def encode(*args)
52
+ new_items = encode_items(*args)
32
53
 
33
- entries =
34
- each_with_object([]).with_index do |(entry, ret), index|
35
- new_entry = block_given? ? yield(entry, index) : entry
36
- ret << new_entry if new_entry
37
- end
54
+ if new_items.empty?
55
+ return [0].pack('n')
56
+ end
38
57
 
39
- # "An empty INDEX is represented by a count field with a 0 value and
40
- # no additional fields. Thus, the total size of an empty INDEX is 2
41
- # bytes."
42
- result << [entries.size].pack('n')
43
- return result if entries.empty?
58
+ if new_items.length > 0xffff
59
+ raise Error, 'Too many items in a CFF index'
60
+ end
44
61
 
45
- offset_size = (Math.log2(entries.size) / 8.0).round + 1
46
- result << [offset_size].pack('C')
47
- data_offset = 1
62
+ offsets_array =
63
+ new_items
64
+ .each_with_object([1]) { |item, offsets|
65
+ offsets << (offsets.last + item.length)
66
+ }
48
67
 
49
- data = EncodedString.new
68
+ offset_size = (offsets_array.last.bit_length / 8.0).ceil
50
69
 
51
- entries.each do |entry|
52
- result << encode_offset(data_offset, offset_size)
53
- data << entry
54
- data_offset += entry.length
55
- end
70
+ offsets_array.map! { |offset| encode_offset(offset, offset_size) }
56
71
 
57
- unless entries.empty?
58
- result << encode_offset(data_offset, offset_size)
59
- end
60
-
61
- result << data
72
+ EncodedString.new.concat(
73
+ [new_items.length, offset_size].pack('nC'),
74
+ *offsets_array,
75
+ *new_items,
76
+ )
62
77
  end
63
78
 
64
79
  private
65
80
 
81
+ attr_reader :items
82
+ attr_reader :offsets
83
+ attr_reader :data_reference_offset
84
+
66
85
  def entry_cache
67
86
  @entry_cache ||= {}
68
87
  end
69
88
 
70
- def absolute_offsets_for(index)
71
- [
72
- table_offset + offsets[index] + data_start_pos,
73
- table_offset + offsets[index + 1] + data_start_pos
74
- ]
89
+ # Returns an array of EncodedString elements (plain strings,
90
+ # placeholders, or EncodedString instances). Each element is supposed to
91
+ # represent an encoded item.
92
+ #
93
+ # This is the place to do all the filtering, reordering, or individual
94
+ # item encoding.
95
+ #
96
+ # It gets all the arguments `encode` gets.
97
+ def encode_items(*)
98
+ items
99
+ end
100
+
101
+ # By default do nothing
102
+ def decode_item(index, _offset, _length)
103
+ items[index]
75
104
  end
76
105
 
77
106
  def encode_offset(offset, offset_size)
@@ -81,42 +110,45 @@ module TTFunk
81
110
  when 2
82
111
  [offset].pack('n')
83
112
  when 3
84
- [offset].pack('N')[1..-1]
113
+ [offset].pack('N')[1..]
85
114
  when 4
86
115
  [offset].pack('N')
87
116
  end
88
117
  end
89
118
 
90
119
  def parse!
91
- @count = read(2, 'n').first
120
+ @entry_cache = {}
121
+
122
+ num_entries = read(2, 'n').first
92
123
 
93
- if count.zero?
124
+ if num_entries.zero?
94
125
  @length = 2
95
- @data = []
126
+ @items = []
96
127
  return
97
128
  end
98
129
 
99
- @offset_size = read(1, 'C').first
130
+ offset_size = read(1, 'C').first
100
131
 
101
- # read an extra offset_size bytes to get rid of the first offset,
102
- # which is always 1
103
- io.read(offset_size)
132
+ @offsets =
133
+ Array.new(num_entries + 1) {
134
+ unpack_offset(io.read(offset_size), offset_size)
135
+ }
104
136
 
105
- @raw_offset_length = count * offset_size
106
- raw_offsets = io.read(raw_offset_length)
137
+ @data_reference_offset = table_offset + 3 + (offsets.length * offset_size) - 1
107
138
 
108
- @offsets = [0] + Array.new(count) do |idx|
109
- start = offset_size * idx
110
- finish = offset_size * (idx + 1)
111
- unpack_offset(raw_offsets[start...finish]) - 1
112
- end
139
+ @length =
140
+ 2 + # num entries
141
+ 1 + # offset size
142
+ (offsets.length * offset_size) + # offsets
143
+ offsets.last - 1 # items
113
144
 
114
- @raw_data = io.read(offsets.last)
115
- @data_start_pos = 3 + offset_size + raw_offset_length
116
- @length = data_start_pos + raw_data.size
145
+ @items =
146
+ offsets.each_cons(2).map { |offset, next_offset|
147
+ io.read(next_offset - offset)
148
+ }
117
149
  end
118
150
 
119
- def unpack_offset(offset_data)
151
+ def unpack_offset(offset_data, offset_size)
120
152
  padding = "\x00" * (4 - offset_size)
121
153
  (padding + offset_data).unpack1('N')
122
154
  end
@@ -5,22 +5,32 @@ require 'forwardable'
5
5
  module TTFunk
6
6
  class Table
7
7
  class Cff < TTFunk::Table
8
+ # CFF Index with indexing starting at 1.
8
9
  class OneBasedIndex
9
10
  extend Forwardable
10
11
 
11
12
  def_delegators :base_index,
12
13
  :each,
13
14
  :table_offset,
14
- :count,
15
+ :items_count,
15
16
  :length,
16
17
  :encode
17
18
 
19
+ # Underlaying Index.
20
+ # @return [TTFunk::Table::Cff::Index]
18
21
  attr_reader :base_index
19
22
 
23
+ # @param args [Array] all params are passed to the base index.
24
+ # @see Index
20
25
  def initialize(*args)
21
26
  @base_index = Index.new(*args)
22
27
  end
23
28
 
29
+ # Get item by index.
30
+ #
31
+ # @param idx [Integer]
32
+ # @return [any]
33
+ # @raise [IndexError] when requested index is 0.
24
34
  def [](idx)
25
35
  if idx.zero?
26
36
  raise IndexError,
@@ -3,33 +3,72 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # Path. Mostly used for CFF glyph outlines.
6
7
  class Path
8
+ # Close path command.
7
9
  CLOSE_PATH_CMD = [:close].freeze
8
10
 
9
- attr_reader :commands, :number_of_contours
11
+ # Commands in this path.
12
+ # @return [Array]
13
+ attr_reader :commands
14
+
15
+ # Number of contours in this path.
16
+ # @return [Integer]
17
+ attr_reader :number_of_contours
10
18
 
11
19
  def initialize
12
20
  @commands = []
13
21
  @number_of_contours = 0
14
22
  end
15
23
 
24
+ # Move implicit cursor to coordinates.
25
+ #
26
+ # @param x [Integer, Float]
27
+ # @param y [Integer, Float]
28
+ # @return [void]
16
29
  def move_to(x, y)
17
30
  @commands << [:move, x, y]
18
31
  end
19
32
 
33
+ # Add a line to coordinates.
34
+ #
35
+ # @param x [Integer, Float]
36
+ # @param y [Integer, Float]
37
+ # @return [void]
20
38
  def line_to(x, y)
21
39
  @commands << [:line, x, y]
22
40
  end
23
41
 
24
- def curve_to(x1, y1, x2, y2, x, y) # rubocop: disable Metrics/ParameterLists,Style/CommentedKeyword
42
+ # Add a Bézier curve. Current position is the first control point, (`x1`,
43
+ # `y1`) is the second, (`x2`, `y2`) is the third, and (`x`, `y`) is the
44
+ # last control point.
45
+ #
46
+ # @param x1 [Integer, Float]
47
+ # @param y1 [Integer, Float]
48
+ # @param x2 [Integer, Float]
49
+ # @param y2 [Integer, Float]
50
+ # @param x [Integer, Float]
51
+ # @param y [Integer, Float]
52
+ # @return [void]
53
+ def curve_to(x1, y1, x2, y2, x, y) # rubocop: disable Metrics/ParameterLists
25
54
  @commands << [:curve, x1, y1, x2, y2, x, y]
26
55
  end
27
56
 
57
+ # Close current contour.
58
+ #
59
+ # @return [void]
28
60
  def close_path
29
61
  @commands << CLOSE_PATH_CMD
30
62
  @number_of_contours += 1
31
63
  end
32
64
 
65
+ # Reposition and scale path.
66
+ #
67
+ # @param x [Integer, Float] new horizontal position.
68
+ # @param y [Integer, Float] new vertical position.
69
+ # @param font_size [Integer, Float] font size.
70
+ # @param units_per_em [Integer] units per Em as defined in the font.
71
+ # @return [TTFunk::Table::Cff::Path]
33
72
  def render(x: 0, y: 0, font_size: 72, units_per_em: 1000)
34
73
  new_path = self.class.new
35
74
  scale = 1.0 / units_per_em * font_size
@@ -47,7 +86,7 @@ module TTFunk
47
86
  x + (cmd[3] * scale),
48
87
  y + (-cmd[4] * scale),
49
88
  x + (cmd[5] * scale),
50
- y + (-cmd[6] * scale)
89
+ y + (-cmd[6] * scale),
51
90
  )
52
91
  when :close
53
92
  new_path.close_path
@@ -60,7 +99,7 @@ module TTFunk
60
99
  private
61
100
 
62
101
  def format_values(command)
63
- command[1..-1].map { |k| format('%.2f', k) }.join(' ')
102
+ command[1..].map { |k| format('%.2f', k) }.join(' ')
64
103
  end
65
104
  end
66
105
  end
@@ -3,22 +3,33 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Private dict.
6
7
  class PrivateDict < TTFunk::Table::Cff::Dict
8
+ # Default value of Default Width X.
7
9
  DEFAULT_WIDTH_X_DEFAULT = 0
10
+
11
+ # Default value of Nominal Width X.
8
12
  DEFAULT_WIDTH_X_NOMINAL = 0
13
+
14
+ # Length of placeholders.
9
15
  PLACEHOLDER_LENGTH = 5
10
16
 
17
+ # Operators we care about in this dict.
11
18
  OPERATORS = {
12
19
  subrs: 19,
13
20
  default_width_x: 20,
14
- nominal_width_x: 21
21
+ nominal_width_x: 21,
15
22
  }.freeze
16
23
 
24
+ # Inverse operator mapping.
17
25
  OPERATOR_CODES = OPERATORS.invert
18
26
 
19
- # @TODO: use mapping to determine which subroutines are still used.
20
- # For now, just encode them all.
21
- def encode(_mapping)
27
+ # Encode dict.
28
+ #
29
+ # @return [TTFunk::EncodedString]
30
+ def encode
31
+ # TODO: use mapping to determine which subroutines are still used.
32
+ # For now, just encode them all.
22
33
  EncodedString.new do |result|
23
34
  each do |operator, operands|
24
35
  case OPERATOR_CODES[operator]
@@ -33,19 +44,24 @@ module TTFunk
33
44
  end
34
45
  end
35
46
 
47
+ # Finalize dict.
48
+ #
49
+ # @param private_dict_data [TTFunk::EncodedString]
50
+ # @return [void]
36
51
  def finalize(private_dict_data)
37
52
  return unless subr_index
38
53
 
39
54
  encoded_subr_index = subr_index.encode
40
55
  encoded_offset = encode_integer32(private_dict_data.length)
41
56
 
42
- private_dict_data.resolve_placeholder(
43
- :"subrs_#{@table_offset}", encoded_offset
44
- )
57
+ private_dict_data.resolve_placeholder(:"subrs_#{@table_offset}", encoded_offset)
45
58
 
46
59
  private_dict_data << encoded_subr_index
47
60
  end
48
61
 
62
+ # Subroutine index.
63
+ #
64
+ # @return [TTFunk::Table::Cff::SubrIndex, nil]
49
65
  def subr_index
50
66
  @subr_index ||=
51
67
  if (subr_offset = self[OPERATORS[:subrs]])
@@ -53,6 +69,9 @@ module TTFunk
53
69
  end
54
70
  end
55
71
 
72
+ # Default Width X.
73
+ #
74
+ # @return [Integer]
56
75
  def default_width_x
57
76
  if (width = self[OPERATORS[:default_width_x]])
58
77
  width.first
@@ -61,6 +80,9 @@ module TTFunk
61
80
  end
62
81
  end
63
82
 
83
+ # Nominal Width X.
84
+ #
85
+ # @return [Integer]
64
86
  def nominal_width_x
65
87
  if (width = self[OPERATORS[:nominal_width_x]])
66
88
  width.first
@@ -72,10 +94,8 @@ module TTFunk
72
94
  private
73
95
 
74
96
  def encode_subrs
75
- EncodedString.new.tap do |result|
76
- result << Placeholder.new(
77
- :"subrs_#{@table_offset}", length: PLACEHOLDER_LENGTH
78
- )
97
+ EncodedString.new do |result|
98
+ result << Placeholder.new(:"subrs_#{@table_offset}", length: PLACEHOLDER_LENGTH)
79
99
  end
80
100
  end
81
101
  end
@@ -3,11 +3,16 @@
3
3
  module TTFunk
4
4
  class Table
5
5
  class Cff < TTFunk::Table
6
+ # CFF Subroutine index.
6
7
  class SubrIndex < TTFunk::Table::Cff::Index
8
+ # Subroutine index biase. For correct subroutine selection the
9
+ # calculated bias must be added to the subroutine number operand before
10
+ # accessing the index.
11
+ # @return [Integer]
7
12
  def bias
8
- if count < 1240
13
+ if items.length < 1240
9
14
  107
10
- elsif count < 33_900
15
+ elsif items.length < 33_900
11
16
  1131
12
17
  else
13
18
  32_768