fontisan 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +529 -65
- data/Gemfile +1 -0
- data/LICENSE +5 -1
- data/README.adoc +1301 -275
- data/Rakefile +27 -2
- data/benchmark/variation_quick_bench.rb +47 -0
- data/docs/EXTRACT_TTC_MIGRATION.md +549 -0
- data/fontisan.gemspec +4 -1
- data/lib/fontisan/binary/base_record.rb +22 -1
- data/lib/fontisan/cli.rb +309 -0
- data/lib/fontisan/collection/builder.rb +260 -0
- data/lib/fontisan/collection/offset_calculator.rb +227 -0
- data/lib/fontisan/collection/table_analyzer.rb +204 -0
- data/lib/fontisan/collection/table_deduplicator.rb +241 -0
- data/lib/fontisan/collection/writer.rb +306 -0
- data/lib/fontisan/commands/base_command.rb +8 -1
- data/lib/fontisan/commands/convert_command.rb +291 -0
- data/lib/fontisan/commands/export_command.rb +161 -0
- data/lib/fontisan/commands/info_command.rb +40 -6
- data/lib/fontisan/commands/instance_command.rb +295 -0
- data/lib/fontisan/commands/ls_command.rb +113 -0
- data/lib/fontisan/commands/pack_command.rb +241 -0
- data/lib/fontisan/commands/subset_command.rb +245 -0
- data/lib/fontisan/commands/unpack_command.rb +338 -0
- data/lib/fontisan/commands/validate_command.rb +178 -0
- data/lib/fontisan/commands/variable_command.rb +30 -1
- data/lib/fontisan/config/collection_settings.yml +56 -0
- data/lib/fontisan/config/conversion_matrix.yml +212 -0
- data/lib/fontisan/config/export_settings.yml +66 -0
- data/lib/fontisan/config/subset_profiles.yml +100 -0
- data/lib/fontisan/config/svg_settings.yml +60 -0
- data/lib/fontisan/config/validation_rules.yml +149 -0
- data/lib/fontisan/config/variable_settings.yml +99 -0
- data/lib/fontisan/config/woff2_settings.yml +77 -0
- data/lib/fontisan/constants.rb +69 -0
- data/lib/fontisan/converters/conversion_strategy.rb +96 -0
- data/lib/fontisan/converters/format_converter.rb +259 -0
- data/lib/fontisan/converters/outline_converter.rb +936 -0
- data/lib/fontisan/converters/svg_generator.rb +244 -0
- data/lib/fontisan/converters/table_copier.rb +117 -0
- data/lib/fontisan/converters/woff2_encoder.rb +416 -0
- data/lib/fontisan/converters/woff_writer.rb +391 -0
- data/lib/fontisan/error.rb +203 -0
- data/lib/fontisan/export/exporter.rb +262 -0
- data/lib/fontisan/export/table_serializer.rb +255 -0
- data/lib/fontisan/export/transformers/font_to_ttx.rb +172 -0
- data/lib/fontisan/export/transformers/head_transformer.rb +96 -0
- data/lib/fontisan/export/transformers/hhea_transformer.rb +59 -0
- data/lib/fontisan/export/transformers/maxp_transformer.rb +63 -0
- data/lib/fontisan/export/transformers/name_transformer.rb +63 -0
- data/lib/fontisan/export/transformers/os2_transformer.rb +121 -0
- data/lib/fontisan/export/transformers/post_transformer.rb +51 -0
- data/lib/fontisan/export/ttx_generator.rb +527 -0
- data/lib/fontisan/export/ttx_parser.rb +300 -0
- data/lib/fontisan/font_loader.rb +121 -12
- data/lib/fontisan/font_writer.rb +301 -0
- data/lib/fontisan/formatters/text_formatter.rb +102 -0
- data/lib/fontisan/glyph_accessor.rb +503 -0
- data/lib/fontisan/hints/hint_converter.rb +177 -0
- data/lib/fontisan/hints/postscript_hint_applier.rb +185 -0
- data/lib/fontisan/hints/postscript_hint_extractor.rb +254 -0
- data/lib/fontisan/hints/truetype_hint_applier.rb +71 -0
- data/lib/fontisan/hints/truetype_hint_extractor.rb +162 -0
- data/lib/fontisan/loading_modes.rb +113 -0
- data/lib/fontisan/metrics_calculator.rb +277 -0
- data/lib/fontisan/models/collection_font_summary.rb +52 -0
- data/lib/fontisan/models/collection_info.rb +76 -0
- data/lib/fontisan/models/collection_list_info.rb +37 -0
- data/lib/fontisan/models/font_export.rb +158 -0
- data/lib/fontisan/models/font_summary.rb +48 -0
- data/lib/fontisan/models/glyph_outline.rb +343 -0
- data/lib/fontisan/models/hint.rb +233 -0
- data/lib/fontisan/models/outline.rb +664 -0
- data/lib/fontisan/models/table_sharing_info.rb +40 -0
- data/lib/fontisan/models/ttx/glyph_order.rb +31 -0
- data/lib/fontisan/models/ttx/tables/binary_table.rb +67 -0
- data/lib/fontisan/models/ttx/tables/head_table.rb +74 -0
- data/lib/fontisan/models/ttx/tables/hhea_table.rb +74 -0
- data/lib/fontisan/models/ttx/tables/maxp_table.rb +55 -0
- data/lib/fontisan/models/ttx/tables/name_table.rb +45 -0
- data/lib/fontisan/models/ttx/tables/os2_table.rb +157 -0
- data/lib/fontisan/models/ttx/tables/post_table.rb +50 -0
- data/lib/fontisan/models/ttx/ttfont.rb +49 -0
- data/lib/fontisan/models/validation_report.rb +203 -0
- data/lib/fontisan/open_type_collection.rb +156 -2
- data/lib/fontisan/open_type_font.rb +296 -10
- data/lib/fontisan/optimizers/charstring_rewriter.rb +161 -0
- data/lib/fontisan/optimizers/pattern_analyzer.rb +308 -0
- data/lib/fontisan/optimizers/stack_tracker.rb +246 -0
- data/lib/fontisan/optimizers/subroutine_builder.rb +134 -0
- data/lib/fontisan/optimizers/subroutine_generator.rb +207 -0
- data/lib/fontisan/optimizers/subroutine_optimizer.rb +107 -0
- data/lib/fontisan/outline_extractor.rb +423 -0
- data/lib/fontisan/subset/builder.rb +268 -0
- data/lib/fontisan/subset/glyph_mapping.rb +215 -0
- data/lib/fontisan/subset/options.rb +142 -0
- data/lib/fontisan/subset/profile.rb +152 -0
- data/lib/fontisan/subset/table_subsetter.rb +461 -0
- data/lib/fontisan/svg/font_face_generator.rb +278 -0
- data/lib/fontisan/svg/font_generator.rb +264 -0
- data/lib/fontisan/svg/glyph_generator.rb +168 -0
- data/lib/fontisan/svg/view_box_calculator.rb +137 -0
- data/lib/fontisan/tables/cff/cff_glyph.rb +176 -0
- data/lib/fontisan/tables/cff/charset.rb +282 -0
- data/lib/fontisan/tables/cff/charstring.rb +905 -0
- data/lib/fontisan/tables/cff/charstring_builder.rb +322 -0
- data/lib/fontisan/tables/cff/charstrings_index.rb +162 -0
- data/lib/fontisan/tables/cff/dict.rb +351 -0
- data/lib/fontisan/tables/cff/dict_builder.rb +242 -0
- data/lib/fontisan/tables/cff/encoding.rb +274 -0
- data/lib/fontisan/tables/cff/header.rb +102 -0
- data/lib/fontisan/tables/cff/index.rb +237 -0
- data/lib/fontisan/tables/cff/index_builder.rb +170 -0
- data/lib/fontisan/tables/cff/private_dict.rb +284 -0
- data/lib/fontisan/tables/cff/top_dict.rb +236 -0
- data/lib/fontisan/tables/cff.rb +487 -0
- data/lib/fontisan/tables/cff2/blend_operator.rb +240 -0
- data/lib/fontisan/tables/cff2/charstring_parser.rb +591 -0
- data/lib/fontisan/tables/cff2/operand_stack.rb +232 -0
- data/lib/fontisan/tables/cff2.rb +341 -0
- data/lib/fontisan/tables/cvar.rb +242 -0
- data/lib/fontisan/tables/fvar.rb +2 -2
- data/lib/fontisan/tables/glyf/compound_glyph.rb +483 -0
- data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +136 -0
- data/lib/fontisan/tables/glyf/curve_converter.rb +343 -0
- data/lib/fontisan/tables/glyf/glyph_builder.rb +450 -0
- data/lib/fontisan/tables/glyf/simple_glyph.rb +382 -0
- data/lib/fontisan/tables/glyf.rb +235 -0
- data/lib/fontisan/tables/gvar.rb +270 -0
- data/lib/fontisan/tables/hhea.rb +124 -0
- data/lib/fontisan/tables/hmtx.rb +287 -0
- data/lib/fontisan/tables/hvar.rb +191 -0
- data/lib/fontisan/tables/loca.rb +322 -0
- data/lib/fontisan/tables/maxp.rb +192 -0
- data/lib/fontisan/tables/mvar.rb +185 -0
- data/lib/fontisan/tables/name.rb +99 -30
- data/lib/fontisan/tables/variation_common.rb +346 -0
- data/lib/fontisan/tables/vvar.rb +234 -0
- data/lib/fontisan/true_type_collection.rb +156 -2
- data/lib/fontisan/true_type_font.rb +297 -11
- data/lib/fontisan/utilities/brotli_wrapper.rb +159 -0
- data/lib/fontisan/utilities/checksum_calculator.rb +18 -0
- data/lib/fontisan/utils/thread_pool.rb +134 -0
- data/lib/fontisan/validation/checksum_validator.rb +170 -0
- data/lib/fontisan/validation/consistency_validator.rb +197 -0
- data/lib/fontisan/validation/structure_validator.rb +198 -0
- data/lib/fontisan/validation/table_validator.rb +158 -0
- data/lib/fontisan/validation/validator.rb +152 -0
- data/lib/fontisan/variable/axis_normalizer.rb +215 -0
- data/lib/fontisan/variable/delta_applicator.rb +313 -0
- data/lib/fontisan/variable/glyph_delta_processor.rb +218 -0
- data/lib/fontisan/variable/instancer.rb +344 -0
- data/lib/fontisan/variable/metric_delta_processor.rb +282 -0
- data/lib/fontisan/variable/region_matcher.rb +208 -0
- data/lib/fontisan/variable/static_font_builder.rb +213 -0
- data/lib/fontisan/variable/table_updater.rb +219 -0
- data/lib/fontisan/variation/blend_applier.rb +199 -0
- data/lib/fontisan/variation/cache.rb +298 -0
- data/lib/fontisan/variation/cache_key_builder.rb +162 -0
- data/lib/fontisan/variation/converter.rb +268 -0
- data/lib/fontisan/variation/data_extractor.rb +86 -0
- data/lib/fontisan/variation/delta_applier.rb +266 -0
- data/lib/fontisan/variation/delta_parser.rb +228 -0
- data/lib/fontisan/variation/inspector.rb +275 -0
- data/lib/fontisan/variation/instance_generator.rb +273 -0
- data/lib/fontisan/variation/interpolator.rb +231 -0
- data/lib/fontisan/variation/metrics_adjuster.rb +318 -0
- data/lib/fontisan/variation/optimizer.rb +418 -0
- data/lib/fontisan/variation/parallel_generator.rb +150 -0
- data/lib/fontisan/variation/region_matcher.rb +221 -0
- data/lib/fontisan/variation/subsetter.rb +463 -0
- data/lib/fontisan/variation/table_accessor.rb +105 -0
- data/lib/fontisan/variation/validator.rb +345 -0
- data/lib/fontisan/variation/variation_context.rb +211 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/directory.rb +257 -0
- data/lib/fontisan/woff2/header.rb +101 -0
- data/lib/fontisan/woff2/table_transformer.rb +163 -0
- data/lib/fontisan/woff2_font.rb +712 -0
- data/lib/fontisan/woff_font.rb +483 -0
- data/lib/fontisan.rb +120 -0
- data/scripts/compare_stack_aware.rb +187 -0
- data/scripts/measure_optimization.rb +141 -0
- metadata +205 -4
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontisan
|
|
4
|
+
module Tables
|
|
5
|
+
class Cff2
|
|
6
|
+
# Blend operator handler for CFF2 CharStrings
|
|
7
|
+
#
|
|
8
|
+
# The blend operator is the key mechanism for applying variations in CFF2.
|
|
9
|
+
# It takes base values and deltas, and applies them based on variation
|
|
10
|
+
# coordinates to produce blended values.
|
|
11
|
+
#
|
|
12
|
+
# Blend Operator Format:
|
|
13
|
+
# Stack: base1 Δ1_axis1 ... Δ1_axisN base2 Δ2_axis1 ... Δ2_axisN ... K N
|
|
14
|
+
#
|
|
15
|
+
# Where:
|
|
16
|
+
# - base_i = base value for the i-th operand
|
|
17
|
+
# - Δi_axisj = delta for i-th operand on j-th axis
|
|
18
|
+
# - K = number of operands to blend (integer)
|
|
19
|
+
# - N = number of variation axes (integer)
|
|
20
|
+
#
|
|
21
|
+
# Result:
|
|
22
|
+
# Produces K blended values on the stack
|
|
23
|
+
#
|
|
24
|
+
# Blending Formula:
|
|
25
|
+
# blended_value = base + Σ(delta[i] * scalar[i])
|
|
26
|
+
#
|
|
27
|
+
# Where scalar[i] is computed from the current design space coordinates
|
|
28
|
+
# for axis i.
|
|
29
|
+
#
|
|
30
|
+
# Reference: Adobe Technical Note #5177
|
|
31
|
+
#
|
|
32
|
+
# @example Applying blend with coordinates
|
|
33
|
+
# blend = BlendOperator.new(num_axes: 2)
|
|
34
|
+
# data = {
|
|
35
|
+
# num_values: 2,
|
|
36
|
+
# num_axes: 2,
|
|
37
|
+
# blends: [
|
|
38
|
+
# { base: 100, deltas: [10, 5] },
|
|
39
|
+
# { base: 200, deltas: [20, 10] }
|
|
40
|
+
# ]
|
|
41
|
+
# }
|
|
42
|
+
# scalars = [0.5, 0.3] # From coordinate interpolation
|
|
43
|
+
# result = blend.apply(data, scalars)
|
|
44
|
+
# # => [105.0, 216.0] # 100 + (10*0.5 + 5*0.3), 200 + (20*0.5 + 10*0.3)
|
|
45
|
+
class BlendOperator
|
|
46
|
+
# @return [Integer] Number of variation axes
|
|
47
|
+
attr_reader :num_axes
|
|
48
|
+
|
|
49
|
+
# Initialize blend operator handler
|
|
50
|
+
#
|
|
51
|
+
# @param num_axes [Integer] Number of variation axes
|
|
52
|
+
def initialize(num_axes:)
|
|
53
|
+
@num_axes = num_axes
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Parse blend operands from stack
|
|
57
|
+
#
|
|
58
|
+
# Extracts blend data from a flattened array of operands.
|
|
59
|
+
#
|
|
60
|
+
# @param operands [Array<Numeric>] Stack operands (including K and N)
|
|
61
|
+
# @return [Hash, nil] Parsed blend data or nil if invalid
|
|
62
|
+
def parse(operands)
|
|
63
|
+
return nil if operands.size < 2
|
|
64
|
+
|
|
65
|
+
# Last two values are K and N
|
|
66
|
+
n = operands[-1].to_i
|
|
67
|
+
k = operands[-2].to_i
|
|
68
|
+
|
|
69
|
+
# Validate number of axes matches
|
|
70
|
+
if n != @num_axes
|
|
71
|
+
warn "Blend operator axes mismatch: expected #{@num_axes}, got #{n}"
|
|
72
|
+
return nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Validate we have enough operands: K * (N + 1) + 2
|
|
76
|
+
required_total = k * (n + 1) + 2
|
|
77
|
+
if operands.size < required_total
|
|
78
|
+
warn "Blend requires #{required_total} operands, got #{operands.size}"
|
|
79
|
+
return nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Extract blend operands (everything except K and N)
|
|
83
|
+
blend_operands = operands[-required_total..-3]
|
|
84
|
+
|
|
85
|
+
# Parse into base + deltas structure
|
|
86
|
+
blends = []
|
|
87
|
+
k.times do |i|
|
|
88
|
+
offset = i * (n + 1)
|
|
89
|
+
base = blend_operands[offset]
|
|
90
|
+
deltas = blend_operands[offset + 1, n] || []
|
|
91
|
+
|
|
92
|
+
blends << {
|
|
93
|
+
base: base,
|
|
94
|
+
deltas: deltas,
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
num_values: k,
|
|
100
|
+
num_axes: n,
|
|
101
|
+
blends: blends,
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Apply blend operation with variation scalars
|
|
106
|
+
#
|
|
107
|
+
# Computes blended values from base values and deltas using the
|
|
108
|
+
# provided scalars (one per axis).
|
|
109
|
+
#
|
|
110
|
+
# @param blend_data [Hash] Parsed blend data from parse()
|
|
111
|
+
# @param scalars [Array<Float>] Variation scalars (one per axis)
|
|
112
|
+
# @return [Array<Float>] Blended values
|
|
113
|
+
def apply(blend_data, scalars)
|
|
114
|
+
return [] if blend_data.nil?
|
|
115
|
+
|
|
116
|
+
# Ensure we have scalars for all axes
|
|
117
|
+
scalars = Array(scalars)
|
|
118
|
+
if scalars.size < blend_data[:num_axes]
|
|
119
|
+
# Pad with zeros if not enough scalars
|
|
120
|
+
scalars = scalars + ([0.0] * (blend_data[:num_axes] - scalars.size))
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Apply blend to each value
|
|
124
|
+
blend_data[:blends].map do |blend|
|
|
125
|
+
apply_single_blend(blend, scalars)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Apply blend to a single value
|
|
130
|
+
#
|
|
131
|
+
# @param blend [Hash] Single blend entry with :base and :deltas
|
|
132
|
+
# @param scalars [Array<Float>] Variation scalars
|
|
133
|
+
# @return [Float] Blended value
|
|
134
|
+
def apply_single_blend(blend, scalars)
|
|
135
|
+
base = blend[:base].to_f
|
|
136
|
+
deltas = blend[:deltas]
|
|
137
|
+
|
|
138
|
+
# Apply formula: result = base + Σ(delta[i] * scalar[i])
|
|
139
|
+
result = base
|
|
140
|
+
deltas.each_with_index do |delta, axis_index|
|
|
141
|
+
scalar = scalars[axis_index] || 0.0
|
|
142
|
+
result += delta.to_f * scalar
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
result
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Calculate variation scalars from coordinates
|
|
149
|
+
#
|
|
150
|
+
# This converts normalized coordinates [-1, 1] to scalars for each axis.
|
|
151
|
+
# For now, this is a simple pass-through, but will integrate with the
|
|
152
|
+
# interpolator in Phase B.
|
|
153
|
+
#
|
|
154
|
+
# @param coordinates [Hash<String, Float>] Axis coordinates
|
|
155
|
+
# @param axes [Array<VariationAxisRecord>] Variation axes from fvar
|
|
156
|
+
# @return [Array<Float>] Scalars for each axis
|
|
157
|
+
def calculate_scalars(coordinates, axes)
|
|
158
|
+
return [] if axes.nil? || axes.empty?
|
|
159
|
+
|
|
160
|
+
axes.map do |axis|
|
|
161
|
+
coord = coordinates[axis.axis_tag] || axis.default_value
|
|
162
|
+
normalize_coordinate(coord, axis)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Normalize a coordinate value to [-1, 1] range
|
|
167
|
+
#
|
|
168
|
+
# @param value [Float] Coordinate value
|
|
169
|
+
# @param axis [VariationAxisRecord] Axis definition
|
|
170
|
+
# @return [Float] Normalized coordinate in [-1, 1]
|
|
171
|
+
def normalize_coordinate(value, axis)
|
|
172
|
+
# Clamp to axis range
|
|
173
|
+
value = [[value, axis.min_value].max, axis.max_value].min
|
|
174
|
+
|
|
175
|
+
# Normalize to [-1, 1]
|
|
176
|
+
if value < axis.default_value
|
|
177
|
+
# Normalize between min and default (maps to -1..0)
|
|
178
|
+
range = axis.default_value - axis.min_value
|
|
179
|
+
return -1.0 if range.zero?
|
|
180
|
+
|
|
181
|
+
(value - axis.default_value) / range
|
|
182
|
+
elsif value > axis.default_value
|
|
183
|
+
# Normalize between default and max (maps to 0..1)
|
|
184
|
+
range = axis.max_value - axis.default_value
|
|
185
|
+
return 1.0 if range.zero?
|
|
186
|
+
|
|
187
|
+
(value - axis.default_value) / range
|
|
188
|
+
else
|
|
189
|
+
# At default value
|
|
190
|
+
0.0
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Validate blend data structure
|
|
195
|
+
#
|
|
196
|
+
# @param blend_data [Hash] Blend data to validate
|
|
197
|
+
# @return [Boolean] True if valid
|
|
198
|
+
def valid?(blend_data)
|
|
199
|
+
return false if blend_data.nil?
|
|
200
|
+
return false unless blend_data.is_a?(Hash)
|
|
201
|
+
return false unless blend_data.key?(:num_values)
|
|
202
|
+
return false unless blend_data.key?(:num_axes)
|
|
203
|
+
return false unless blend_data.key?(:blends)
|
|
204
|
+
return false unless blend_data[:num_values].is_a?(Integer)
|
|
205
|
+
return false unless blend_data[:num_axes].is_a?(Integer)
|
|
206
|
+
return false unless blend_data[:blends].is_a?(Array)
|
|
207
|
+
return false if blend_data[:blends].size != blend_data[:num_values]
|
|
208
|
+
|
|
209
|
+
# Validate each blend entry
|
|
210
|
+
blend_data[:blends].all? do |blend|
|
|
211
|
+
blend.is_a?(Hash) &&
|
|
212
|
+
blend.key?(:base) &&
|
|
213
|
+
blend.key?(:deltas) &&
|
|
214
|
+
blend[:deltas].is_a?(Array) &&
|
|
215
|
+
blend[:deltas].size == blend_data[:num_axes]
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Get number of operands required for blend
|
|
220
|
+
#
|
|
221
|
+
# @param k [Integer] Number of values to blend
|
|
222
|
+
# @param n [Integer] Number of axes
|
|
223
|
+
# @return [Integer] Total operands required (including K and N)
|
|
224
|
+
def self.operand_count(k, n)
|
|
225
|
+
k * (n + 1) + 2
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Check if enough operands are available
|
|
229
|
+
#
|
|
230
|
+
# @param stack_size [Integer] Current stack size
|
|
231
|
+
# @param k [Integer] Number of values to blend
|
|
232
|
+
# @param n [Integer] Number of axes
|
|
233
|
+
# @return [Boolean] True if enough operands
|
|
234
|
+
def self.sufficient_operands?(stack_size, k, n)
|
|
235
|
+
stack_size >= operand_count(k, n)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|