vcs_ruby 1.0.1 → 1.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.
- data/bin/vcs.rb +4 -4
- data/lib/FFmpeg/ffmpeg.rb +141 -0
- data/lib/FFmpeg/ffmpeg_audio_stream.rb +38 -0
- data/lib/FFmpeg/ffmpeg_meta_info.rb +37 -0
- data/lib/FFmpeg/ffmpeg_video_stream.rb +52 -0
- data/lib/{mplayer.rb → MPlayer/mplayer.rb} +48 -63
- data/lib/MPlayer/mplayer_audio_stream.rb +35 -0
- data/lib/MPlayer/mplayer_meta_info.rb +40 -0
- data/lib/MPlayer/mplayer_video_stream.rb +44 -0
- data/lib/capturer.rb +17 -21
- data/lib/command.rb +4 -2
- data/lib/configuration.rb +18 -7
- data/lib/contact_sheet.rb +106 -127
- data/lib/defaults.yml +4 -4
- data/lib/font.rb +12 -12
- data/lib/{thumbnail.rb → frame.rb} +45 -24
- data/lib/libAV/libav.rb +143 -0
- data/lib/libAV/libav_audio_stream.rb +38 -0
- data/lib/libAV/libav_meta_info.rb +37 -0
- data/lib/libAV/libav_video_stream.rb +52 -0
- data/lib/stream.rb +24 -0
- data/lib/tools.rb +13 -20
- data/lib/vcs.rb +16 -4
- data/lib/version.info +1 -1
- data/lib/version.rb +2 -2
- data/lib/video.rb +89 -0
- metadata +37 -8
- data/lib/ffmpeg.rb +0 -183
- data/lib/libav.rb +0 -181
data/lib/configuration.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
|
5
5
|
require 'font'
|
6
|
-
|
6
|
+
require 'singleton'
|
7
7
|
|
8
8
|
class ::Hash
|
9
9
|
def deep_merge(second)
|
@@ -14,22 +14,21 @@ end
|
|
14
14
|
|
15
15
|
module VCSRuby
|
16
16
|
class Configuration
|
17
|
-
|
17
|
+
include Singleton
|
18
|
+
|
18
19
|
attr_reader :header_font, :title_font, :timestamp_font, :signature_font
|
20
|
+
attr_writer :verbose, :quiet, :capturer
|
19
21
|
|
20
|
-
def initialize
|
22
|
+
def initialize
|
21
23
|
default_config_file = File.expand_path("defaults.yml", File.dirname(__FILE__))
|
22
24
|
@config = ::YAML::load_file(default_config_file)
|
23
25
|
|
24
26
|
local_config_files = ['~/.vcs.rb.yml']
|
25
27
|
local_config_files.select{ |f| File.exists?(f) }.each do |local_config_file|
|
26
|
-
puts "Local configuration file loaded: #{local_config_file}" if Tools.verbose?
|
27
28
|
local_config = YAML::load_file(local_config_file)
|
28
29
|
@config = @config.deep_merge(local_config)
|
29
30
|
end
|
30
31
|
|
31
|
-
load_profile profile if profile
|
32
|
-
|
33
32
|
@header_font = Font.new @config['style']['header']['font'], @config['style']['header']['size']
|
34
33
|
@title_font = Font.new @config['style']['title']['font'], @config['style']['title']['size']
|
35
34
|
@timestamp_font = Font.new @config['style']['timestamp']['font'], @config['style']['timestamp']['size']
|
@@ -42,7 +41,7 @@ module VCSRuby
|
|
42
41
|
found = false
|
43
42
|
profiles.each do |profile|
|
44
43
|
if File.exists?(profile)
|
45
|
-
puts "Profile loaded: #{profile}" if
|
44
|
+
puts "Profile loaded: #{profile}" if verbose?
|
46
45
|
config = YAML::load_file(profile)
|
47
46
|
@config = @config.deep_merge(config)
|
48
47
|
found = true
|
@@ -52,6 +51,18 @@ module VCSRuby
|
|
52
51
|
raise "No profile '#{profile}' found" unless found
|
53
52
|
end
|
54
53
|
|
54
|
+
def verbose?
|
55
|
+
@verbose
|
56
|
+
end
|
57
|
+
|
58
|
+
def quiet?
|
59
|
+
@quiet
|
60
|
+
end
|
61
|
+
|
62
|
+
def capturer
|
63
|
+
@capturer || :any
|
64
|
+
end
|
65
|
+
|
55
66
|
def rows
|
56
67
|
@config['main']['rows'] ? @config['main']['rows'].to_i : nil
|
57
68
|
end
|
data/lib/contact_sheet.rb
CHANGED
@@ -10,49 +10,45 @@ require 'vcs'
|
|
10
10
|
|
11
11
|
module VCSRuby
|
12
12
|
class ContactSheet
|
13
|
-
attr_accessor :
|
13
|
+
attr_accessor :signature, :title, :highlight
|
14
14
|
attr_accessor :softshadow, :timestamp, :polaroid
|
15
15
|
attr_reader :thumbnail_width, :thumbnail_height
|
16
16
|
attr_reader :length, :from, :to
|
17
17
|
|
18
|
-
def initialize video,
|
19
|
-
@
|
20
|
-
@
|
18
|
+
def initialize video, capturer
|
19
|
+
@video = video
|
20
|
+
@capturer = capturer
|
21
21
|
@signature = "Created by Video Contact Sheet Ruby"
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
initialize_filename
|
23
|
+
|
24
|
+
if Configuration.instance.verbose?
|
25
|
+
puts "Processing #{File.basename(video.full_path)}..."
|
26
|
+
end
|
27
|
+
|
28
|
+
return unless @video.valid?
|
29
|
+
|
25
30
|
detect_video_properties
|
26
31
|
|
27
|
-
@
|
28
|
-
@
|
32
|
+
@from = TimeIndex.new 0
|
33
|
+
@to = @video.info.duration
|
29
34
|
|
30
|
-
@timestamp =
|
31
|
-
@softshadow =
|
32
|
-
@polaroid =
|
35
|
+
@timestamp = Configuration.instance.timestamp
|
36
|
+
@softshadow = Configuration.instance.softshadow
|
37
|
+
@polaroid = Configuration.instance.polaroid
|
33
38
|
|
34
39
|
@tempdir = Dir.mktmpdir
|
35
40
|
|
36
41
|
ObjectSpace.define_finalizer(self, self.class.finalize(@tempdir) )
|
42
|
+
initialize_geometry(Configuration.instance.rows, Configuration.instance.columns, Configuration.instance.interval)
|
37
43
|
|
38
|
-
initialize_geometry(@configuration.rows, @configuration.columns, @configuration.interval)
|
39
44
|
end
|
40
45
|
|
41
|
-
def initialize_filename
|
42
|
-
@out_path = File.dirname(
|
43
|
-
@out_filename = File.basename(
|
44
|
-
ext = File.extname(filename).gsub('.', '')
|
45
|
-
if ['png', 'jpg', 'jpeg', 'tiff'].include?(ext)
|
46
|
-
@format ||= ext.to_sym
|
47
|
-
end
|
48
|
-
end
|
46
|
+
def initialize_filename override = nil
|
47
|
+
@out_path = File.dirname(@video.full_path)
|
48
|
+
@out_filename = File.basename(override || @video.full_path,'.*')
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
def full_path
|
55
|
-
File.join(@out_path, filename)
|
50
|
+
extension = override ? File.extname(override) : ''
|
51
|
+
@format = extension.length > 0 ? extension : '.png'
|
56
52
|
end
|
57
53
|
|
58
54
|
def initialize_geometry(rows, columns, interval)
|
@@ -62,6 +58,14 @@ module VCSRuby
|
|
62
58
|
@interval = interval
|
63
59
|
end
|
64
60
|
|
61
|
+
def filename
|
62
|
+
"#{@out_filename}#{@format}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def full_path
|
66
|
+
File.join(@out_path, filename)
|
67
|
+
end
|
68
|
+
|
65
69
|
def rows
|
66
70
|
@rows
|
67
71
|
end
|
@@ -71,7 +75,7 @@ module VCSRuby
|
|
71
75
|
end
|
72
76
|
|
73
77
|
def interval
|
74
|
-
@interval || (@to - @from) / (number_of_caps
|
78
|
+
@interval || (@to - @from) / (number_of_caps)
|
75
79
|
end
|
76
80
|
|
77
81
|
def number_of_caps
|
@@ -112,61 +116,37 @@ module VCSRuby
|
|
112
116
|
end
|
113
117
|
end
|
114
118
|
|
115
|
-
|
116
|
-
def self.finalize(tempdir)
|
117
|
-
proc do
|
118
|
-
puts "Cleaning up..." unless Tools.quiet?
|
119
|
-
FileUtils.rm_r tempdir
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
119
|
def build
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
120
|
+
if (@video.info.duration.total_seconds < 1.0)
|
121
|
+
puts "Video is shorter than 1 sec"
|
122
|
+
else
|
123
|
+
initialize_filters
|
124
|
+
initialize_thumbnails
|
125
|
+
capture_thumbnails
|
128
126
|
|
129
|
-
|
130
|
-
|
127
|
+
puts "Composing standard contact sheet..." unless Configuration.instance.quiet?
|
128
|
+
montage = splice_montage(montage_thumbs)
|
131
129
|
|
132
|
-
|
130
|
+
image = MiniMagick::Image.open(montage)
|
133
131
|
|
134
|
-
|
135
|
-
|
132
|
+
puts "Adding header and footer..." unless Configuration.instance.quiet?
|
133
|
+
final = add_header_and_footer image
|
136
134
|
|
137
|
-
|
138
|
-
|
135
|
+
puts "Done. Output wrote to '#{filename}'" unless Configuration.instance.quiet?
|
136
|
+
FileUtils.mv(final, full_path)
|
137
|
+
end
|
139
138
|
end
|
140
139
|
|
141
|
-
|
142
140
|
private
|
143
|
-
def
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
else
|
148
|
-
result = available_capturers.select{ |c| c.name == @capturer }.first
|
141
|
+
def self.finalize(tempdir)
|
142
|
+
proc do
|
143
|
+
puts "Cleaning up..." unless Configuration.instance.quiet?
|
144
|
+
FileUtils.rm_r tempdir
|
149
145
|
end
|
150
|
-
raise "Selected Capturer (#{@capturer.to_s}) not available. Install one of these: #{@capturers.map{ |c| c.name }.join(', ')}" unless result
|
151
|
-
return result
|
152
|
-
end
|
153
|
-
|
154
|
-
def initialize_capturers video
|
155
|
-
@capturers = []
|
156
|
-
@capturers << LibAV.new(video)
|
157
|
-
@capturers << MPlayer.new(video)
|
158
|
-
@capturers << FFmpeg.new(video)
|
159
|
-
|
160
|
-
@video = video
|
161
|
-
|
162
|
-
puts "Available capturers: #{available_capturers.map{ |c| c.to_s }.join(', ')}" if Tools.verbose?
|
163
|
-
end
|
164
|
-
|
165
|
-
def available_capturers
|
166
|
-
@capturers.select{ |c| c.available? }
|
167
146
|
end
|
168
147
|
|
169
148
|
def initialize_filters
|
149
|
+
@filters = []
|
170
150
|
@filters << :resize_filter
|
171
151
|
@filters << :softshadow_filter if softshadow
|
172
152
|
@filters << :timestamp_filter if timestamp
|
@@ -174,14 +154,14 @@ private
|
|
174
154
|
end
|
175
155
|
|
176
156
|
def initialize_thumbnails
|
177
|
-
|
157
|
+
@thumbnails = []
|
158
|
+
time = @from + (interval / 2)
|
178
159
|
(1..number_of_caps).each do |i|
|
179
|
-
thumb =
|
180
|
-
|
160
|
+
thumb = Frame.new @video, @capturer, time
|
161
|
+
time = time + interval
|
181
162
|
thumb.width = thumbnail_width
|
182
163
|
thumb.height = thumbnail_height
|
183
|
-
thumb.
|
184
|
-
thumb.image_path = File::join(@tempdir, "th#{"%03d" % i}.#{selected_capturer.format.to_s}")
|
164
|
+
thumb.filename = File::join(@tempdir, "th#{"%03d" % i}.#{@capturer.format_extension}")
|
185
165
|
thumb.filters.push(*@filters)
|
186
166
|
|
187
167
|
@thumbnails << thumb
|
@@ -189,11 +169,11 @@ private
|
|
189
169
|
end
|
190
170
|
|
191
171
|
def capture_thumbnails
|
192
|
-
puts "Capturing in range [#{from}..#{to}]. Total length: #{@length}" unless
|
172
|
+
puts "Capturing in range [#{from}..#{to}]. Total length: #{@length}" unless Configuration.instance.quiet?
|
193
173
|
|
194
174
|
@thumbnails.each_with_index do |thumbnail, i|
|
195
|
-
puts "Generating capture ##{i + 1}/#{number_of_caps} #{thumbnail.time}..." unless
|
196
|
-
if
|
175
|
+
puts "Generating capture ##{i + 1}/#{number_of_caps} #{thumbnail.time}..." unless Configuration.instance.quiet?
|
176
|
+
if Configuration.instance.blank_evasion?
|
197
177
|
thumbnail.capture_and_evade interval
|
198
178
|
else
|
199
179
|
thumbnail.capture
|
@@ -208,25 +188,25 @@ private
|
|
208
188
|
end
|
209
189
|
|
210
190
|
def detect_length
|
211
|
-
@length =
|
191
|
+
@length = @video.info.duration
|
212
192
|
|
213
193
|
@from = TimeIndex.new 0.0
|
214
194
|
@to = @length
|
215
195
|
end
|
216
196
|
|
217
197
|
def detect_dimensions
|
218
|
-
@thumbnail_width =
|
219
|
-
@thumbnail_height =
|
198
|
+
@thumbnail_width = @video.video.width
|
199
|
+
@thumbnail_height = @video.video.height
|
220
200
|
end
|
221
201
|
|
222
202
|
def montage_thumbs
|
223
203
|
file_path = File::join(@tempdir, 'montage.png')
|
224
204
|
MiniMagick::Tool::Montage.new do |montage|
|
225
|
-
montage.background
|
205
|
+
montage.background Configuration.instance.contact_background
|
226
206
|
@thumbnails.each do |thumbnail|
|
227
|
-
montage << thumbnail.
|
207
|
+
montage << thumbnail.filename
|
228
208
|
end
|
229
|
-
montage.geometry "+#{
|
209
|
+
montage.geometry "+#{Configuration.instance.padding}+#{Configuration.instance.padding}"
|
230
210
|
# rows or columns can be nil (auto fit)
|
231
211
|
montage.tile "#{@columns}x#{@rows}"
|
232
212
|
montage << file_path
|
@@ -236,18 +216,18 @@ private
|
|
236
216
|
|
237
217
|
def splice_montage montage_path
|
238
218
|
if softshadow
|
239
|
-
left =
|
240
|
-
top =
|
241
|
-
bottom = right =
|
219
|
+
left = Configuration.instance.padding + 3
|
220
|
+
top = Configuration.instance.padding + 5
|
221
|
+
bottom = right = Configuration.instance.padding
|
242
222
|
else
|
243
|
-
left = right = top = bottom =
|
244
|
-
end
|
223
|
+
left = right = top = bottom = Configuration.instance.padding
|
224
|
+
end
|
245
225
|
|
246
226
|
|
247
227
|
file_path = File::join(@tempdir, 'spliced.png')
|
248
228
|
MiniMagick::Tool::Convert.new do |convert|
|
249
229
|
convert << montage_path
|
250
|
-
convert.background
|
230
|
+
convert.background Configuration.instance.contact_background
|
251
231
|
|
252
232
|
convert.splice "#{left}x#{top}"
|
253
233
|
convert.gravity 'SouthEast'
|
@@ -262,14 +242,14 @@ private
|
|
262
242
|
file_path = File::join(@tempdir, 'title.png')
|
263
243
|
MiniMagick::Tool::Convert.new do |convert|
|
264
244
|
convert.stack do |ul|
|
265
|
-
ul.size "#{montage.width}x#{
|
266
|
-
ul.xc
|
267
|
-
if
|
268
|
-
ul.font
|
245
|
+
ul.size "#{montage.width}x#{Configuration.instance.title_font.line_height}"
|
246
|
+
ul.xc Configuration.instance.title_background
|
247
|
+
if Configuration.instance.title_font.exists?
|
248
|
+
ul.font Configuration.instance.title_font.path
|
269
249
|
end
|
270
|
-
ul.pointsize
|
271
|
-
ul.background
|
272
|
-
ul.fill
|
250
|
+
ul.pointsize Configuration.instance.title_font.size
|
251
|
+
ul.background Configuration.instance.title_background
|
252
|
+
ul.fill Configuration.instance.title_color
|
273
253
|
ul.gravity 'Center'
|
274
254
|
ul.annotate(0, @title)
|
275
255
|
end
|
@@ -281,12 +261,11 @@ private
|
|
281
261
|
|
282
262
|
def create_highlight montage
|
283
263
|
puts "Generating highlight..."
|
284
|
-
thumb =
|
264
|
+
thumb = Frame.new @video, @highlight
|
285
265
|
|
286
266
|
thumb.width = thumbnail_width
|
287
267
|
thumb.height = thumbnail_height
|
288
|
-
thumb.
|
289
|
-
thumb.image_path = File::join(@tempdir, "highlight_thumb.png")
|
268
|
+
thumb.filename = File::join(@tempdir, "highlight_thumb.png")
|
290
269
|
thumb.capture
|
291
270
|
thumb.apply_filters
|
292
271
|
|
@@ -294,9 +273,9 @@ private
|
|
294
273
|
MiniMagick::Tool::Convert.new do |convert|
|
295
274
|
convert.stack do |a|
|
296
275
|
a.size "#{montage.width}x#{thumbnail_height+20}"
|
297
|
-
a.xc
|
276
|
+
a.xc Configuration.instance.highlight_background
|
298
277
|
a.gravity 'Center'
|
299
|
-
a << thumb.
|
278
|
+
a << thumb.filename
|
300
279
|
a.composite
|
301
280
|
end
|
302
281
|
convert.stack do |a|
|
@@ -312,33 +291,33 @@ private
|
|
312
291
|
|
313
292
|
def add_header_and_footer montage
|
314
293
|
file_path = File::join(@tempdir, filename)
|
315
|
-
header_height =
|
316
|
-
signature_height =
|
294
|
+
header_height = Configuration.instance.header_font.line_height * 3
|
295
|
+
signature_height = Configuration.instance.signature_font.line_height + 8
|
317
296
|
MiniMagick::Tool::Convert.new do |convert|
|
318
297
|
convert.stack do |a|
|
319
298
|
a.size "#{montage.width - 18}x1"
|
320
|
-
a.xc
|
299
|
+
a.xc Configuration.instance.header_background
|
321
300
|
a.size.+
|
322
|
-
if
|
323
|
-
a.font
|
301
|
+
if Configuration.instance.header_font.exists?
|
302
|
+
a.font Configuration.instance.header_font.path
|
324
303
|
end
|
325
|
-
a.pointsize
|
326
|
-
a.background
|
327
|
-
a.fill
|
304
|
+
a.pointsize Configuration.instance.header_font.size
|
305
|
+
a.background Configuration.instance.header_background
|
306
|
+
a.fill Configuration.instance.header_color
|
328
307
|
a.stack do |b|
|
329
308
|
b.gravity 'West'
|
330
309
|
b.stack do |c|
|
331
310
|
c.label 'Filename: '
|
332
|
-
if
|
333
|
-
c.font
|
311
|
+
if Configuration.instance.header_font.exists?
|
312
|
+
c.font Configuration.instance.header_font.path
|
334
313
|
end
|
335
|
-
c.label File.basename(@video)
|
314
|
+
c.label File.basename(@video.full_path)
|
336
315
|
c.append.+
|
337
316
|
end
|
338
|
-
if
|
339
|
-
b.font
|
317
|
+
if Configuration.instance.header_font.exists?
|
318
|
+
b.font Configuration.instance.header_font.path
|
340
319
|
end
|
341
|
-
b.label "File size: #{Tools.to_human_size(File.size(@video))}"
|
320
|
+
b.label "File size: #{Tools.to_human_size(File.size(@video.full_path))}"
|
342
321
|
b.label "Length: #{@length.to_timestamp}"
|
343
322
|
b.append
|
344
323
|
b.crop "#{montage.width}x#{header_height}+0+0"
|
@@ -347,11 +326,11 @@ private
|
|
347
326
|
a.stack do |b|
|
348
327
|
b.size "#{montage.width}x#{header_height}"
|
349
328
|
b.gravity 'East'
|
350
|
-
b.fill
|
329
|
+
b.fill Configuration.instance.header_color
|
351
330
|
b.annotate '+0-1'
|
352
|
-
b << "Dimensions: #{
|
331
|
+
b << "Dimensions: #{@video.video.width}x#{@video.video.height}\nFormat: #{@video.video.codec(true)} / #{@video.audio.codec(true)}\nFPS: #{"%.02f" % @video.video.frame_rate.to_f}"
|
353
332
|
end
|
354
|
-
a.bordercolor
|
333
|
+
a.bordercolor Configuration.instance.header_background
|
355
334
|
a.border 9
|
356
335
|
end
|
357
336
|
convert << create_title(montage) if @title
|
@@ -362,18 +341,18 @@ private
|
|
362
341
|
convert.stack do |a|
|
363
342
|
a.size "#{montage.width}x#{signature_height}"
|
364
343
|
a.gravity 'Center'
|
365
|
-
a.xc
|
366
|
-
if
|
367
|
-
a.font
|
344
|
+
a.xc Configuration.instance.signature_background
|
345
|
+
if Configuration.instance.signature_font.exists?
|
346
|
+
a.font Configuration.instance.signature_font.path
|
368
347
|
end
|
369
|
-
a.pointsize
|
370
|
-
a.fill
|
348
|
+
a.pointsize Configuration.instance.signature_font.size
|
349
|
+
a.fill Configuration.instance.signature_color
|
371
350
|
a.annotate(0, @signature)
|
372
351
|
end
|
373
352
|
convert.append
|
374
353
|
end
|
375
|
-
if format == :jpg || format == :jpeg
|
376
|
-
convert.quality(
|
354
|
+
if @format == :jpg || @format == :jpeg
|
355
|
+
convert.quality(Configuration.instance.quality)
|
377
356
|
end
|
378
357
|
convert << file_path
|
379
358
|
end
|
data/lib/defaults.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
main:
|
2
2
|
rows: 4
|
3
|
-
columns: 4
|
3
|
+
columns: 4
|
4
4
|
interval: ~
|
5
5
|
padding: 2
|
6
6
|
quality: 95
|
@@ -20,7 +20,7 @@ style:
|
|
20
20
|
color: Black
|
21
21
|
background: White
|
22
22
|
highlight:
|
23
|
-
background: LightGoldenRod
|
23
|
+
background: LightGoldenRod
|
24
24
|
contact:
|
25
25
|
background: White
|
26
26
|
timestamp:
|
@@ -35,5 +35,5 @@ style:
|
|
35
35
|
background: SlateGray
|
36
36
|
lowlevel:
|
37
37
|
blank_evasion: true
|
38
|
-
blank_threshold: 0.
|
39
|
-
blank_alternatives: [ -5, 5, -10, 10, -30, 30]
|
38
|
+
blank_threshold: 0.08
|
39
|
+
blank_alternatives: [ -2, 2, -5, 5, -10, 10, -15, 15, -30, 30, 0]
|
data/lib/font.rb
CHANGED
@@ -12,29 +12,29 @@ module VCSRuby
|
|
12
12
|
attr_reader :name, :path, :size
|
13
13
|
|
14
14
|
@@fonts = {}
|
15
|
-
|
15
|
+
|
16
16
|
def initialize name, size
|
17
17
|
@name = name
|
18
18
|
@path = find_path
|
19
19
|
@size = size
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def exists?
|
23
23
|
load_font_cache if @@fonts.length == 0
|
24
|
-
|
24
|
+
|
25
25
|
!!font_by_name(@name)
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def find_path
|
29
29
|
load_font_cache if @@fonts.length == 0
|
30
|
-
|
30
|
+
|
31
31
|
if exists?
|
32
32
|
font_by_name(@name).glyphs
|
33
33
|
else
|
34
34
|
nil
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def font_by_name name
|
39
39
|
if name =~ /\./
|
40
40
|
key, font = @@fonts.select{ |key, f| f.glyphs =~ /#{name}\z/ }.first
|
@@ -43,26 +43,26 @@ module VCSRuby
|
|
43
43
|
@@fonts[name]
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
def load_font_cache
|
48
|
-
|
48
|
+
|
49
49
|
fonts = MiniMagick::Tool::Identify.new(whiny: false) do |identify|
|
50
50
|
identify.list 'font'
|
51
51
|
end
|
52
52
|
|
53
53
|
parse_fonts(fonts)
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def parse_fonts(fonts)
|
57
57
|
font = nil
|
58
58
|
fonts.lines.each do |line|
|
59
59
|
key, value = line.strip.split(':', 2).map(&:strip)
|
60
|
-
|
60
|
+
|
61
61
|
next if [nil, 'Path'].include? key
|
62
62
|
|
63
63
|
if key == 'Font'
|
64
|
-
@@fonts[value] = font = IMFont.new(value)
|
65
|
-
else
|
64
|
+
@@fonts[value] = font = IMFont.new(value)
|
65
|
+
else
|
66
66
|
font.send("#{key}=", value)
|
67
67
|
end
|
68
68
|
end
|