compass 0.10.0.pre5 → 0.10.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/CHANGELOG.markdown +48 -0
  2. data/VERSION.yml +1 -1
  3. data/examples/css3/extensions/compass-colors/README.markdown +50 -0
  4. data/examples/css3/extensions/compass-colors/VERSION.yml +5 -0
  5. data/examples/css3/extensions/compass-colors/example/config.rb +13 -0
  6. data/examples/css3/extensions/compass-colors/example/src/_split_compliment_theme.sass +28 -0
  7. data/examples/css3/extensions/compass-colors/example/src/screen.sass +40 -0
  8. data/examples/css3/extensions/compass-colors/lib/compass-colors.rb +9 -0
  9. data/examples/css3/extensions/compass-colors/lib/compass-colors/compass_extension.rb +7 -0
  10. data/examples/css3/extensions/compass-colors/lib/compass-colors/hsl.rb +109 -0
  11. data/examples/css3/extensions/compass-colors/lib/compass-colors/sass_extensions.rb +112 -0
  12. data/examples/css3/extensions/compass-colors/spec/approximate_color_matching.rb +43 -0
  13. data/examples/css3/extensions/compass-colors/spec/sass_extensions_spec.rb +54 -0
  14. data/examples/css3/extensions/compass-colors/templates/analogous/_theme.sass +28 -0
  15. data/examples/css3/extensions/compass-colors/templates/analogous/manifest.rb +1 -0
  16. data/examples/css3/extensions/compass-colors/templates/basic/_theme.sass +7 -0
  17. data/examples/css3/extensions/compass-colors/templates/basic/manifest.rb +1 -0
  18. data/examples/css3/extensions/compass-colors/templates/complementary/_theme.sass +20 -0
  19. data/examples/css3/extensions/compass-colors/templates/complementary/manifest.rb +1 -0
  20. data/examples/css3/extensions/compass-colors/templates/split_complement/_theme.sass +28 -0
  21. data/examples/css3/extensions/compass-colors/templates/split_complement/manifest.rb +1 -0
  22. data/examples/css3/extensions/compass-colors/templates/triadic/_theme.sass +28 -0
  23. data/examples/css3/extensions/compass-colors/templates/triadic/manifest.rb +1 -0
  24. data/examples/css3/gradients.html.haml +60 -0
  25. data/examples/css3/index.html.haml +10 -8
  26. data/examples/css3/src/fancy-fonts.sass +1 -1
  27. data/examples/css3/src/gradients.sass +69 -0
  28. data/examples/css3/src/main.sass +38 -10
  29. data/frameworks/blueprint/stylesheets/blueprint/_liquid.sass +12 -10
  30. data/frameworks/blueprint/stylesheets/blueprint/modules/_buttons.sass +1 -1
  31. data/frameworks/blueprint/stylesheets/blueprint/modules/_colors.sass +1 -1
  32. data/frameworks/blueprint/stylesheets/blueprint/modules/_debug.sass +1 -1
  33. data/frameworks/blueprint/stylesheets/blueprint/modules/_fancy_type.sass +1 -1
  34. data/frameworks/blueprint/stylesheets/blueprint/modules/_form.sass +1 -1
  35. data/frameworks/blueprint/stylesheets/blueprint/modules/_grid.sass +1 -1
  36. data/frameworks/blueprint/stylesheets/blueprint/modules/_interaction.sass +1 -1
  37. data/frameworks/blueprint/stylesheets/blueprint/modules/_link_icons.sass +1 -1
  38. data/frameworks/blueprint/stylesheets/blueprint/modules/_liquid.sass +1 -1
  39. data/frameworks/blueprint/stylesheets/blueprint/modules/_reset.sass +1 -1
  40. data/frameworks/blueprint/stylesheets/blueprint/modules/_rtl.sass +1 -1
  41. data/frameworks/blueprint/stylesheets/blueprint/modules/_scaffolding.sass +1 -1
  42. data/frameworks/blueprint/stylesheets/blueprint/modules/_typography.sass +1 -1
  43. data/frameworks/blueprint/stylesheets/blueprint/modules/_utilities.sass +1 -1
  44. data/frameworks/compass/stylesheets/compass/css3/_gradient.sass +54 -36
  45. data/frameworks/compass/stylesheets/compass/layout/_sticky_footer.sass +1 -0
  46. data/frameworks/compass/stylesheets/compass/utilities/general/_hacks.sass +7 -7
  47. data/frameworks/compass/stylesheets/compass/utilities/lists/_horizontal_list.sass +6 -2
  48. data/lib/compass.rb +1 -1
  49. data/lib/compass/app_integration/rails.rb +6 -0
  50. data/lib/compass/app_integration/rails/{action_controller.rb → actionpack2/action_controller.rb} +0 -0
  51. data/lib/compass/app_integration/rails/{sass_plugin.rb → actionpack2/sass_plugin.rb} +0 -0
  52. data/lib/compass/app_integration/rails/{urls.rb → actionpack2/urls.rb} +0 -0
  53. data/lib/compass/app_integration/rails/installer.rb +8 -5
  54. data/lib/compass/app_integration/rails/runtime.rb +8 -4
  55. data/lib/compass/commands/installer_command.rb +1 -3
  56. data/lib/compass/commands/project_base.rb +1 -3
  57. data/lib/compass/commands/write_configuration.rb +5 -2
  58. data/lib/compass/compiler.rb +7 -0
  59. data/lib/compass/configuration/adapters.rb +3 -3
  60. data/lib/compass/configuration/defaults.rb +4 -0
  61. data/lib/compass/configuration/helpers.rb +16 -0
  62. data/lib/compass/configuration/inheritance.rb +6 -2
  63. data/lib/compass/configuration/serialization.rb +8 -3
  64. data/lib/compass/exec.rb +5 -1
  65. data/lib/compass/frameworks.rb +15 -5
  66. data/lib/compass/logger.rb +8 -4
  67. data/lib/compass/sass_extensions/functions.rb +3 -2
  68. data/lib/compass/sass_extensions/functions/gradient_support.rb +186 -0
  69. data/lib/compass/sass_extensions/functions/image_size.rb +97 -0
  70. data/test/compass_test.rb +1 -1
  71. data/test/configuration_test.rb +30 -0
  72. data/test/fixtures/stylesheets/compass/100x150.jpg +0 -0
  73. data/test/fixtures/stylesheets/compass/css/gradients.css +71 -0
  74. data/test/fixtures/stylesheets/compass/css/image_size.css +15 -0
  75. data/test/fixtures/stylesheets/compass/css/layout.css +1 -0
  76. data/test/fixtures/stylesheets/compass/images/100x150.gif +0 -0
  77. data/test/fixtures/stylesheets/compass/images/100x150.jpeg +0 -0
  78. data/test/fixtures/stylesheets/compass/images/100x150.jpg +0 -0
  79. data/test/fixtures/stylesheets/compass/images/100x150.png +0 -0
  80. data/test/fixtures/stylesheets/compass/sass/gradients.sass +48 -0
  81. data/test/fixtures/stylesheets/compass/sass/image_size.sass +12 -0
  82. metadata +70 -15
  83. data/examples/css3/src/gradient.sass +0 -10
  84. data/lib/compass/sass_extensions/functions/color_stop.rb +0 -10
