tco 0.0.1 → 0.1.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.
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