coloration 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,314 @@
1
+ #--
2
+ # Colour management with Ruby.
3
+ #
4
+ # Copyright 2005 Austin Ziegler
5
+ # http://rubyforge.org/ruby-pdf/
6
+ #
7
+ # Licensed under a MIT-style licence.
8
+ #
9
+ # $Id: rgb.rb,v 1.6 2005/08/08 02:44:17 austin Exp $
10
+ #++
11
+
12
+ # An RGB colour object.
13
+ class Color::RGB
14
+ # The format of a DeviceRGB colour for PDF. In color-tools 2.0 this will
15
+ # be removed from this package and added back as a modification by the
16
+ # PDF::Writer package.
17
+ PDF_FORMAT_STR = "%.3f %.3f %.3f %s"
18
+
19
+ class << self
20
+ # Creates an RGB colour object from percentages 0..100.
21
+ #
22
+ # Color::RGB.from_percentage(10, 20 30)
23
+ def from_percentage(r = 0, g = 0, b = 0)
24
+ from_fraction(r / 100.0, g / 100.0, b / 100.0)
25
+ end
26
+
27
+ # Creates an RGB colour object from fractional values 0..1.
28
+ #
29
+ # Color::RGB.from_fraction(.3, .2, .1)
30
+ def from_fraction(r = 0.0, g = 0.0, b = 0.0)
31
+ colour = Color::RGB.new
32
+ colour.r = r
33
+ colour.g = g
34
+ colour.b = b
35
+ colour
36
+ end
37
+
38
+ # Creates an RGB colour object from an HTML colour descriptor (e.g.,
39
+ # <tt>"fed"</tt> or <tt>"#cabbed;"</tt>.
40
+ #
41
+ # Color::RGB.from_html("fed")
42
+ # Color::RGB.from_html("#fed")
43
+ # Color::RGB.from_html("#cabbed")
44
+ # Color::RGB.from_html("cabbed")
45
+ def from_html(html_colour)
46
+ html_colour = html_colour.gsub(%r{[#;]}, '')
47
+
48
+ case html_colour.size
49
+ when 0
50
+ return
51
+ when 3
52
+ colours = html_colour.scan(%r{[0-9A-Fa-f]}).map { |el| (el * 2).to_i(16) }
53
+ when 6
54
+ colours = html_colour.scan(%r<[0-9A-Fa-f]{2}>).map { |el| el.to_i(16) }
55
+ else
56
+ raise ArgumentError
57
+ end
58
+
59
+ Color::RGB.new(*colours)
60
+ end
61
+ end
62
+
63
+ # Compares the other colour to this one. The other colour will be
64
+ # converted to RGB before comparison, so the comparison between a RGB
65
+ # colour and a non-RGB colour will be approximate and based on the other
66
+ # colour's default #to_rgb conversion. If there is no #to_rgb
67
+ # conversion, this will raise an exception. This will report that two
68
+ # RGB colours are equivalent if all component values are within 1e-4
69
+ # (0.0001) of each other.
70
+ def ==(other)
71
+ other = other.to_rgb
72
+ other.kind_of?(Color::RGB) and
73
+ ((@r - other.r).abs <= 1e-4) and
74
+ ((@g - other.g).abs <= 1e-4) and
75
+ ((@b - other.b).abs <= 1e-4)
76
+ end
77
+
78
+ # Creates an RGB colour object from the standard range 0..255.
79
+ #
80
+ # Color::RGB.new(32, 64, 128)
81
+ # Color::RGB.new(0x20, 0x40, 0x80)
82
+ def initialize(r = 0, g = 0, b = 0)
83
+ @r = r / 255.0
84
+ @g = g / 255.0
85
+ @b = b / 255.0
86
+ end
87
+
88
+ # Present the colour as a DeviceRGB fill colour string for PDF. This
89
+ # will be removed from the default package in color-tools 2.0.
90
+ def pdf_fill
91
+ PDF_FORMAT_STR % [ @r, @g, @b, "rg" ]
92
+ end
93
+
94
+ # Present the colour as a DeviceRGB stroke colour string for PDF. This
95
+ # will be removed from the default package in color-tools 2.0.
96
+ def pdf_stroke
97
+ PDF_FORMAT_STR % [ @r, @g, @b, "RG" ]
98
+ end
99
+
100
+ # Present the colour as an HTML/CSS colour string.
101
+ def html
102
+ r = (@r * 255).round
103
+ r = 255 if r > 255
104
+
105
+ g = (@g * 255).round
106
+ g = 255 if g > 255
107
+
108
+ b = (@b * 255).round
109
+ b = 255 if b > 255
110
+
111
+ "#%02x%02x%02x" % [ r, g, b ]
112
+ end
113
+
114
+ # Converts the RGB colour to CMYK. Most colour experts strongly suggest
115
+ # that this is not a good idea (some even suggesting that it's a very
116
+ # bad idea). CMYK represents additive percentages of inks on white
117
+ # paper, whereas RGB represents mixed colour intensities on a black
118
+ # screen.
119
+ #
120
+ # However, the colour conversion can be done. The basic method is
121
+ # multi-step:
122
+ #
123
+ # 1. Convert the R, G, and B components to C, M, and Y components.
124
+ # c = 1.0 � r
125
+ # m = 1.0 � g
126
+ # y = 1.0 � b
127
+ # 2. Compute the minimum amount of black (K) required to smooth the
128
+ # colour in inks.
129
+ # k = min(c, m, y)
130
+ # 3. Perform undercolour removal on the C, M, and Y components of the
131
+ # colours because less of each colour is needed for each bit of
132
+ # black. Also, regenerate the black (K) based on the undercolour
133
+ # removal so that the colour is more accurately represented in ink.
134
+ # c = min(1.0, max(0.0, c � UCR(k)))
135
+ # m = min(1.0, max(0.0, m � UCR(k)))
136
+ # y = min(1.0, max(0.0, y � UCR(k)))
137
+ # k = min(1.0, max(0.0, BG(k)))
138
+ #
139
+ # The undercolour removal function and the black generation functions
140
+ # return a value based on the brightness of the RGB colour.
141
+ def to_cmyk
142
+ c = 1.0 - @r.to_f
143
+ m = 1.0 - @g.to_f
144
+ y = 1.0 - @b.to_f
145
+
146
+ k = [c, m, y].min
147
+ k = k - (k * brightness)
148
+
149
+ c = [1.0, [0.0, c - k].max].min
150
+ m = [1.0, [0.0, m - k].max].min
151
+ y = [1.0, [0.0, y - k].max].min
152
+ k = [1.0, [0.0, k].max].min
153
+
154
+ Color::CMYK.from_fraction(c, m, y, k)
155
+ end
156
+
157
+ def to_rgb(ignored = nil)
158
+ self
159
+ end
160
+
161
+ # Returns the YIQ (NTSC) colour encoding of the RGB value.
162
+ def to_yiq
163
+ y = (@r * 0.299) + (@g * 0.587) + (@b * 0.114)
164
+ i = (@r * 0.596) + (@g * -0.275) + (@b * -0.321)
165
+ q = (@r * 0.212) + (@g * -0.523) + (@b * 0.311)
166
+ Color::YIQ.from_fraction(y, i, q)
167
+ end
168
+
169
+ # Returns the HSL colour encoding of the RGB value.
170
+ def to_hsl
171
+ min = [ @r, @g, @b ].min
172
+ max = [ @r, @g, @b ].max
173
+ delta = (max - min).to_f
174
+
175
+ lum = (max + min) / 2.0
176
+
177
+ if delta <= 1e-5 # close to 0.0, so it's a grey
178
+ hue = 0
179
+ sat = 0
180
+ else
181
+ if (lum - 0.5) <= 1e-5
182
+ sat = delta / (max + min).to_f
183
+ else
184
+ sat = delta / (2 - max - min).to_f
185
+ end
186
+
187
+ if @r == max
188
+ hue = (@g - @b) / delta.to_f
189
+ elsif @g == max
190
+ hue = (2.0 + @b - @r) / delta.to_f
191
+ elsif (@b - max) <= 1e-5
192
+ hue = (4.0 + @r - @g) / delta.to_f
193
+ end
194
+ hue /= 6.0
195
+
196
+ hue += 1 if hue < 0
197
+ hue -= 1 if hue > 1
198
+ end
199
+ Color::HSL.from_fraction(hue, sat, lum)
200
+ end
201
+
202
+ # Mix the RGB hue with White so that the RGB hue is the specified
203
+ # percentage of the resulting colour. Strictly speaking, this isn't a
204
+ # darken_by operation.
205
+ def lighten_by(percent)
206
+ mix_with(White, percent)
207
+ end
208
+
209
+ # Mix the RGB hue with Black so that the RGB hue is the specified
210
+ # percentage of the resulting colour. Strictly speaking, this isn't a
211
+ # darken_by operation.
212
+ def darken_by(percent)
213
+ mix_with(Black, percent)
214
+ end
215
+
216
+ # Mix the mask colour (which must be an RGB object) with the current
217
+ # colour at the stated opacity percentage (0..100).
218
+ def mix_with(mask, opacity)
219
+ opacity /= 100.0
220
+ rgb = self.dup
221
+
222
+ rgb.r = (@r * opacity) + (mask.r * (1 - opacity))
223
+ rgb.g = (@g * opacity) + (mask.g * (1 - opacity))
224
+ rgb.b = (@b * opacity) + (mask.b * (1 - opacity))
225
+
226
+ rgb
227
+ end
228
+
229
+ # Returns the brightness value for a colour, a number between 0..1.
230
+ # Based on the Y value of YIQ encoding, representing luminosity, or
231
+ # perceived brightness.
232
+ #
233
+ # This may be modified in a future version of color-tools to use the
234
+ # luminosity value of HSL.
235
+ def brightness
236
+ to_yiq.y
237
+ end
238
+ def to_grayscale
239
+ Color::GrayScale.from_fraction(to_hsl.l)
240
+ end
241
+
242
+ alias to_greyscale to_grayscale
243
+
244
+ # Returns a new colour with the brightness adjusted by the specified
245
+ # percentage. Negative percentages will darken the colour; positive
246
+ # percentages will brighten the colour.
247
+ #
248
+ # Color::RGB::DarkBlue.adjust_brightness(10)
249
+ # Color::RGB::DarkBlue.adjust_brightness(-10)
250
+ def adjust_brightness(percent)
251
+ percent /= 100.0
252
+ percent += 1.0
253
+ percent = [ percent, 2.0 ].min
254
+ percent = [ 0.0, percent ].max
255
+
256
+ hsl = to_hsl
257
+ hsl.l *= percent
258
+ hsl.to_rgb
259
+ end
260
+
261
+ # Returns a new colour with the saturation adjusted by the specified
262
+ # percentage. Negative percentages will reduce the saturation; positive
263
+ # percentages will increase the saturation.
264
+ #
265
+ # Color::RGB::DarkBlue.adjust_saturation(10)
266
+ # Color::RGB::DarkBlue.adjust_saturation(-10)
267
+ def adjust_saturation(percent)
268
+ percent /= 100.0
269
+ percent += 1.0
270
+ percent = [ percent, 2.0 ].min
271
+ percent = [ 0.0, percent ].max
272
+
273
+ hsl = to_hsl
274
+ hsl.s *= percent
275
+ hsl.to_rgb
276
+ end
277
+
278
+ # Returns a new colour with the hue adjusted by the specified
279
+ # percentage. Negative percentages will reduce the hue; positive
280
+ # percentages will increase the hue.
281
+ #
282
+ # Color::RGB::DarkBlue.adjust_hue(10)
283
+ # Color::RGB::DarkBlue.adjust_hue(-10)
284
+ def adjust_hue(percent)
285
+ percent /= 100.0
286
+ percent += 1.0
287
+ percent = [ percent, 2.0 ].min
288
+ percent = [ 0.0, percent ].max
289
+
290
+ hsl = to_hsl
291
+ hsl.h *= percent
292
+ hsl.to_rgb
293
+ end
294
+
295
+ attr_accessor :r, :g, :b
296
+ remove_method :r=, :g=, :b= ;
297
+ def r=(rr) #:nodoc:
298
+ rr = 1.0 if rr > 1
299
+ rr = 0.0 if rr < 0
300
+ @r = rr
301
+ end
302
+ def g=(gg) #:nodoc:
303
+ gg = 1.0 if gg > 1
304
+ gg = 0.0 if gg < 0
305
+ @g = gg
306
+ end
307
+ def b=(bb) #:nodoc:
308
+ bb = 1.0 if bb > 1
309
+ bb = 0.0 if bb < 0
310
+ @b = bb
311
+ end
312
+ end
313
+
314
+ require 'coloration/color/rgb-colors'
@@ -0,0 +1,28 @@
1
+ #--
2
+ # Colour management with Ruby.
3
+ #
4
+ # Copyright 2005 Austin Ziegler
5
+ # http://rubyforge.org/ruby-pdf/
6
+ #
7
+ # Licensed under a MIT-style licence.
8
+ #
9
+ # $Id: metallic.rb,v 1.1 2005/08/05 23:07:20 austin Exp $
10
+ #++
11
+
12
+ # This namespace contains some RGB metallic colours suggested by Jim Freeze.
13
+ module Color::RGB::Metallic
14
+ Aluminum = Color::RGB.new(0x99, 0x99, 0x99)
15
+ CoolCopper = Color::RGB.new(0xd9, 0x87, 0x19)
16
+ Copper = Color::RGB.new(0xb8, 0x73, 0x33)
17
+ Iron = Color::RGB.new(0x4c, 0x4c, 0x4c)
18
+ Lead = Color::RGB.new(0x19, 0x19, 0x19)
19
+ Magnesium = Color::RGB.new(0xb3, 0xb3, 0xb3)
20
+ Mercury = Color::RGB.new(0xe6, 0xe6, 0xe6)
21
+ Nickel = Color::RGB.new(0x80, 0x80, 0x80)
22
+ PolySilicon = Color::RGB.new(0x60, 0x00, 0x00)
23
+ Poly = PolySilicon
24
+ Silver = Color::RGB.new(0xcc, 0xcc, 0xcc)
25
+ Steel = Color::RGB.new(0x66, 0x66, 0x66)
26
+ Tin = Color::RGB.new(0x7f, 0x7f, 0x7f)
27
+ Tungsten = Color::RGB.new(0x33, 0x33, 0x33)
28
+ end
@@ -0,0 +1,78 @@
1
+ #--
2
+ # Colour management with Ruby.
3
+ #
4
+ # Copyright 2005 Austin Ziegler
5
+ # http://rubyforge.org/ruby-pdf/
6
+ #
7
+ # Licensed under a MIT-style licence.
8
+ #
9
+ # $Id: yiq.rb,v 1.3 2005/08/08 02:44:17 austin Exp $
10
+ #++
11
+
12
+ # A colour object representing YIQ (NTSC) colour encoding.
13
+ class Color::YIQ
14
+ # Creates a YIQ colour object from fractional values 0 .. 1.
15
+ #
16
+ # Color::YIQ.new(0.3, 0.2, 0.1)
17
+ def self.from_fraction(y = 0, i = 0, q = 0)
18
+ color = Color::YIQ.new
19
+ color.y = y
20
+ color.i = i
21
+ color.q = q
22
+ color
23
+ end
24
+
25
+ # Creates a YIQ colour object from percentages 0 .. 100.
26
+ #
27
+ # Color::YIQ.new(10, 20, 30)
28
+ def initialize(y = 0, i = 0, q = 0)
29
+ @y = y / 100.0
30
+ @i = i / 100.0
31
+ @q = q / 100.0
32
+ end
33
+
34
+ # Compares the other colour to this one. The other colour will be
35
+ # converted to YIQ before comparison, so the comparison between a YIQ
36
+ # colour and a non-YIQ colour will be approximate and based on the other
37
+ # colour's #to_yiq conversion. If there is no #to_yiq conversion, this
38
+ # will raise an exception. This will report that two YIQ values are
39
+ # equivalent if all component colours are within 1e-4 (0.0001) of each
40
+ # other.
41
+ def ==(other)
42
+ other = other.to_yiq
43
+ other.kind_of?(Color::YIQ) and
44
+ ((@y - other.y).abs <= 1e-4) and
45
+ ((@i - other.i).abs <= 1e-4) and
46
+ ((@q - other.q).abs <= 1e-4)
47
+ end
48
+
49
+ def to_yiq
50
+ self
51
+ end
52
+
53
+ def brightness
54
+ @y
55
+ end
56
+ def to_grayscale
57
+ Color::GrayScale.new(@y)
58
+ end
59
+ alias to_greyscale to_grayscale
60
+
61
+ attr_accessor :y, :i, :q
62
+ remove_method :y=, :i=, :q=
63
+ def y=(yy) #:nodoc:
64
+ yy = 1.0 if yy > 1
65
+ yy = 0.0 if yy < 0
66
+ @y = yy
67
+ end
68
+ def i=(ii) #:nodoc:
69
+ ii = 1.0 if ii > 1
70
+ ii = 0.0 if ii < 0
71
+ @i = ii
72
+ end
73
+ def q=(qq) #:nodoc:
74
+ qq = 1.0 if qq > 1
75
+ qq = 0.0 if qq < 0
76
+ @q = qq
77
+ end
78
+ end
@@ -1,4 +1,6 @@
1
1
  class Color::RGBA < Color::RGB
2
+
3
+ # @param col [String] e.g. '#1a1a1a'
2
4
  def self.from_html(col, bg)
3
5
  if col.size > 7 # we have color with alpha channel
4
6
  alpha = (100 * ((col[-2..-1]).to_i(16) / 255.0)).to_i
@@ -8,4 +10,5 @@ class Color::RGBA < Color::RGB
8
10
  super(col)
9
11
  end
10
12
  end
13
+
11
14
  end
@@ -0,0 +1,86 @@
1
+ module Coloration
2
+
3
+ module Converters
4
+
5
+ class AbstractConverter
6
+
7
+ attr_accessor :name, :ui, :items, :input, :result
8
+
9
+ class << self
10
+ attr_reader :in_theme_type
11
+ end
12
+
13
+ def self.process_cmd_line
14
+ new(ARGV).process
15
+ end
16
+
17
+ def initialize(argv = ARGV)
18
+ @argv = argv
19
+ end
20
+
21
+ def process
22
+ return usage_message unless argv.any?
23
+
24
+ read
25
+
26
+ parse_input
27
+
28
+ build_result
29
+
30
+ write
31
+
32
+ rescue Coloration::Readers::TextMateThemeReader::InvalidThemeError
33
+ invalid_theme_message
34
+
35
+ end
36
+
37
+ protected
38
+
39
+ # @!attribute [r] argv
40
+ # @return [Array<String>]
41
+ attr_reader :argv
42
+
43
+ # @!attribute [r] converter
44
+ # @return [Class]
45
+ attr_reader :converter
46
+
47
+ # @return [String]
48
+ def comment_message
49
+ "Converted from #{in_theme_type} theme #{name} using Coloration " \
50
+ "v#{VERSION} (http://github.com/sickill/coloration)"
51
+ end
52
+
53
+ private
54
+
55
+ def in_theme_filename
56
+ argv[0]
57
+ end
58
+
59
+ def invalid_theme_message
60
+ STDERR.puts "Err: given file doesn't look like xml plist file " \
61
+ "containing Textmate theme"
62
+ end
63
+
64
+ def out_theme_filename
65
+ argv[1] || in_theme_filename.
66
+ gsub(/\.#{in_theme_ext}$/, ".#{out_theme_ext}")
67
+ end
68
+
69
+ def read
70
+ @input = File.read(in_theme_filename)
71
+ end
72
+
73
+ def usage_message
74
+ STDOUT.puts "#{File.basename($0)} <in #{in_theme_type} theme> [out " \
75
+ "#{out_theme_type} theme]"
76
+ end
77
+
78
+ def write
79
+ @output = File.open(out_theme_filename, 'w') { |f| f.write(result) }
80
+ end
81
+
82
+ end # AbstractConverter
83
+
84
+ end # Converters
85
+
86
+ end # Coloration