@@ -31,6 +31,7 @@ module Compass
31
31
  if config.is_a?(Compass::Configuration::Data)
32
32
  config
33
33
  elsif config.respond_to?(:read)
34
+ filename ||= config.to_s if config.is_a?(Pathname)
34
35
  Compass::Configuration::Data.new_from_string(config.read, filename)
35
36
  elsif config.is_a?(Hash)
36
37
  Compass::Configuration::Data.new(filename, config)
@@ -88,6 +89,12 @@ module Compass
88
89
  end
89
90
  end
90
91
 
92
+ def discover_extensions!
93
+ if File.exists?(configuration.extensions_path)
94
+ Compass::Frameworks.discover(configuration.extensions_path)
95
+ end
96
+ end
97
+
91
98
  # Returns a full path to the relative path to the project directory
92
99
  def projectize(path, project_path = nil)
93
100
  project_path ||= configuration.project_path
@@ -112,6 +119,15 @@ module Compass
112
119
  possible_files.detect{|f| File.exists?(f)}
113
120
  end
114
121
 
122
+ def handle_configuration_change!
123
+ if (compiler = Compass.compiler).new_config?
124
+ compiler.clean!
125
+ end
126
+ end
127
+
128
+ def compiler
129
+ Compass::Compiler.new(*Compass.configuration.to_compiler_arguments)
130
+ end
115
131
  end
