fontisan 0.4.22 → 0.4.24
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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +240 -24
- data/Gemfile +6 -0
- data/Rakefile +9 -6
- data/benchmark/compile_benchmark.rb +13 -5
- data/docs/.vitepress/config.ts +1 -0
- data/docs/STITCHER_GUIDE.adoc +47 -3
- data/docs/api/layer.md +89 -0
- data/docs/cli/subset.md +25 -6
- data/docs/guide/cli/subset.md +31 -0
- data/docs/guide/color-fonts/bitmaps.md +17 -0
- data/lib/fontisan/cli.rb +2 -1
- data/lib/fontisan/collection/writer.rb +2 -1
- data/lib/fontisan/commands/validate_collection_command.rb +7 -1
- data/lib/fontisan/commands.rb +2 -1
- data/lib/fontisan/config/subset_profiles.yml +2 -0
- data/lib/fontisan/converters/collection_converter.rb +6 -1
- data/lib/fontisan/converters/svg_generator.rb +3 -1
- data/lib/fontisan/stitcher/cbdt_propagator.rb +44 -22
- data/lib/fontisan/stitcher/collection_result.rb +2 -1
- data/lib/fontisan/stitcher/glyph_cloner.rb +41 -0
- data/lib/fontisan/stitcher/glyph_copier.rb +5 -34
- data/lib/fontisan/stitcher/partition_strategy/by_block.rb +3 -1
- data/lib/fontisan/stitcher/partition_strategy/by_script.rb +6 -2
- data/lib/fontisan/stitcher/source.rb +2 -1
- data/lib/fontisan/stitcher/unique_glyph_name.rb +46 -0
- data/lib/fontisan/stitcher.rb +4 -1
- data/lib/fontisan/subset/shared_state.rb +42 -0
- data/lib/fontisan/subset/subset_context.rb +20 -0
- data/lib/fontisan/subset/table_strategy/cbdt.rb +30 -0
- data/lib/fontisan/subset/table_strategy/cblc.rb +27 -0
- data/lib/fontisan/subset/table_strategy/cmap.rb +146 -0
- data/lib/fontisan/subset/table_strategy/color_bitmap_placement.rb +20 -0
- data/lib/fontisan/subset/table_strategy/color_bitmap_strike_plan.rb +45 -0
- data/lib/fontisan/subset/table_strategy/color_bitmap_subset_plan.rb +62 -0
- data/lib/fontisan/subset/table_strategy/color_bitmap_subsetter.rb +182 -0
- data/lib/fontisan/subset/table_strategy/color_bitmap_subtable_plan.rb +59 -0
- data/lib/fontisan/subset/table_strategy/glyf.rb +29 -0
- data/lib/fontisan/subset/table_strategy/glyf_loca_builder.rb +146 -0
- data/lib/fontisan/subset/table_strategy/head.rb +42 -0
- data/lib/fontisan/subset/table_strategy/hhea.rb +67 -0
- data/lib/fontisan/subset/table_strategy/hmtx.rb +52 -0
- data/lib/fontisan/subset/table_strategy/loca.rb +41 -0
- data/lib/fontisan/subset/table_strategy/maxp.rb +23 -0
- data/lib/fontisan/subset/table_strategy/name.rb +19 -0
- data/lib/fontisan/subset/table_strategy/os2.rb +21 -0
- data/lib/fontisan/subset/table_strategy/pass_through.rb +20 -0
- data/lib/fontisan/subset/table_strategy/post.rb +37 -0
- data/lib/fontisan/subset/table_strategy.rb +75 -0
- data/lib/fontisan/subset/table_subsetter.rb +49 -616
- data/lib/fontisan/subset.rb +3 -0
- data/lib/fontisan/svg/standalone_glyph.rb +4 -1
- data/lib/fontisan/svg_to_glyf/assembler.rb +6 -3
- data/lib/fontisan/svg_to_glyf/geometry/transform_parser.rb +11 -4
- data/lib/fontisan/svg_to_glyf/geometry.rb +4 -2
- data/lib/fontisan/tables/cbdt.rb +66 -121
- data/lib/fontisan/tables/cblc.rb +110 -231
- data/lib/fontisan/tables/cblc_big_glyph_metrics.rb +28 -0
- data/lib/fontisan/tables/cblc_bitmap_size.rb +67 -0
- data/lib/fontisan/tables/cblc_glyph_bitmap_location.rb +28 -0
- data/lib/fontisan/tables/cblc_index_subtable.rb +104 -0
- data/lib/fontisan/tables/cblc_index_subtable_array_entry.rb +20 -0
- data/lib/fontisan/tables/cblc_index_subtable_format_parser.rb +150 -0
- data/lib/fontisan/tables/cblc_index_subtable_header.rb +18 -0
- data/lib/fontisan/tables/cblc_sbit_line_metrics.rb +28 -0
- data/lib/fontisan/tables/cff/cff2_charstring_builder.rb +11 -5
- data/lib/fontisan/tables/cff.rb +2 -1
- data/lib/fontisan/tables/cff2/index_builder.rb +2 -1
- data/lib/fontisan/tables.rb +12 -0
- data/lib/fontisan/ufo/cli.rb +4 -1
- data/lib/fontisan/ufo/compile/cbdt_cblc.rb +2 -1
- data/lib/fontisan/ufo/compile/cff.rb +6 -2
- data/lib/fontisan/ufo/compile/cmap.rb +11 -5
- data/lib/fontisan/ufo/compile/cpal.rb +2 -1
- data/lib/fontisan/ufo/compile/feature_writers/gdef.rb +3 -1
- data/lib/fontisan/ufo/compile/feature_writers/mark_family_base.rb +3 -1
- data/lib/fontisan/ufo/compile/feature_writers.rb +2 -1
- data/lib/fontisan/ufo/compile/filters/cubic_to_quadratic.rb +4 -2
- data/lib/fontisan/ufo/compile/filters/remove_overlaps.rb +2 -1
- data/lib/fontisan/ufo/compile/fvar.rb +7 -3
- data/lib/fontisan/ufo/compile/glyf_loca.rb +4 -2
- data/lib/fontisan/ufo/compile/gvar.rb +5 -2
- data/lib/fontisan/ufo/compile/head.rb +2 -1
- data/lib/fontisan/ufo/compile/item_variation_store.rb +2 -1
- data/lib/fontisan/ufo/compile/name.rb +3 -1
- data/lib/fontisan/ufo/compile/sbix.rb +2 -1
- data/lib/fontisan/ufo/compile/stat.rb +6 -3
- data/lib/fontisan/ufo/convert/from_bin_data.rb +6 -2
- data/lib/fontisan/ufo/convert/to_dfont.rb +6 -2
- data/lib/fontisan/ufo/convert/to_otc.rb +4 -1
- data/lib/fontisan/ufo/convert/to_postscript.rb +8 -3
- data/lib/fontisan/ufo/convert/to_woff.rb +8 -3
- data/lib/fontisan/ufo/convert/to_woff2.rb +8 -3
- data/lib/fontisan/ufo/convert.rb +4 -1
- data/lib/fontisan/ufo/glyph_exists_error.rb +23 -0
- data/lib/fontisan/ufo/info.rb +4 -2
- data/lib/fontisan/ufo/layer.rb +42 -2
- data/lib/fontisan/ufo/plist.rb +7 -2
- data/lib/fontisan/ufo/reader.rb +3 -1
- data/lib/fontisan/ufo/writer.rb +13 -5
- data/lib/fontisan/ufo.rb +2 -1
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/collection_decoder.rb +4 -1
- data/lib/fontisan/woff2/collection_encoder.rb +4 -1
- data/lib/fontisan/woff2/glyf_canonicalizer.rb +2 -1
- data/lib/fontisan/woff2/glyf_loca_reconstruct.rb +5 -2
- data/lib/fontisan/woff2/sfnt_checksum.rb +2 -1
- data/lib/fontisan.rb +0 -1
- metadata +36 -4
- data/lib/fontisan/tasks/fixture_downloader.rb +0 -162
- data/lib/fontisan/tasks.rb +0 -11
data/lib/fontisan/subset.rb
CHANGED
|
@@ -8,6 +8,9 @@ module Fontisan
|
|
|
8
8
|
autoload :GlyphMapping, "fontisan/subset/glyph_mapping"
|
|
9
9
|
autoload :Options, "fontisan/subset/options"
|
|
10
10
|
autoload :Profile, "fontisan/subset/profile"
|
|
11
|
+
autoload :SharedState, "fontisan/subset/shared_state"
|
|
12
|
+
autoload :SubsetContext, "fontisan/subset/subset_context"
|
|
13
|
+
autoload :TableStrategy, "fontisan/subset/table_strategy"
|
|
11
14
|
autoload :TableSubsetter, "fontisan/subset/table_subsetter"
|
|
12
15
|
end
|
|
13
16
|
end
|
|
@@ -118,7 +118,10 @@ module Fontisan
|
|
|
118
118
|
|
|
119
119
|
def bounding_box(glyph)
|
|
120
120
|
points = glyph.contours.flat_map(&:points)
|
|
121
|
-
|
|
121
|
+
if points.empty?
|
|
122
|
+
return { x_min: 0, y_min: 0, x_max: @units_per_em,
|
|
123
|
+
y_max: @units_per_em }
|
|
124
|
+
end
|
|
122
125
|
|
|
123
126
|
xs = points.map(&:x)
|
|
124
127
|
ys = points.map(&:y)
|
|
@@ -74,7 +74,8 @@ module Fontisan
|
|
|
74
74
|
private
|
|
75
75
|
|
|
76
76
|
def build_from_doc_path(data, group_transform, doc, codepoint)
|
|
77
|
-
final = normalizer_for(width: doc.viewbox_width,
|
|
77
|
+
final = normalizer_for(width: doc.viewbox_width,
|
|
78
|
+
height: doc.viewbox_height)
|
|
78
79
|
.final_transform(group_transform)
|
|
79
80
|
contours = build_contours(data, final)
|
|
80
81
|
assemble_glyph(glyph_name_for(codepoint), contours, codepoint)
|
|
@@ -89,13 +90,15 @@ module Fontisan
|
|
|
89
90
|
def transform_contour(contour, transform)
|
|
90
91
|
points = contour.points.map do |pt|
|
|
91
92
|
x, y = transform.apply(pt.x, pt.y)
|
|
92
|
-
Fontisan::Ufo::Point.new(x: x.round, y: y.round, type: pt.type,
|
|
93
|
+
Fontisan::Ufo::Point.new(x: x.round, y: y.round, type: pt.type,
|
|
94
|
+
smooth: pt.smooth)
|
|
93
95
|
end
|
|
94
96
|
Fontisan::Ufo::Contour.new(points)
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
def normalizer_for(width:, height:)
|
|
98
|
-
Geometry::Normalizer.new(viewbox_width: width, viewbox_height: height,
|
|
100
|
+
Geometry::Normalizer.new(viewbox_width: width, viewbox_height: height,
|
|
101
|
+
upm: @upm)
|
|
99
102
|
end
|
|
100
103
|
|
|
101
104
|
def assemble_glyph(name, contours, codepoint)
|
|
@@ -22,7 +22,9 @@ module Fontisan
|
|
|
22
22
|
transforms = transform_string.scan(FUNCTION_RE).map do |name, args|
|
|
23
23
|
build_transform(name, args)
|
|
24
24
|
end
|
|
25
|
-
transforms.reduce(AffineTransform.identity)
|
|
25
|
+
transforms.reduce(AffineTransform.identity) do |acc, t|
|
|
26
|
+
acc.compose(t)
|
|
27
|
+
end
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def self.build_transform(name, args_string)
|
|
@@ -35,7 +37,8 @@ module Fontisan
|
|
|
35
37
|
when "skewX" then AffineTransform.skew_x_radians(degrees_to_radians(args.fetch(0)))
|
|
36
38
|
when "skewY" then AffineTransform.skew_y_radians(degrees_to_radians(args.fetch(0)))
|
|
37
39
|
else
|
|
38
|
-
raise ArgumentError,
|
|
40
|
+
raise ArgumentError,
|
|
41
|
+
"unknown SVG transform function: #{name.inspect}"
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
|
|
@@ -59,9 +62,13 @@ module Fontisan
|
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
def self.build_matrix(args)
|
|
62
|
-
|
|
65
|
+
if args.size != 6
|
|
66
|
+
raise ArgumentError,
|
|
67
|
+
"matrix() requires 6 arguments, got #{args.size}"
|
|
68
|
+
end
|
|
63
69
|
|
|
64
|
-
AffineTransform.new(args[0], args[1], args[2], args[3], args[4],
|
|
70
|
+
AffineTransform.new(args[0], args[1], args[2], args[3], args[4],
|
|
71
|
+
args[5])
|
|
65
72
|
end
|
|
66
73
|
|
|
67
74
|
# Rotate around a specific point: translate to origin, rotate,
|
|
@@ -5,8 +5,10 @@ module Fontisan
|
|
|
5
5
|
# Geometry primitives for SvgToGlyf: affine transforms, SVG
|
|
6
6
|
# transform attribute parsing, and coordinate normalization.
|
|
7
7
|
module Geometry
|
|
8
|
-
autoload :AffineTransform,
|
|
9
|
-
|
|
8
|
+
autoload :AffineTransform,
|
|
9
|
+
"fontisan/svg_to_glyf/geometry/affine_transform"
|
|
10
|
+
autoload :TransformParser,
|
|
11
|
+
"fontisan/svg_to_glyf/geometry/transform_parser"
|
|
10
12
|
autoload :Normalizer, "fontisan/svg_to_glyf/geometry/normalizer"
|
|
11
13
|
end
|
|
12
14
|
end
|
data/lib/fontisan/tables/cbdt.rb
CHANGED
|
@@ -1,167 +1,112 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "stringio"
|
|
4
|
-
|
|
5
3
|
module Fontisan
|
|
6
4
|
module Tables
|
|
7
|
-
# CBDT (Color Bitmap Data) table
|
|
8
|
-
#
|
|
9
|
-
# The CBDT table contains the actual bitmap data for color glyphs. It works
|
|
10
|
-
# together with the CBLC table which provides the location information for
|
|
11
|
-
# finding bitmaps in this table.
|
|
12
|
-
#
|
|
13
|
-
# CBDT Table Structure:
|
|
14
|
-
# ```
|
|
15
|
-
# CBDT Table = Header (8 bytes)
|
|
16
|
-
# + Bitmap Data (variable length)
|
|
17
|
-
# ```
|
|
18
|
-
#
|
|
19
|
-
# Header (8 bytes):
|
|
20
|
-
# - majorVersion (uint16): Major version (2 or 3)
|
|
21
|
-
# - minorVersion (uint16): Minor version (0)
|
|
22
|
-
# - reserved (uint32): Reserved, set to 0
|
|
5
|
+
# CBDT (Color Bitmap Data) table.
|
|
23
6
|
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# - Format 19: Metrics in CBLC, PNG data
|
|
7
|
+
# CBDT stores the raw bitmap data blocks for color glyphs. CBLC indexes
|
|
8
|
+
# into CBDT via `imageDataOffset` and per-glyph offset arrays. CBDT
|
|
9
|
+
# itself is essentially a blob: the 4-byte header (majorVersion +
|
|
10
|
+
# minorVersion) followed by an opaque sequence of bitmap blocks.
|
|
29
11
|
#
|
|
30
|
-
# This
|
|
31
|
-
#
|
|
12
|
+
# This model exposes the header fields via BinData and preserves the
|
|
13
|
+
# original bytes for subsetting and direct slicing. A Cbdt constructed
|
|
14
|
+
# via `Cbdt.new` (no `read`) has no captured bytes — `raw_data` is nil
|
|
15
|
+
# and `data_size` returns 0.
|
|
32
16
|
#
|
|
33
|
-
# Reference: OpenType CBDT specification
|
|
34
|
-
# https://
|
|
17
|
+
# Reference: OpenType CBDT specification.
|
|
18
|
+
# https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt
|
|
35
19
|
#
|
|
36
|
-
# @example Reading a
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# bitmap_data = cbdt.bitmap_data_at(offset, length)
|
|
20
|
+
# @example Reading CBDT and slicing a bitmap block
|
|
21
|
+
# cbdt = Fontisan::Tables::Cbdt.read(font.table_data["CBDT"])
|
|
22
|
+
# bytes = cbdt.bitmap_data_at(120, 4096)
|
|
40
23
|
class Cbdt < Binary::BaseRecord
|
|
41
|
-
# OpenType table tag for CBDT
|
|
42
24
|
TAG = "CBDT"
|
|
43
25
|
|
|
44
|
-
#
|
|
45
|
-
VERSION_2_0 =
|
|
46
|
-
|
|
26
|
+
# CBDT v2.0 — original release
|
|
27
|
+
VERSION_2_0 = 0x00020000
|
|
28
|
+
# CBDT v3.0 — adds PNG image format support
|
|
29
|
+
VERSION_3_0 = 0x00030000
|
|
47
30
|
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
uint16 :major_version
|
|
32
|
+
uint16 :minor_version
|
|
50
33
|
|
|
51
|
-
#
|
|
52
|
-
attr_reader :minor_version
|
|
53
|
-
|
|
54
|
-
# @return [String] Raw binary data for the entire CBDT table
|
|
55
|
-
attr_reader :raw_data
|
|
56
|
-
|
|
57
|
-
# Override read to parse CBDT structure
|
|
34
|
+
# Whether this instance was populated from real table bytes.
|
|
58
35
|
#
|
|
59
|
-
# @
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
cbdt = new
|
|
63
|
-
return cbdt if io.nil?
|
|
64
|
-
|
|
65
|
-
data = io.is_a?(String) ? io : io.read
|
|
66
|
-
cbdt.parse!(data)
|
|
67
|
-
cbdt
|
|
36
|
+
# @return [Boolean]
|
|
37
|
+
def has_raw_data?
|
|
38
|
+
defined?(@raw_data) && !@raw_data.nil? ? true : false
|
|
68
39
|
end
|
|
69
40
|
|
|
70
|
-
#
|
|
41
|
+
# Raw bytes captured at read time, or nil for a fresh instance.
|
|
71
42
|
#
|
|
72
|
-
# @
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@raw_data = data
|
|
76
|
-
io = StringIO.new(data)
|
|
77
|
-
|
|
78
|
-
# Parse CBDT header (8 bytes)
|
|
79
|
-
parse_header(io)
|
|
80
|
-
validate_header!
|
|
81
|
-
rescue StandardError => e
|
|
82
|
-
raise CorruptedTableError, "Failed to parse CBDT table: #{e.message}"
|
|
83
|
-
end
|
|
43
|
+
# @return [String, nil]
|
|
44
|
+
def raw_data
|
|
45
|
+
return @raw_data if defined?(@raw_data)
|
|
84
46
|
|
|
85
|
-
|
|
86
|
-
#
|
|
87
|
-
# Used together with CBLC index to extract bitmap data.
|
|
88
|
-
#
|
|
89
|
-
# @param offset [Integer] Offset from start of table
|
|
90
|
-
# @param length [Integer] Length of bitmap data
|
|
91
|
-
# @return [String, nil] Binary bitmap data or nil
|
|
92
|
-
def bitmap_data_at(offset, length)
|
|
93
|
-
return nil if offset.nil? || length.nil?
|
|
94
|
-
return nil if offset.negative? || length.negative?
|
|
95
|
-
return nil if offset + length > raw_data.length
|
|
96
|
-
|
|
97
|
-
raw_data[offset, length]
|
|
47
|
+
nil
|
|
98
48
|
end
|
|
99
49
|
|
|
100
|
-
#
|
|
50
|
+
# Combined version number (e.g. 0x00030000 for v3.0).
|
|
101
51
|
#
|
|
102
|
-
# @return [Integer]
|
|
52
|
+
# @return [Integer]
|
|
103
53
|
def version
|
|
104
|
-
return nil if major_version.nil? || minor_version.nil?
|
|
105
|
-
|
|
106
54
|
(major_version << 16) | minor_version
|
|
107
55
|
end
|
|
108
56
|
|
|
109
|
-
#
|
|
57
|
+
# Total byte size of the captured CBDT bytes, or 0 if none.
|
|
110
58
|
#
|
|
111
|
-
# @return [Integer]
|
|
59
|
+
# @return [Integer]
|
|
112
60
|
def data_size
|
|
113
|
-
raw_data&.
|
|
61
|
+
raw_data&.bytesize || 0
|
|
114
62
|
end
|
|
115
63
|
|
|
116
|
-
#
|
|
64
|
+
# Whether `offset` falls within the captured CBDT byte range.
|
|
117
65
|
#
|
|
118
|
-
# @param offset [Integer]
|
|
119
|
-
# @return [Boolean]
|
|
66
|
+
# @param offset [Integer, nil]
|
|
67
|
+
# @return [Boolean]
|
|
120
68
|
def valid_offset?(offset)
|
|
121
69
|
return false if offset.nil? || offset.negative?
|
|
122
|
-
return false if raw_data.nil?
|
|
123
70
|
|
|
124
|
-
offset <
|
|
71
|
+
offset < data_size
|
|
125
72
|
end
|
|
126
73
|
|
|
127
|
-
#
|
|
74
|
+
# Slice `length` bytes starting at `offset` from the CBDT blob.
|
|
75
|
+
# Returns nil for out-of-range offsets/lengths so callers can use a
|
|
76
|
+
# nil check instead of rescuing IndexError.
|
|
128
77
|
#
|
|
129
|
-
# @
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return
|
|
134
|
-
return
|
|
78
|
+
# @param offset [Integer, nil] start byte
|
|
79
|
+
# @param length [Integer, nil] number of bytes
|
|
80
|
+
# @return [String, nil]
|
|
81
|
+
def bitmap_data_at(offset, length)
|
|
82
|
+
return nil if offset.nil? || length.nil?
|
|
83
|
+
return nil if offset.negative? || length.negative?
|
|
84
|
+
return nil if offset + length > data_size
|
|
135
85
|
|
|
136
|
-
|
|
86
|
+
raw_data[offset, length]
|
|
137
87
|
end
|
|
138
88
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
#
|
|
89
|
+
# Serialize the captured bytes back to binary. Falls back to BinData's
|
|
90
|
+
# default serialization (4-byte header) if no bytes were captured,
|
|
91
|
+
# so consumers can construct a CBDT from scratch.
|
|
142
92
|
#
|
|
143
|
-
# @
|
|
144
|
-
def
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
93
|
+
# @return [String]
|
|
94
|
+
def to_binary_s
|
|
95
|
+
return raw_data if has_raw_data?
|
|
96
|
+
|
|
97
|
+
super
|
|
148
98
|
end
|
|
149
99
|
|
|
150
|
-
#
|
|
100
|
+
# Whether the header declares a CBDT version fontisan understands
|
|
101
|
+
# (v2.0 or v3.0) and any raw bytes were captured.
|
|
151
102
|
#
|
|
152
|
-
# @
|
|
153
|
-
def
|
|
154
|
-
unless [2, 3].include?(major_version)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
unless minor_version.zero?
|
|
161
|
-
raise CorruptedTableError,
|
|
162
|
-
"Unsupported CBDT minor version: #{minor_version} " \
|
|
163
|
-
"(only version 0 supported)"
|
|
164
|
-
end
|
|
103
|
+
# @return [Boolean]
|
|
104
|
+
def valid?
|
|
105
|
+
return false unless [2, 3].include?(major_version)
|
|
106
|
+
return false unless minor_version.zero?
|
|
107
|
+
return false unless has_raw_data?
|
|
108
|
+
|
|
109
|
+
true
|
|
165
110
|
end
|
|
166
111
|
end
|
|
167
112
|
end
|