tco 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "tco"
4
+ require "rmagick"
5
+
6
+ Magick::Image.read("tux.png")[0].each_pixel do |pixel, col, row|
7
+ c = [pixel.red, pixel.green, pixel.blue].map { |v| 255*(v/65535.0) }
8
+ print " ".bg c
9
+ puts if col >= 53
10
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "tco"
4
+
5
+ flag = <<-EOS
6
+ RRR BBBBBBBBBB RR BBBBBBBBBB RRR
7
+ RR BBBBBBBB RR BBBBBBBB RR
8
+ B RR BBBBBB RR BBBBBB RR B
9
+ BBB RR BBBB RR BBBB RR BBB
10
+ BBBBB RR BBB RR BBB RR BBBBB
11
+ RR
12
+ RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
13
+ RR
14
+ BBBBB RR BBB RR BBB RR BBBBB
15
+ BBB RR BBBB RR BBBB RR BBB
16
+ B RR BBBBBB RR BBBBBB RR B
17
+ RR BBBBBBBB RR BBBBBBBB RR
18
+ RRR BBBBBBBBBB RR BBBBBBBBBB RRR
19
+ EOS
20
+
21
+ flag.split("").each do |c|
22
+ case c
23
+ when "R" then print " ".bg "#c9120a"
24
+ when "B" then print " ".bg "#10116d"
25
+ when " " then print " ".bg "#ffffff"
26
+ when "\n" then print "\n"
27
+ end
28
+ end
@@ -0,0 +1,173 @@
1
+ # tco - terminal colouring application and library
2
+ # Copyright (c) 2013, 2014 Radek Pazdera
3
+
4
+ # MIT License
5
+
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ require "tco/version"
25
+ require "tco/config"
26
+ require "tco/colouring"
27
+ require "tco/style"
28
+ require "tco/parser"
29
+
30
+ module Tco
31
+ @config = Config.new ["/etc/tco.conf", "~/.tco.conf"]
32
+ @colouring = Colouring.new @config
33
+
34
+ def self.colour(fg, bg, string)
35
+ decorate string, Style.new(fg, bg)
36
+ end
37
+
38
+ def self.fg(colour, string)
39
+ decorate string, Style.new(colour)
40
+ end
41
+
42
+ def self.bg(colour, string)
43
+ decorate string, Style.new(nil, colour)
44
+ end
45
+
46
+ def self.bright(string)
47
+ decorate string, Style.new(nil, nil, true, false)
48
+ end
49
+
50
+ def self.underline(string)
51
+ decorate string, Style.new(nil, nil, false, true)
52
+ end
53
+
54
+ def self.style(style_name, string)
55
+ @colouring.decorate(string, @colouring.get_style(style_name))
56
+ end
57
+
58
+ def self.get_style(style_name)
59
+ @colouring.get_style style_name
60
+ end
61
+
62
+ def self.parse(string, default_style)
63
+ p = Parser.new default_style
64
+ segments = p.parse string
65
+
66
+ output = ""
67
+ segments.each do |seg|
68
+ style = if seg.params[:base_style]
69
+ @colouring.get_style seg.params[:base_style]
70
+ else
71
+ Style.new
72
+ end
73
+
74
+ style.fg = seg.params[:fg]
75
+ style.bg = seg.params[:bg]
76
+ style.bright = seg.params[:bright]
77
+ style.underline = seg.params[:underline]
78
+
79
+ output << decorate(seg.to_s, style)
80
+ end
81
+ output
82
+ end
83
+
84
+ def self.decorate(string, (fg, bg, bright, underline))
85
+ @colouring.decorate string, [fg, bg, bright, underline]
86
+ end
87
+
88
+ def self.config
89
+ @config
90
+ end
91
+
92
+ def self.reconfigure(config)
93
+ @config = config
94
+ @colouring = Colouring.new config
95
+ end
96
+
97
+ def self.display_palette
98
+ # TODO: Might be worth sorting, so the pallete is easier to navigate
99
+ colours = @colouring.palette.colours
100
+ colours_per_line = (`tput cols`.to_i / 9 - 0.5).ceil
101
+
102
+ c = 0
103
+ while c < colours.length do
104
+ if (c + colours_per_line) < colours.length
105
+ squares = colours_per_line
106
+ else
107
+ squares = colours.length - c
108
+ end
109
+
110
+ # Prepare the squares for the line
111
+ square_styles = []
112
+ squares.times do
113
+ black = Tco::Colour.new([0,0,0])
114
+ white = Tco::Colour.new([255,255,255])
115
+
116
+ font_colour = if (colours[c] - black).abs > (colours[c] - white).abs
117
+ black
118
+ else
119
+ white
120
+ end
121
+
122
+ square_styles.push [c, font_colour, colours[c]]
123
+ c += 1
124
+ end
125
+
126
+ # The first empty line
127
+ square_styles.each { |c, fg, bg| print Tco::colour fg, bg, " "*9 }
128
+ puts
129
+
130
+ # Colour index
131
+ square_styles.each do |c, fg, bg|
132
+ print Tco::colour fg, bg, c.to_s.center(9)
133
+ end
134
+ puts
135
+
136
+ # Colour value
137
+ square_styles.each do |c, fg, bg|
138
+ print Tco::colour fg, bg, bg.to_s.center(9)
139
+ end
140
+ puts
141
+
142
+ # Final empty line
143
+ square_styles.each { |c, fg, bg| print Tco::colour fg, bg, " "*9 }
144
+ puts
145
+ end
146
+ end
147
+ end
148
+
149
+ class String
150
+ def decorate
151
+ Tco::parse self, Tco::Style.new
152
+ end
153
+
154
+ def fg(colour)
155
+ Tco::fg colour, self
156
+ end
157
+
158
+ def bg(colour)
159
+ Tco::bg colour, self
160
+ end
161
+
162
+ def bright
163
+ Tco::bright self
164
+ end
165
+
166
+ def underline
167
+ Tco::underline self
168
+ end
169
+
170
+ def style(style)
171
+ Tco::style style, self
172
+ end
173
+ end
@@ -0,0 +1,227 @@
1
+ # tco - terminal colouring application and library
2
+ # Copyright (c) 2013, 2014 Radek Pazdera
3
+
4
+ # MIT License
5
+
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ require 'tco/palette'
25
+ require 'tco/style'
26
+
27
+ module Tco
28
+ class Colouring
29
+ ANSI_FG_BASE = 30
30
+ ANSI_BG_BASE = 40
31
+
32
+ attr_reader :palette
33
+
34
+ def initialize(configuration)
35
+ @palette = Palette.new configuration.options["palette"]
36
+ @output_type = configuration.options["output"]
37
+
38
+ configuration.colour_values.each do |id, value|
39
+ @palette.set_colour_value(parse_colour_id(id), parse_rgb_value(value))
40
+ end
41
+
42
+ @names = {}
43
+ configuration.names.each do |name, colour_def|
44
+ @names[name] = resolve_colour_def colour_def
45
+ end
46
+
47
+ @styles = {}
48
+ configuration.styles.each do |name, style|
49
+ @styles[name] = Style.new(resolve_colour_def(style[:fg]),
50
+ resolve_colour_def(style[:bg]),
51
+ style[:bright], style[:underline])
52
+ end
53
+ end
54
+
55
+ # Decorate a string according to the style passed. The input string
56
+ # is processed line-by-line (the escape sequences are added to each
57
+ # line). This is due to some problems I've been having with some
58
+ # terminal emulators not handling multi-line coloured sequences well.
59
+ def decorate(string, (fg, bg, bright, underline))
60
+ return string unless STDOUT.isatty || @output_type == :raw
61
+
62
+ fg = to_colour fg
63
+ bg = to_colour bg
64
+
65
+ output = []
66
+ lines = string.lines.map(&:chomp)
67
+ lines.each do |line|
68
+ unless line.length <= 0
69
+ line = case @palette.type
70
+ when "ansi" then colour_ansi line, fg, bg
71
+ when "extended" then colour_extended line, fg, bg
72
+ else raise "Unknown palette '#{@palette.type}'."
73
+ end
74
+
75
+ if bright
76
+ line = e(1) + line
77
+ end
78
+
79
+ if underline
80
+ line = e(4) + line
81
+ end
82
+
83
+ if (bright or underline) and fg == nil and bg == nil
84
+ line << e(0)
85
+ end
86
+ end
87
+
88
+ output.push line
89
+ end
90
+
91
+ output << "" if string =~ /\n$/
92
+ output.join "\n"
93
+ end
94
+
95
+ def get_style(name)
96
+ raise "Style '#{name}' not found." unless @styles.has_key? name
97
+
98
+ @styles[name]
99
+ end
100
+
101
+ def set_output(output_type)
102
+ unless [:term, :raw].include? output_type
103
+ raise "Output '#{output_type}' not supported."
104
+ end
105
+ @output_type = output_type
106
+ end
107
+
108
+ private
109
+ def e(seq)
110
+ if @output_type == :raw
111
+ "\\033[#{seq}m"
112
+ else
113
+ "\033[#{seq}m"
114
+ end
115
+ end
116
+
117
+ def colour_ansi(string, fg=nil, bg=nil)
118
+ unless fg == nil
119
+ colour_id = @palette.match_colour(fg)
120
+ string = e(colour_id + 30) + string
121
+ end
122
+
123
+ unless bg == nil
124
+ colour_id = @palette.match_colour(bg)
125
+ string = e(colour_id + 40) + string
126
+ end
127
+
128
+ unless fg == nil and bg == nil
129
+ string << e(0)
130
+ end
131
+
132
+ string
133
+ end
134
+
135
+ def colour_extended(string, fg=nil, bg=nil)
136
+ unless fg == nil
137
+ colour_id = @palette.match_colour(fg)
138
+ string = e("38;5;#{colour_id}") + string
139
+ end
140
+
141
+ unless bg == nil
142
+ colour_id = @palette.match_colour(bg)
143
+ string = e("48;5;#{colour_id}") + string
144
+ end
145
+
146
+ unless fg == nil and bg == nil
147
+ string << e(0)
148
+ end
149
+
150
+ string
151
+ end
152
+
153
+ def parse_colour_id(id_in_string)
154
+ id = String.new(id_in_string)
155
+ if id[0] == '@'
156
+ id[0] = ''
157
+ return id.to_i
158
+ end
159
+
160
+ raise "Invalid colour id #{id_in_string}."
161
+ end
162
+
163
+ def parse_rgb_value(rgb_value_in_string)
164
+ error_msg = "Invalid RGB value '#{rgb_value_in_string}'."
165
+ rgb_value = String.new rgb_value_in_string
166
+ case
167
+ when rgb_value[0] == '#'
168
+ rgb_value[0] = ''
169
+ when rgb_value[0..1] == '0x'
170
+ rgb_value[0..1] = ''
171
+ else
172
+ raise error_msg
173
+ end
174
+
175
+ raise error_msg unless rgb_value =~ /^[0-9a-fA-F]+$/
176
+
177
+ case rgb_value.length
178
+ when 3
179
+ rgb_value.scan(/./).map { |c| c.to_i 16 }
180
+ when 6
181
+ rgb_value.scan(/../).map { |c| c.to_i 16 }
182
+ else
183
+ raise error_msg
184
+ end
185
+ end
186
+
187
+ def resolve_colour_name(name)
188
+ raise "Name '#{name}' not found." unless @names.has_key? name
189
+
190
+ @names[name]
191
+ end
192
+
193
+ def resolve_colour_def(colour_def)
194
+ return nil if colour_def == "" || colour_def == "default"
195
+ begin
196
+ @palette.get_colour_value parse_colour_id colour_def
197
+ rescue RuntimeError
198
+ begin
199
+ parse_rgb_value colour_def
200
+ rescue RuntimeError
201
+ begin
202
+ colour_def = resolve_colour_name colour_def
203
+ if colour_def.is_a? String
204
+ resolve_colour_def colour_def
205
+ else
206
+ colour_def
207
+ end
208
+ rescue RuntimeError
209
+ raise "Invalid colour definition '#{colour_def}'."
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def to_colour(value)
216
+ rgb = case
217
+ when value.is_a?(String) then resolve_colour_def value
218
+ when value.is_a?(Array) then value
219
+ when value.is_a?(Colour) then value.rgb
220
+ when value == nil then return nil
221
+ else raise "Colour value type '#{value.class}' not supported."
222
+ end
223
+
224
+ Colour.new rgb
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,137 @@
1
+ # tco - terminal colouring application and library
2
+ # Copyright (c) 2013, 2014 Radek Pazdera
3
+
4
+ # MIT License
5
+
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ require 'yaml'
25
+
26
+ module Tco
27
+ class Config
28
+ attr_accessor :options, :colour_values, :names, :styles
29
+
30
+ def initialize(locations=[])
31
+ @options = {
32
+ "palette" => "extended",
33
+ "output" => "term"
34
+ }
35
+ @colour_values = {}
36
+ @names = {
37
+ "black" => "@0",
38
+ "red" => "@1",
39
+ "green" => "@2",
40
+ "yellow" => "@3",
41
+ "blue" => "@4",
42
+ "magenta" => "@5",
43
+ "cyan" => "@6",
44
+ "light-grey" => "@7",
45
+ "grey" => "@8",
46
+ "light-red" => "@9",
47
+ "light-green" => "@10",
48
+ "light-yellow" => "@11",
49
+ "light-blue" => "@12",
50
+ "light-magenta" => "@13",
51
+ "light-cyan" => "@14",
52
+ "white" => "@15"
53
+ }
54
+
55
+ @styles = {}
56
+
57
+ locations.each do |conf_file|
58
+ conf_file = File.expand_path conf_file
59
+ next unless File.exists? conf_file
60
+ load conf_file
61
+ end
62
+ end
63
+
64
+ def load(path)
65
+ conf_file = YAML::load_file path
66
+
67
+ if conf_file.has_key? "options"
68
+ if conf_file["options"].is_a? Hash
69
+ conf_file["options"].each do |id, value|
70
+ @colour_values[id] = value
71
+ end
72
+ else
73
+ raise "The 'colour_values' config option must be a hash."
74
+ end
75
+ end
76
+
77
+ if conf_file.has_key? "colour_values"
78
+ if conf_file["colour_values"].is_a? Hash
79
+ conf_file["colour_values"].each do |id, value|
80
+ @colour_values[id] = value
81
+ end
82
+ else
83
+ raise "The 'colour_values' config option must be a hash."
84
+ end
85
+ end
86
+
87
+ if conf_file.has_key? "names"
88
+ if conf_file["names"].is_a? Hash
89
+ conf_file["names"].each do |name, colour|
90
+ @names[name] = colour
91
+ end
92
+ else
93
+ raise "Invalid format of the 'names' option."
94
+ end
95
+ end
96
+
97
+ if conf_file.has_key? "styles"
98
+ if conf_file["styles"].is_a? Hash
99
+ conf_file["styles"].each do |name, styledef|
100
+ style = {:fg => nil, :bg => nil, :bright => false,
101
+ :underline => false}
102
+
103
+ if styledef.has_key? "fg"
104
+ style[:fg] = styledef["fg"]
105
+ end
106
+
107
+ if styledef.has_key? "bg"
108
+ style[:bg] = styledef["bg"]
109
+ end
110
+
111
+ if styledef.has_key? "bright"
112
+ style[:bright] = parse_bool styledef["bright"]
113
+ end
114
+
115
+ if styledef.has_key? "underline"
116
+ style[:underline] = parse_bool styledef["underline"]
117
+ end
118
+
119
+ @styles[name] = style
120
+ end
121
+ else
122
+ raise "Invalid format of the 'styles' option."
123
+ end
124
+ end
125
+ end
126
+
127
+ private
128
+ def name_exists?(colour_name)
129
+ @names.has_key? colour_name
130
+ end
131
+
132
+ def parse_bool(value)
133
+ return true if value =~ /true/i || value =~ /yes/i || value.to_i >= 1
134
+ return false
135
+ end
136
+ end
137
+ end