116
132
  end
117
133
 
@@ -145,13 +145,17 @@ module Compass
145
145
  end
146
146
  end
147
147
 
148
- def debug
148
+ def chain
149
149
  instances = [self]
150
150
  instances << instances.last.inherited_data while instances.last.inherited_data
151
+ instances
152
+ end
153
+
154
+ def debug
151
155
  normalized_attrs = {}
152
156
  ATTRIBUTES.each do |prop|
153
157
  values = []
154
- instances.each do |instance|
158
+ chain.each do |instance|
155
159
  values << {
156
160
  :raw => (instance.send("raw_#{prop}") rescue nil),
157
161
  :value => (instance.send("#{prop}_without_default") rescue nil),
@@ -10,7 +10,7 @@ module Compass
10
10
  module ClassMethods
11
11
  def new_from_file(config_file)
12
12
  data = Data.new(config_file)
13
- data.parse(config_file)
13
+ data._parse(config_file)
14
14
  data
15
15
  end
16
16
 
@@ -22,8 +22,12 @@ module Compass
22
22
  end
23
23
 
24
24
  module InstanceMethods
25
- # parses a configuration file which is a ruby script
26
25
  def parse(config_file)
26
+ raise Compass::Error, "Compass.configuration.parse(filename) has been removed. Please call Compass.add_project_configuration(filename) instead."
27
+ end
28
+
29
+ # parses a configuration file which is a ruby script
30
+ def _parse(config_file)
27
31
  unless File.readable?(config_file)
28
32
  raise Compass::Error, "Configuration file, #{config_file}, not found or not readable."
29
33
  end
@@ -37,6 +41,7 @@ module Compass
37
41
  eval(contents, bind, filename)
38
42
  ATTRIBUTES.each do |prop|
39
43
  value = eval(prop.to_s, bind) rescue nil
44
+ value = value.to_s if value.is_a?(Pathname)
40
45
  self.send("#{prop}=", value) unless value.nil?
41
46
  end
42
47
  if @added_import_paths
@@ -73,7 +78,7 @@ module Compass
73
78
  def serialize_property(prop, value)
74
79
  %Q(#{prop} = #{value.inspect}\n)
75
80
  end
76
-
81
+
77
82
  def issue_deprecation_warnings
78
83
  if http_images_path == :relative
79
84
  $stderr.puts "DEPRECATION WARNING: Please set relative_assets = true to enable relative paths."
@@ -6,7 +6,11 @@ require 'compass/actions'
6
6
  require 'compass/installers'
7
7
  require 'compass/commands'
8
8
  require 'rbconfig'
9
- require 'win32console' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
9
+ begin
10
+ require 'win32console' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
11
+ rescue LoadError
12
+ $boring = true
13
+ end
10
14
 
11
15
  module Compass::Exec
12
16
  end
@@ -27,11 +27,20 @@ module Compass
27
27
  end
28
28
  end
29
29
 
30
+ def detect_registration
31
+ @registered = nil
32
+ yield
33
+ @registered
34
+ ensure
35
+ @registered = nil
36
+ end
37
+
30
38
  def register(name, *arguments)
39
+ @registered = Framework.new(name, *arguments)
31
40
  if idx = ALL.index(self[name])
32
- ALL[idx] = Framework.new(name, *arguments)
41
+ ALL[idx] = @registered
33
42
  else
34
- ALL << Framework.new(name, *arguments)
43
+ ALL << @registered
35
44
  end
36
45
  end
37
46
 
@@ -54,9 +63,10 @@ module Compass
54
63
  File.join(directory, File.basename(directory)+".rb")
55
64
  ]
56
65
  loader = loaders.detect{|l| File.exists?(l)}
57
- if loader
58
- require loader
59
- else
66
+ registered_framework = detect_registration do
67
+ require loader if loader
68
+ end
69
+ unless registered_framework
60
70
  register File.basename(directory), directory
61
71
  end
62
72
  end
@@ -2,12 +2,13 @@ module Compass
2
2
 
3
3
  class Logger
4
4
 
5
- DEFAULT_ACTIONS = [:directory, :exists, :remove, :create, :overwrite, :compile, :error, :identical]
5
+ DEFAULT_ACTIONS = [:directory, :exists, :remove, :create, :overwrite, :compile, :error, :identical, :warning]
6
6
 
7
7
  COLORS = { :clear => 0, :red => 31, :green => 32, :yellow => 33 }
8
8
 
9
9
  ACTION_COLORS = {
10
10
  :error => :red,
11
+ :warning => :yellow,
11
12
  :compile => :green,
12
13
  :overwrite => :yellow,
13
14
  :create => :green,
@@ -36,7 +37,6 @@ module Compass
36
37
  end
37
38
 
38
39
  def red
39
- return yield unless Compass.configuration.color_output
40
40
  $stderr.write(color(:red))
41
41
  $stdout.write(color(:red))
42
42
  yield
@@ -46,8 +46,12 @@ module Compass
46
46
  end
47
47
 
48
48
  def color(c)
49
- if c && COLORS.has_key?(c.to_sym)
50
- "\e[#{COLORS[c.to_sym]}m"
49
+ if Compass.configuration.color_output && c && COLORS.has_key?(c.to_sym)
50
+ if defined?($boring) && $boring
51
+ ""
52
+ else
53
+ "\e[#{COLORS[c.to_sym]}m"
54
+ end
51
55
  else
52
56
  ""
53
57
  end
@@ -1,7 +1,7 @@
1
1
  module Compass::SassExtensions::Functions
2
2
  end
3
3
 
4
- %w(selectors enumerate urls display inline_image color_stop font_files).each do |func|
4
+ %w(selectors enumerate urls display inline_image image_size gradient_support font_files).each do |func|
5
5
  require "compass/sass_extensions/functions/#{func}"
6
6
  end
7
7
 
@@ -11,7 +11,8 @@ module Sass::Script::Functions
11
11
  include Compass::SassExtensions::Functions::Urls
12
12
  include Compass::SassExtensions::Functions::Display
13
13
  include Compass::SassExtensions::Functions::InlineImage
14
- include Compass::SassExtensions::Functions::ColorStop
14
+ include Compass::SassExtensions::Functions::ImageSize
15
+ include Compass::SassExtensions::Functions::GradientSupport::Functions
15
16
  include Compass::SassExtensions::Functions::FontFiles
16
17
  end
17
18
 
@@ -0,0 +1,186 @@
1
+ module Compass::SassExtensions::Functions::GradientSupport
2
+
3
+ class List < Sass::Script::Literal
4
+ attr_accessor :values
5
+ def initialize(*values)
6
+ self.values = values
7
+ end
8
+ def inspect
9
+ to_s
10
+ end
11
+ def to_s
12
+ values.map{|v| v.to_s}.join(", ")
13
+ end
14
+ end
15
+
16
+ class ColorStop < Sass::Script::Literal
17
+ attr_accessor :color, :stop
18
+ def initialize(color, stop = nil)
19
+ self.color, self.stop = color, stop
20
+ end
21
+ def inspect
22
+ to_s
23
+ end
24
+ def to_s
25
+ s = "#{color}"
26
+ if stop
27
+ s << " "
28
+ if stop.unitless?
29
+ s << stop.times(Sass::Script::Number.new(100, ["%"])).to_s
30
+ else
31
+ s << stop.to_s
32
+ end
33
+ end
34
+ s
35
+ end
36
+ end
37
+
38
+ module Functions
39
+ # returns the opposite position of a side or corner.
40
+ def grad_opposite_position(position)
41
+ opposite = position.value.split(/ +/).map do |pos|
42
+ case pos
43
+ when "top" then "bottom"
44
+ when "bottom" then "top"
45
+ when "left" then "right"
46
+ when "right" then "left"
47
+ when "center" then "center"
48
+ else
49
+ raise Sass::SyntaxError, "Cannot determine the opposite of #{pos}"
50
+ end
51
+ end
52
+ Sass::Script::String.new(opposite.join(" "))
53
+ end
54
+
55
+ # returns color-stop() calls for use in webkit.
56
+ def grad_color_stops(color_list)
57
+ normalize_stops!(color_list)
58
+ max = color_list.values.last.stop
59
+ color_stops = color_list.values.map do |pos|
60
+ # have to convert absolute units to percentages for use in color stop functions.
61
+ stop = pos.stop
62
+ stop = stop.div(max).times(Sass::Script::Number.new(100,["%"])) if stop.numerator_units == max.numerator_units
63
+ "color-stop(#{stop}, #{pos.color})"
64
+ end
65
+ Sass::Script::String.new(color_stops.join(", "))
66
+ end
67
+
68
+ # returns the end position of the gradient from the color stop
69
+ def grad_end_position(color_list, radial = Sass::Script::Bool.new(false))
70
+ default = Sass::Script::Number.new(100)
71
+ grad_position(color_list, Sass::Script::Number.new(color_list.values.size), default, radial)
72
+ end
73
+
74
+ def grad_position(color_list, index, default, radial = Sass::Script::Bool.new(false))
75
+ stop = color_list.values[index.value - 1].stop
76
+ if stop && radial.to_bool
77
+ orig_stop = stop
78
+ if stop.unitless?
79
+ if stop.value <= 1
80
+ # A unitless number is assumed to be a percentage when it's between 0 and 1
81
+ stop = stop.times(Sass::Script::Number.new(100, ["%"]))
82
+ else
83
+ # Otherwise, a unitless number is assumed to be in pixels
84
+ stop = stop.times(Sass::Script::Number.new(1, ["px"]))
85
+ end
86
+ end
87
+ if stop.numerator_units == ["%"] && color_list.values.last.stop && color_list.values.last.stop.numerator_units == ["px"]
88
+ stop = stop.times(color_list.values.last.stop).div(Sass::Script::Number.new(100, ["%"]))
89
+ end
90
+ Compass::Logger.new.record(:warning, "Webkit only supports pixels for the start and end stops for radial gradients. Got: #{orig_stop}") if stop.numerator_units != ["px"]
91
+ stop.div(Sass::Script::Number.new(1, stop.numerator_units, stop.denominator_units))
92
+ elsif stop
93
+ stop
94
+ else
95
+ default
96
+ end
97
+ end
98
+
99
+ # the given a position, return a point in percents
100
+ def grad_point(position)
101
+ position = position.value
102
+ position = if position[" "]
103
+ if position =~ /(top|bottom|center) (left|right|center)/
104
+ "#{$2} #{$1}"
105
+ else
106
+ position
107
+ end
108
+ else
109
+ case position
110
+ when /top|bottom/
111
+ "left #{position}"
112
+ when /left|right/
113
+ "#{position} top"
114
+ else
115
+ position
116
+ end
117
+ end
118
+ Sass::Script::String.new(position.
119
+ gsub(/top/, "0%").
120
+ gsub(/bottom/, "100%").
121
+ gsub(/left/,"0%").
122
+ gsub(/right/,"100%").
123
+ gsub(/center/, "50%"))
124
+ end
125
+
126
+ def color_stops(*args)
127
+ List.new(*args.map do |arg|
128
+ case arg
129
+ when Sass::Script::Color
130
+ ColorStop.new(arg)
131
+ when Sass::Script::String
132
+ color, stop = arg.value.split(/ +/, 2)
133
+ color = Sass::Script::Parser.parse(color, 0, 0)
134
+ if stop =~ /^(\d+)?(?:\.(\d+))?(%)?$/
135
+ integral, decimal, percent = $1, $2, $3
136
+ number = "#{integral || 0}.#{decimal || 0}".to_f
137
+ number = number / 100 if percent
138
+ if number > 1
139
+ raise Sass::SyntaxError, "A color stop location must be between 0#{"%" if percent} and 1#{"00%" if percent}. Got: #{stop}"
140
+ end
141
+ stop = Sass::Script::Number.new(number)
142
+ elsif !stop.nil?
143
+ number = Sass::Script::Parser.parse(stop, 0, 0)
144
+ unless number.is_a?(Sass::Script::Number)
145
+ raise Sass::SyntaxError, "A color stop location must be a number. Got: #{stop}"
146
+ end
147
+ stop = number
148
+ end
149
+ ColorStop.new(color, stop)
150
+ else
151
+ raise Sass::SyntaxError, "Not a valid color stop: #{arg}"
152
+ end
153
+ end)
154
+ end
155
+ private
156
+ def normalize_stops!(color_list)
157
+ positions = color_list.values
158
+ # fill in the start and end positions, if unspecified
159
+ positions.first.stop = Sass::Script::Number.new(0) unless positions.first.stop
160
+ positions.last.stop = Sass::Script::Number.new(100, ["%"]) unless positions.last.stop
161
+ # fill in empty values
162
+ for i in 0...positions.size
163
+ if positions[i].stop.nil?
164
+ num = 2.0
165
+ for j in (i+1)...positions.size
166
+ if positions[j].stop
167
+ positions[i].stop = positions[i-1].stop.plus((positions[j].stop.minus(positions[i-1].stop)).div(Sass::Script::Number.new(num)))
168
+ break
169
+ else
170
+ num += 1
171
+ end
172
+ end
173
+ end
174
+ end
175
+ # normalize unitless numbers
176
+ positions.each do |pos|
177
+ if pos.stop.unitless? && pos.stop.value <= 1
178
+ pos.stop = pos.stop.times(Sass::Script::Number.new(100, ["%"]))
179
+ elsif pos.stop.unitless?
180
+ pos.stop = pos.stop.times(Sass::Script::Number.new(1, ["px"]))
181
+ end
182
+ end
183
+ nil
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,97 @@
1
+ module Compass::SassExtensions::Functions::ImageSize
2
+ # Returns the width of the image relative to the images directory
3
+ def image_width(image_file)
4
+ image_path = real_path(image_file)
5
+ width = ImageProperties.new(image_path).size.first
6
+ Sass::Script::Number.new(width,["px"])
7
+ end
8
+
9
+ # Returns the height of the image relative to the images directory
10
+ def image_height(image_file)
11
+ image_path = real_path(image_file)
12
+ height = ImageProperties.new(image_path).size.last
13
+ Sass::Script::Number.new(height, ["px"])
14
+ end
15
+
16
+ private
17
+ def real_path(image_file)
18
+ path = image_file.value
19
+ # Compute the real path to the image on the file stystem if the images_dir is set.
20
+ if Compass.configuration.images_path
21
+ File.join(Compass.configuration.images_path, path)
22
+ else
23
+ File.join(Compass.configuration.project_path, path)
24
+ end
25
+ end
26
+
27
+ class ImageProperties
28
+ def initialize(file)
29
+ @file = file
30
+ @file_type = File.extname(@file)[1..-1]
31
+ end
32
+
33
+ def size
34
+ @dimensions ||= send(:"get_size_for_#{@file_type}")
35
+ rescue NoMethodError
36
+ raise Sass::SyntaxError, "Unrecognized file type: #{@file_type}"
37
+ end
38
+
39
+ private
40
+ def get_size_for_png
41
+ IO.read(@file)[0x10..0x18].unpack('NN')
42
+ end
43
+
44
+ def get_size_for_gif
45
+ size = IO.read(@file)[6..10].unpack('SS')
46
+ end
47
+
48
+ def get_size_for_jpg
49
+ get_size_for_jpeg
50
+ end
51
+
52
+ def get_size_for_jpeg
53
+ jpeg = JPEG.new(@file)
54
+ [jpeg.width, jpeg.height]
55
+ end
56
+ end
57
+
58
+ class JPEG
59
+ attr_reader :width, :height, :bits
60
+
61
+ def initialize(file)
62
+ if file.kind_of? IO
63
+ examine(file)
64
+ else
65
+ File.open(file, 'rb') { |io| examine(io) }
66
+ end
67
+ end
68
+
69
+ private
70
+ def examine(io)
71
+ raise 'malformed JPEG' unless io.getc == 0xFF && io.getc == 0xD8 # SOI
72
+
73
+ class << io
74
+ def readint; (readchar << 8) + readchar; end
75
+ def readframe; read(readint - 2); end
76
+ def readsof; [readint, readchar, readint, readint, readchar]; end
77
+ def next
78
+ c = readchar while c != 0xFF
79
+ c = readchar while c == 0xFF
80
+ c
81
+ end
82
+ end
83
+
84
+ while marker = io.next
85
+ case marker
86
+ when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF # SOF markers
87
+ length, @bits, @height, @width, components = io.readsof
88
+ raise 'malformed JPEG' unless length == 8 + components * 3
89
+ when 0xD9, 0xDA then break # EOI, SOS
90
+ when 0xFE then @comment = io.readframe # COM
91
+ when 0xE1 then io.readframe # APP1, contains EXIF tag
92
+ else io.readframe # ignore frame
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end