public
Description: Copy as RTF - a TextMate bundle to make copying syntax highlighted text into Keynote, etc easy peasy
Homepage: http://macromates.com
Clone URL: git://github.com/drnic/copy-as-rtf-tmbundle.git
Search Repo:
Removing old Lib folder
dstrelau (author)
Fri Apr 25 18:02:42 -0700 2008
commit  d9835f2ef66db57e2e2ae490cc3c7e433808cf42
tree    6765d146b4539c469437ba6feea4c921d6ff2b13
parent  3c80cbfc5587112b92ef25c647a38df07be48dfd
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
@@ -1,258 +1 @@
0
-require 'rexml/document'
0
-
0
-$: << ENV['TM_SUPPORT_PATH'] + '/lib'
0
-require "textmate"
0
-require 'cgi'
0
-
0
-class RtfExporter
0
-
0
- def initialize
0
- @styles={}
0
- @colors = ""
0
- @num_colors=1
0
- end
0
-
0
- def generate_rtf input
0
- generate_stylesheet_from_theme
0
- doc = rtf_document input
0
- CGI::unescapeHTML(doc)
0
- end
0
-
0
- def add_style_recursive scopes, style, styles
0
- current = scopes.shift.strip
0
- styles[current] ||= {}
0
- if scopes.empty?
0
- styles[current][:default] = style
0
- else
0
- add_style_recursive scopes, style, styles[current]
0
- end
0
- end
0
-
0
- def add_style_from_textmate_theme name, settings
0
- style = {}
0
- style_names = name.split ','
0
- style_names.each do |sn|
0
- add_style_recursive sn.split('.'), style, @styles
0
- end
0
-
0
- if fs = settings['fontStyle']
0
- style[:bold] = fs =~ /bold/
0
- style[:italic] if fs =~ /italic/
0
- style[:underline] if fs =~ /underline/
0
- end
0
- if col = settings['foreground']
0
- style[:color] = hex_color_to_rtf col
0
- @colors << style[:color]
0
- style[:color_index] = @num_colors+=1
0
- end
0
- end
0
-
0
- def hex_color_to_rtf hex
0
- hex =~ /#(..)(..)(..)/
0
- r = $1.hex
0
- g = $2.hex
0
- b = $3.hex
0
- return "\\red#{r}\\green#{g}\\blue#{b};"
0
- end
0
-
0
- def generate_stylesheet_from_theme(theme_class = nil)
0
-   theme_class = '' if theme_class == nil
0
-   require "#{ENV['TM_SUPPORT_PATH']}/lib/plist"
0
-
0
-   # Load TM preferences to discover the current theme and font settings
0
-   textmate_pref_file = '~/Library/Preferences/com.macromates.textmate.plist'
0
-   prefs = PropertyList.load(File.open(File.expand_path(textmate_pref_file)))
0
-   theme_uuid = prefs['OakThemeManagerSelectedTheme']
0
-   # Load the active theme. Unfortunately, this requires us to scan through
0
-   # all discoverable theme files...
0
-   unless theme_plist = find_theme(theme_uuid)
0
-     print "Could not locate your theme file!"
0
-     abort
0
-   end
0
-
0
-   theme_comment = theme_plist['comment']
0
-   theme_name = theme_plist['name']
0
-   theme_class.replace(theme_name)
0
-   theme_class.downcase!
0
-   theme_class.gsub!(/[^a-z0-9_-]/, '_')
0
-   theme_class.gsub!(/_+/, '_')
0
-
0
-   @font_name = prefs['OakTextViewNormalFontName'] || 'Monaco'
0
-   @font_size = (prefs['OakTextViewNormalFontSize'] || 11).to_s
0
-   @font_size.sub! /\.\d+$/, ''
0
-   @font_size = @font_size.to_i * 3
0
-   
0
-   @font_name = '"' + @font_name + '"' if @font_name.include?(' ') &&
0
-     !@font_name.include?('"')
0
-
0
-   theme_plist['settings'].each do | setting |
0
-    if (!setting['name'] and setting['settings'])
0
-       body_bg = setting['settings']['background'] || '#ffffff'
0
-       body_fg = setting['settings']['foreground'] || '#000000'
0
-       selection_bg = setting['settings']['selection']
0
-       body_bg = hex_color_to_rtf(body_bg)
0
-       body_fg = hex_color_to_rtf(body_fg)
0
-       selection_bg = hex_color_to_rtf(selection_bg) if selection_bg
0
-       @colors << body_fg
0
-       next
0
-     end
0
-    if setting['name'] && setting['scope']
0
-     scope_name = setting['scope']
0
-     # scope_name.gsub! /(^|[ ])-[^ ]+/, '' # strip negated scopes
0
-     # scope_name.gsub! /\./, '_' # change inner '.' to '_'
0
-     # #scope_name.gsub! /(^|[ ])/, '\1.'
0
-     # scope_name.gsub! /[ ]/, '_' # spaces to underscores
0
-     # scope_name.gsub! /(^|,\s+)/m, '\1'
0
-    add_style_from_textmate_theme scope_name, setting['settings']
0
-    end
0
-   end
0
- end
0
-
0
- def color_table
0
- "{\\colortbl;#{@colors}}"
0
- end
0
-
0
- def font_table
0
- "{\\fonttbl {\\f0 #{@font_name};}}"
0
- end
0
-
0
- def rtf_document input
0
- <<-RTF_DOC
0
-{\\rtf
0
-#{font_table}
0
-#{color_table}
0
-{\\pard\\ql
0
-\\f0\\fs#{@font_size} #{document_to_rtf input}
0
-}}
0
-RTF_DOC
0
- end
0
-
0
- # {\\rtf
0
- # #{font_table}
0
- # #{color_table}
0
- # {\\pard\\ql
0
- # \\f0\\fs#{@font_size}\\cf1\\b Hello, World!\\line
0
- # \\tab \\cf2 Next Line\\line
0
- # \\tab\\tab\\i \\cf3 Another line
0
- # }}
0
-
0
- # Search heuristic is based on the Theme Builder bundle's
0
- # "Create for Current Language" command
0
- def find_theme(uuid)
0
-   theme_dirs = [
0
-     File.expand_path('~/Library/Application Support/TextMate/Themes'),
0
-     '/Library/Application Support/TextMate/Themes',
0
-     TextMate.app_path + '/Contents/SharedSupport/Themes'
0
-   ]
0
-
0
-   theme_dirs.each do |theme_dir|
0
-     if File.exists? theme_dir
0
-       themes = Dir.entries(theme_dir).find_all { |theme| theme =~ /.+\.(tmTheme|plist)$/ }
0
-       themes.each do |theme|
0
-         plist = PropertyList.load(File.open("#{theme_dir}/#{theme}"))
0
-         return plist if plist["uuid"] == uuid
0
-       end
0
-     end
0
-   end
0
-   return nil
0
- end
0
-
0
- def detab(str, width)
0
-   lines = str.split(/\n/)
0
-   lines.each do | line |
0
-     line_sans_markup = line.gsub(/<[^>]*>/, '').gsub(/&[^;]+;/i, '.')
0
-     while (index = line_sans_markup.index("\t"))
0
-       tab = line_sans_markup[0..index].jlength - 1
0
-       padding = " " * ((tab / width + 1) * width - tab)
0
-       line_sans_markup.sub!("\t", padding)
0
-       line.sub!("\t", padding)
0
-     end
0
-   end
0
-   return lines.join("\n")
0
- end
0
-
0
- def document_to_rtf(input, opt = {})
0
-   # Read the source document / selection
0
-   # Convert tabs to spaces using configured tab width
0
-   input = detab(input, (ENV['TM_TAB_SIZE'] || '8').to_i)
0
-   input = input.gsub /\n/,"\\\\line\n"
0
-   
0
-   @style_stack = []
0
-
0
-   # Meat. The poor-man's tokenizer. Fortunately, our input is simple
0
-   # and easy to parse.
0
-   tokens = input.split(/(<[^>]*>)/)
0
-   code_rtf = ''
0
-   tokens.each do |token|
0
-     case token
0
-     when /^<\//
0
-      # closing tag
0
-      pop_style
0
-     when /^<>$/
0
-      # skip empty tags, resulting from name = ''
0
-     when /^</
0
-      if token =~ /^<([^>]+)>$/
0
-      scope = $1
0
-      push_style scope
0
-       end
0
-     else
0
-      next if token.empty?
0
-      code_rtf << '{'
0
-      style = current_style_as_rtf
0
-      if style && !style.empty? && !token.strip.empty?
0
-      code_rtf << current_style_as_rtf << ' '
0
-     end
0
-       code_rtf << token << '}'
0
-     end
0
-   end
0
-   
0
-   return code_rtf
0
- end
0
-
0
- def current_style
0
- @style_stack[0] || {}
0
- end
0
-
0
- def get_style_recursive scopes, styles
0
- return nil unless styles
0
- cur = scopes.shift.strip
0
- style = nil
0
- unless scopes.empty?
0
- style = get_style_recursive(scopes, styles[cur]) || style
0
- end
0
- style = style || styles[:default]
0
- style
0
- end
0
-
0
- def current_style_as_rtf
0
- cur = current_style
0
- rtf = ''
0
- rtf << "\\cf#{cur[:color_index]}" if cur[:color_index]
0
- rtf << "\\b" if cur[:bold]
0
- rtf << "\\i" if cur[:italic]
0
- rtf << "\\ul" if cur[:underline]
0
- rtf
0
- end
0
-
0
- def push_style name
0
- cur = current_style
0
- # p "getting #{name}"
0
- new_style = get_style_recursive(name.split('.'), @styles)
0
- # p "got #{new_style.inspect}"
0
- # p "current: #{cur.inspect}"
0
- new_style = cur.merge new_style if new_style
0
- new_style ||= {}
0
- unless new_style[:color_index]
0
- new_style[:color_index] = 0
0
- end
0
- # p "merged: #{new_style.inspect}"
0
- @style_stack.unshift new_style
0
- new_style
0
- end
0
-
0
- def pop_style
0
- @style_stack.shift
0
- end
0
-
0
-end
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
@@ -1,268 +1 @@
0
-require 'rexml/document'
0
-
0
-$: << ENV['TM_SUPPORT_PATH'] + '/lib'
0
-require "textmate"
0
-require 'cgi'
0
-
0
-class RtfExporter
0
-
0
- def initialize
0
- @styles={}
0
- @colors = ""
0
- @num_colors=1
0
- end
0
-
0
- def generate_rtf input
0
- generate_stylesheet_from_theme
0
- doc = rtf_document input
0
- CGI::unescapeHTML(doc)
0
- end
0
-
0
- def add_style_recursive scopes, style, styles
0
- current = scopes.shift.strip
0
- styles[current] ||= {}
0
- if scopes.empty?
0
- styles[current][:default] = style
0
- else
0
- add_style_recursive scopes, style, styles[current]
0
- end
0
- end
0
-
0
- def add_style_from_textmate_theme name, settings
0
- style = {}
0
- style_names = name.split ','
0
- style_names.each do |sn|
0
- add_style_recursive sn.split('.'), style, @styles
0
- end
0
-
0
- if fs = settings['fontStyle']
0
- style[:bold] = fs =~ /bold/
0
- style[:italic] if fs =~ /italic/
0
- style[:underline] if fs =~ /underline/
0
- end
0
- if col = settings['foreground']
0
- style[:color] = hex_color_to_rtf col
0
- @colors << style[:color]
0
- style[:color_index] = @num_colors+=1
0
- end
0
- end
0
-
0
- def hex_color_to_rtf hex
0
- hex =~ /#(..)(..)(..)/
0
- r = $1.hex
0
- g = $2.hex
0
- b = $3.hex
0
- return "\\red#{r}\\green#{g}\\blue#{b};"
0
- end
0
-
0
- def generate_stylesheet_from_theme(theme_class = nil)
0
-   theme_class = '' if theme_class == nil
0
-   require "#{ENV['TM_SUPPORT_PATH']}/lib/osx/plist"
0
-
0
-   # Load TM preferences to discover the current theme and font settings
0
-   textmate_pref_file = '~/Library/Preferences/com.macromates.textmate.plist'
0
-   prefs = PropertyList.load(File.open(File.expand_path(textmate_pref_file)))
0
-   theme_uuid = prefs['OakThemeManagerSelectedTheme']
0
-   # Load the active theme. Unfortunately, this requires us to scan through
0
-   # all discoverable theme files...
0
-   unless theme_plist = find_theme(theme_uuid)
0
-     print "Could not locate your theme file!"
0
-     abort
0
-   end
0
-
0
-   theme_comment = theme_plist['comment']
0
-   theme_name = theme_plist['name']
0
-   theme_class.replace(theme_name)
0
-   theme_class.downcase!
0
-   theme_class.gsub!(/[^a-z0-9_-]/, '_')
0
-   theme_class.gsub!(/_+/, '_')
0
-
0
-   @font_name = prefs['OakTextViewNormalFontName'] || 'Monaco'
0
-   @font_size = (prefs['OakTextViewNormalFontSize'] || 11).to_s
0
-   @font_size.sub! /\.\d+$/, ''
0
-   @font_size = @font_size.to_i * 3
0
-   
0
-   @font_name = '"' + @font_name + '"' if @font_name.include?(' ') &&
0
-     !@font_name.include?('"')
0
-
0
-   theme_plist['settings'].each do | setting |
0
-    if (!setting['name'] and setting['settings'])
0
-       body_bg = setting['settings']['background'] || '#ffffff'
0
-       body_fg = setting['settings']['foreground'] || '#000000'
0
-       selection_bg = setting['settings']['selection']
0
-       body_bg = hex_color_to_rtf(body_bg)
0
-       body_fg = hex_color_to_rtf(body_fg)
0
-       selection_bg = hex_color_to_rtf(selection_bg) if selection_bg
0
-       @colors << body_fg
0
-       next
0
-     end
0
-    if setting['name'] && setting['scope']
0
-     scope_name = setting['scope']
0
-     # scope_name.gsub! /(^|[ ])-[^ ]+/, '' # strip negated scopes
0
-     # scope_name.gsub! /\./, '_' # change inner '.' to '_'
0
-     # #scope_name.gsub! /(^|[ ])/, '\1.'
0
-     # scope_name.gsub! /[ ]/, '_' # spaces to underscores
0
-     # scope_name.gsub! /(^|,\s+)/m, '\1'
0
-    add_style_from_textmate_theme scope_name, setting['settings']
0
-    end
0
-   end
0
- end
0
-
0
- def color_table
0
- "{\\colortbl;#{@colors}}"
0
- end
0
-
0
- def font_table
0
- "{\\fonttbl {\\f0 #{@font_name};}}"
0
- end
0
-
0
- def rtf_document input
0
- <<-RTF_DOC
0
-{\\rtf
0
-#{font_table}
0
-#{color_table}
0
-{\\pard\\ql
0
-\\f0\\fs#{@font_size} #{document_to_rtf input}
0
-}}
0
-RTF_DOC
0
- end
0
-
0
- # {\\rtf
0
- # #{font_table}
0
- # #{color_table}
0
- # {\\pard\\ql
0
- # \\f0\\fs#{@font_size}\\cf1\\b Hello, World!\\line
0
- # \\tab \\cf2 Next Line\\line
0
- # \\tab\\tab\\i \\cf3 Another line
0
- # }}
0
-
0
- # Search heuristic is based on the Theme Builder bundle's
0
- # "Create for Current Language" command
0
- def find_theme(uuid)
0
-   theme_dirs = [
0
-     File.expand_path('~/Library/Application Support/TextMate/Themes'),
0
-     '/Library/Application Support/TextMate/Themes',
0
-     TextMate.app_path + '/Contents/SharedSupport/Themes'
0
-   ]
0
-
0
-   theme_dirs.each do |theme_dir|
0
-     if File.exists? theme_dir
0
-       themes = Dir.entries(theme_dir).find_all { |theme| theme =~ /.+\.(tmTheme|plist)$/ }
0
-       themes.each do |theme|
0
-         plist = PropertyList.load(File.open("#{theme_dir}/#{theme}"))
0
-         return plist if plist["uuid"] == uuid
0
-       end
0
-     end
0
-   end
0
-   return nil
0
- end
0
-
0
- def detab(str, width)
0
-   lines = str.split(/\n/)
0
-   lines.each do | line |
0
-     line_sans_markup = line.gsub(/<[^>]*>/, '').gsub(/&[^;]+;/i, '.')
0
-     while (index = line_sans_markup.index("\t"))
0
-       tab = line_sans_markup[0..index].jlength - 1
0
-       padding = " " * ((tab / width + 1) * width - tab)
0
-       line_sans_markup.sub!("\t", padding)
0
-       line.sub!("\t", padding)
0
-     end
0
-   end
0
-   return lines.join("\n")
0
- end
0
-
0
- def document_to_rtf(input, opt = {})
0
-   # Read the source document / selection
0
-   # Convert tabs to spaces using configured tab width
0
-   input = detab(input, (ENV['TM_TAB_SIZE'] || '8').to_i)
0
-
0
- input.gsub! /\\/, "__backslash__"
0
-   input.gsub! /\\n/, "__newline__"
0
-   input.gsub! /\n/, "\\\\line\n"
0
-   input.gsub! /\{/, "\\{"
0
-   input.gsub! /\}/, "\\}"
0
-   input.gsub! /__newline__/, "\\\\\\n"
0
-   input.gsub! /__backslash__/, "\\\\\\"
0
-
0
-   
0
-   @style_stack = []
0
-
0
-   # Meat. The poor-man's tokenizer. Fortunately, our input is simple
0
-   # and easy to parse.
0
-   tokens = input.split(/(<[^>]*>)/)
0
-   code_rtf = ''
0
-   tokens.each do |token|
0
-     case token
0
-     when /^<\//
0
-      # closing tag
0
-      pop_style
0
-     when /^<>$/
0
-      # skip empty tags, resulting from name = ''
0
-     when /^</
0
-      if token =~ /^<([^>]+)>$/
0
-      scope = $1
0
-      push_style scope
0
-       end
0
-     else
0
-      next if token.empty?
0
-      code_rtf << '{'
0
-      style = current_style_as_rtf
0
-      if style && !style.empty? && !token.strip.empty?
0
-      code_rtf << current_style_as_rtf << ' '
0
-     end
0
-       code_rtf << token << '}'
0
-     end
0
-   end
0
-   
0
-   return code_rtf
0
- end
0
-
0
- def current_style
0
- @style_stack[0] || {}
0
- end
0
-
0
- def get_style_recursive scopes, styles
0
- #scopes -= ["punctuation", "definition"] # nasty workaround hack
0
-
0
- return nil unless styles
0
- cur = scopes.shift.strip
0
-
0
- style = nil
0
- unless scopes.empty?
0
- style = get_style_recursive(scopes, styles[cur]) || style
0
- end
0
- style ||= styles[:default]
0
- end
0
-
0
- def current_style_as_rtf
0
- cur = current_style
0
- rtf = ''
0
- rtf << "\\cf#{cur[:color_index]}" if cur[:color_index]
0
- rtf << "\\b" if cur[:bold]
0
- rtf << "\\i" if cur[:italic]
0
- rtf << "\\ul" if cur[:underline]
0
- rtf
0
- end
0
-
0
- def push_style name
0
- cur = current_style
0
- # p "getting #{name}"
0
- new_style = get_style_recursive(name.split('.'), @styles)
0
- # p "got #{new_style.inspect}"
0
- # p "current: #{cur.inspect}"
0
- new_style = cur.merge new_style if new_style
0
- new_style ||= cur || {}
0
- unless new_style[:color_index]
0
- new_style[:color_index] = 0
0
- end
0
- # p "merged: #{new_style.inspect}"
0
- @style_stack.unshift new_style
0
- new_style
0
- end
0
-
0
- def pop_style
0
- @style_stack.shift
0
- end
0
-
0
-end
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
@@ -1,268 +1 @@
0
-require 'rexml/document'
0
-
0
-$: << ENV['TM_SUPPORT_PATH'] + '/lib'
0
-require "textmate"
0
-require 'cgi'
0
-
0
-class RtfExporter
0
-
0
- def initialize
0
- @styles={}
0
- @colors = ""
0
- @num_colors=1
0
- end
0
-
0
- def generate_rtf input
0
- generate_stylesheet_from_theme
0
- doc = rtf_document input
0
- CGI::unescapeHTML(doc)
0
- end
0
-
0
- def add_style_recursive scopes, style, styles
0
- current = scopes.shift.strip
0
- styles[current] ||= {}
0
- if scopes.empty?
0
- styles[current][:default] = style
0
- else
0
- add_style_recursive scopes, style, styles[current]
0
- end
0
- end
0
-
0
- def add_style_from_textmate_theme name, settings
0
- style = {}
0
- style_names = name.split ','
0
- style_names.each do |sn|
0
- add_style_recursive sn.split('.'), style, @styles
0
- end
0
-
0
- if fs = settings['fontStyle']
0
- style[:bold] = fs =~ /bold/
0
- style[:italic] if fs =~ /italic/
0
- style[:underline] if fs =~ /underline/
0
- end
0
- if col = settings['foreground']
0
- style[:color] = hex_color_to_rtf col
0
- @colors << style[:color]
0
- style[:color_index] = @num_colors+=1
0
- end
0
- end
0
-
0
- def hex_color_to_rtf hex
0
- hex =~ /#(..)(..)(..)/
0
- r = $1.hex
0
- g = $2.hex
0
- b = $3.hex
0
- return "\\red#{r}\\green#{g}\\blue#{b};"
0
- end
0
-
0
- def generate_stylesheet_from_theme(theme_class = nil)
0
-   theme_class = '' if theme_class == nil
0
-   require "#{ENV['TM_SUPPORT_PATH']}/lib/osx/plist"
0
-
0
-   # Load TM preferences to discover the current theme and font settings
0
-   textmate_pref_file = '~/Library/Preferences/com.macromates.textmate.plist'
0
-   prefs = PropertyList.load(File.open(File.expand_path(textmate_pref_file)))
0
-   theme_uuid = prefs['OakThemeManagerSelectedTheme']
0
-   # Load the active theme. Unfortunately, this requires us to scan through
0
-   # all discoverable theme files...
0
-   unless theme_plist = find_theme(theme_uuid)
0
-     print "Could not locate your theme file!"
0
-     abort
0
-   end
0
-
0
-   theme_comment = theme_plist['comment']
0
-   theme_name = theme_plist['name']
0
-   theme_class.replace(theme_name)
0
-   theme_class.downcase!
0
-   theme_class.gsub!(/[^a-z0-9_-]/, '_')
0
-   theme_class.gsub!(/_+/, '_')
0
-
0
-   @font_name = prefs['OakTextViewNormalFontName'] || 'Monaco'
0
-   @font_size = (prefs['OakTextViewNormalFontSize'] || 11).to_s
0
-   @font_size.sub! /\.\d+$/, ''
0
-   @font_size = @font_size.to_i * 3
0
-   
0
-   @font_name = '"' + @font_name + '"' if @font_name.include?(' ') &&
0
-     !@font_name.include?('"')
0
-
0
-   theme_plist['settings'].each do | setting |
0
-    if (!setting['name'] and setting['settings'])
0
-       body_bg = setting['settings']['background'] || '#ffffff'
0
-       body_fg = setting['settings']['foreground'] || '#000000'
0
-       selection_bg = setting['settings']['selection']
0
-       body_bg = hex_color_to_rtf(body_bg)
0
-       body_fg = hex_color_to_rtf(body_fg)
0
-       selection_bg = hex_color_to_rtf(selection_bg) if selection_bg
0
-       @colors << body_fg
0
-       next
0
-     end
0
-    if setting['name'] && setting['scope']
0
-     scope_name = setting['scope']
0
-     # scope_name.gsub! /(^|[ ])-[^ ]+/, '' # strip negated scopes
0
-     # scope_name.gsub! /\./, '_' # change inner '.' to '_'
0
-     # #scope_name.gsub! /(^|[ ])/, '\1.'
0
-     # scope_name.gsub! /[ ]/, '_' # spaces to underscores
0
-     # scope_name.gsub! /(^|,\s+)/m, '\1'
0
-    add_style_from_textmate_theme scope_name, setting['settings']
0
-    end
0
-   end
0
- end
0
-
0
- def color_table
0
- "{\\colortbl;#{@colors}}"
0
- end
0
-
0
- def font_table
0
- "{\\fonttbl {\\f0 #{@font_name};}}"
0
- end
0
-
0
- def rtf_document input
0
- <<-RTF_DOC
0
-{\\rtf
0
-#{font_table}
0
-#{color_table}
0
-{\\pard\\ql
0
-\\f0\\fs#{@font_size} #{document_to_rtf input}
0
-}}
0
-RTF_DOC
0
- end
0
-
0
- # {\\rtf
0
- # #{font_table}
0
- # #{color_table}
0
- # {\\pard\\ql
0
- # \\f0\\fs#{@font_size}\\cf1\\b Hello, World!\\line
0
- # \\tab \\cf2 Next Line\\line
0
- # \\tab\\tab\\i \\cf3 Another line
0
- # }}
0
-
0
- # Search heuristic is based on the Theme Builder bundle's
0
- # "Create for Current Language" command
0
- def find_theme(uuid)
0
-   theme_dirs = [
0
-     File.expand_path('~/Library/Application Support/TextMate/Themes'),
0
-     '/Library/Application Support/TextMate/Themes',
0
-     TextMate.app_path + '/Contents/SharedSupport/Themes'
0
-   ]
0
-
0
-   theme_dirs.each do |theme_dir|
0
-     if File.exists? theme_dir
0
-       themes = Dir.entries(theme_dir).find_all { |theme| theme =~ /.+\.(tmTheme|plist)$/ }
0
-       themes.each do |theme|
0
-         plist = PropertyList.load(File.open("#{theme_dir}/#{theme}"))
0
-         return plist if plist["uuid"] == uuid
0
-       end
0
-     end
0
-   end
0
-   return nil
0
- end
0
-
0
- def detab(str, width)
0
-   lines = str.split(/\n/)
0
-   lines.each do | line |
0
-     line_sans_markup = line.gsub(/<[^>]*>/, '').gsub(/&[^;]+;/i, '.')
0
-     while (index = line_sans_markup.index("