compass 0.10.0.pre5 → 0.10.0.pre6

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 (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