libis-format 1.0.2 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/libis_format +8 -0
- data/lib/libis/format.rb +1 -0
- data/lib/libis/format/cli/convert.rb +310 -0
- data/lib/libis/format/cli/format.rb +77 -0
- data/lib/libis/format/cli/prompt_helper.rb +87 -0
- data/lib/libis/format/cli/sub_command.rb +22 -0
- data/lib/libis/format/command_line.rb +25 -0
- data/lib/libis/format/converter/image_converter.rb +51 -28
- data/lib/libis/format/identifier.rb +4 -4
- data/lib/libis/format/type_database.rb +25 -92
- data/lib/libis/format/type_database_impl.rb +120 -0
- data/lib/libis/format/version.rb +1 -1
- data/libis-format.gemspec +0 -1
- data/spec/data/multipage.tif +0 -0
- data/spec/data/test.pdf +0 -0
- data/spec/data/test.tif +0 -0
- data/spec/identifier_spec.rb +2 -2
- data/spec/type_database_spec.rb +4 -4
- metadata +10 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a96a71d90952cf5d8be62d9b82ac6fc22c4f080a
|
4
|
+
data.tar.gz: 79bda271ae8b628e96c7a3d19b65884a3fe1eee0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 303f4353c79d988110668ddc1c4bb5867d4e644b30d1a6f60e24da13a2f8f99703ab1b8f5afba73ed11aee0cfdd8bad6e759e857350a97d734d021fcfca31c32
|
7
|
+
data.tar.gz: 612a76df7a9907900aa292e68065f3c13d1362edffa01cbf2425edb8c8f41040d17871680ad7bab430a49790da96bba323ce2706ab13dfa39494ca8795e7d639
|
data/bin/libis_format
ADDED
data/lib/libis/format.rb
CHANGED
@@ -4,6 +4,7 @@ module Libis
|
|
4
4
|
module Format
|
5
5
|
autoload :Config, 'libis/format/config'
|
6
6
|
autoload :TypeDatabase, 'libis/format/type_database'
|
7
|
+
autoload :TypeDatabaseImpl, 'libis/format/type_database_impl'
|
7
8
|
autoload :Identifier, 'libis/format/identifier'
|
8
9
|
|
9
10
|
autoload :Tool, 'libis/format/tool'
|
@@ -0,0 +1,310 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
require 'libis-format'
|
3
|
+
require 'libis-tools'
|
4
|
+
require 'libis/tools/extend/hash'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
require 'libis/format/converter/image_converter'
|
10
|
+
|
11
|
+
require_relative 'sub_command'
|
12
|
+
|
13
|
+
module Libis
|
14
|
+
module Format
|
15
|
+
module Cli
|
16
|
+
class Convert < SubCommand
|
17
|
+
|
18
|
+
no_commands do
|
19
|
+
def self.description(field)
|
20
|
+
"#{STRING_CONFIG[field]}." + (DEFAULT_CONFIG[field].nil? ? '' : " default: #{DEFAULT_CONFIG[field]}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
DEFAULT_CONFIG = {
|
25
|
+
scale: '100%',
|
26
|
+
resize: '100%',
|
27
|
+
wm_tiles: 4,
|
28
|
+
wm_resize: 1.0,
|
29
|
+
wm_gap: 0.2,
|
30
|
+
wm_rotation: 30,
|
31
|
+
wm_opacity: 0.1,
|
32
|
+
wm_gravity: 'center',
|
33
|
+
wm_composition: 'modulate'
|
34
|
+
}
|
35
|
+
|
36
|
+
STRING_CONFIG = {
|
37
|
+
page: 'Page number to select for multipage documents',
|
38
|
+
quiet: 'suppress all warning messages. Error messages are still reported',
|
39
|
+
scale: 'minify / magnify the image with pixel block averaging and pixel replication, respectively',
|
40
|
+
resize: 'Resize the image',
|
41
|
+
resample: 'Resize the image so that its rendered size remains the same as the original at the specified target resolution',
|
42
|
+
flatten: 'Create a canvas the size of the first images virtual canvas using the current -background color, and -compose each image in turn onto that canvas. Images falling outside that canvas is clipped',
|
43
|
+
delete_date: 'Remove modified date and created date metadata from the target image',
|
44
|
+
colorspace: 'Set the image colorspace',
|
45
|
+
profile: 'Specify color profile used for the image',
|
46
|
+
wm_text: 'Create watermark with given text',
|
47
|
+
wm_file: 'Create watermark from given file',
|
48
|
+
wm_tiles: 'Number of tiles of the watermark to distribute over the image. 0: no tiling, 1: watermark image scaled to fit image, n>1: at least n tiles horizontally/vertically, n<0: tile without recaling',
|
49
|
+
wm_resize: 'Resize the watermark image (fraction 0.0 - 1.0)',
|
50
|
+
wm_gap: 'Leave n % of whitespace between the watermark images. Similar effect can be achieved with wm_resize',
|
51
|
+
wm_rotation: 'Rotate the watermark text n degrees counterclockwise (0-360)',
|
52
|
+
wm_opacity: 'Opacity of the watermark (fraction 0.0 - 1.0)',
|
53
|
+
wm_gravity: 'Center point of the watermark overlay',
|
54
|
+
wm_composition: 'Set the type of image composition'
|
55
|
+
}
|
56
|
+
|
57
|
+
desc 'auto SOURCE TARGET [options]', 'Convert SOURCE file to TARGET file using auto algorithm'
|
58
|
+
long_desc <<-DESC
|
59
|
+
|
60
|
+
'auto SOURCE TARGET [options]' will convert a file according to the best conversion chain algorithm.
|
61
|
+
|
62
|
+
A source file name and target file name should be supplied. The source file should exist and be readable.
|
63
|
+
The target file should be writable, but should not exist.
|
64
|
+
|
65
|
+
Optionally an options file name can be added to the command line. The options file should be a valid YAML
|
66
|
+
file that contains either a Hash or an Array of Hashes. The content should contain the required methods and
|
67
|
+
arguments that any of the targetted converters support.
|
68
|
+
|
69
|
+
The source file's format will be identified by the Libis::Format::Identifier and the target file's format
|
70
|
+
will be derived from the file's extension. The Libis::Format::TypeDatabase is used to relate extensions
|
71
|
+
with formats.
|
72
|
+
|
73
|
+
DESC
|
74
|
+
|
75
|
+
method_option :options, aliases: '-o', desc: 'Options file'
|
76
|
+
|
77
|
+
def auto(source_file, target_file)
|
78
|
+
options_file = options[:options]
|
79
|
+
opts = check_input(source_file, target_file, options_file)
|
80
|
+
output, converter = do_convert(source_file, target_file, opts)
|
81
|
+
prompt.ok "Output file '#{output}' created with converter #{converter}."
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'image SOURCE TARGET [options]', 'Convert SOURCE image to TARGET image using ImageConverter'
|
85
|
+
long_desc <<-DESC
|
86
|
+
|
87
|
+
'image SOURCE TARGET [options]' will convert a SOURCE image to TARGET image using the ImageConverter.
|
88
|
+
|
89
|
+
A source file name and target file name should be supplied. The source file should exist and be readable.
|
90
|
+
The target file should be writable, but should not exist.
|
91
|
+
|
92
|
+
The target file's format will be derived from the file's extension. The Libis::Format::TypeDatabase is used
|
93
|
+
to relate extensions with formats.
|
94
|
+
|
95
|
+
DESC
|
96
|
+
|
97
|
+
method_option :page, type: :numeric, aliases: '-p', desc: description(:page)
|
98
|
+
method_option :quiet, type: :boolean, aliases: '-q', desc: description(:quiet)
|
99
|
+
method_option :scale, type: :numeric, aliases: '-c', desc: description(:scale)
|
100
|
+
method_option :resize, type: :numeric, aliases: '-r', desc: description(:resize)
|
101
|
+
method_option :resample, type: :numeric, aliases: '-m', desc: description(:resample)
|
102
|
+
method_option :flatten, type: :boolean, aliases: '-f', desc: description(:flatten)
|
103
|
+
method_option :delete_date, type: :boolean, aliases: '-dd', desc: description(:delete_date)
|
104
|
+
method_option :colorspace, aliases: '-cs', desc: description(:colorspace)
|
105
|
+
method_option :profile, aliases: '-prof', desc: description(:profile)
|
106
|
+
method_option :wm_text, desc: description(:wm_text)
|
107
|
+
method_option :wm_file, desc: description(:wm_file)
|
108
|
+
method_option :wm_tiles, type: :numeric, desc: description(:wm_tiles)
|
109
|
+
method_option :wm_resize, type: :numeric, desc: description(:wm_resize)
|
110
|
+
method_option :wm_gap, type: :numeric, desc: description(:wm_gap)
|
111
|
+
method_option :wm_rotation, type: :numeric, desc: description(:wm_rotation)
|
112
|
+
method_option :wm_opacity, type: :numeric, desc: description(:wm_opacity)
|
113
|
+
method_option :wm_gravity, desc: description(:wm_gravity)
|
114
|
+
method_option :wm_composition, desc: description(:wm_composition)
|
115
|
+
|
116
|
+
def image(source_file, target_file)
|
117
|
+
check_input(source_file, target_file)
|
118
|
+
convert_image(source_file, target_file)
|
119
|
+
prompt.ok "Output file '#{target_file}' created with image converter."
|
120
|
+
end
|
121
|
+
|
122
|
+
protected
|
123
|
+
|
124
|
+
def do_convert(source_file, target_file, opts)
|
125
|
+
format_info = format_identifier(source_file)
|
126
|
+
src_mime = format_info[:mimetype]
|
127
|
+
source_format = format_info[:TYPE]
|
128
|
+
unless source_format
|
129
|
+
prompt.error "File item %s format (#{src_mime}) is not supported."
|
130
|
+
exit
|
131
|
+
end
|
132
|
+
target_format = get_format(target_file)
|
133
|
+
converterlist = []
|
134
|
+
temp_files = []
|
135
|
+
opts.each do |o|
|
136
|
+
o = o.dup
|
137
|
+
tgt_format = o.delete(:target_format) || target_format
|
138
|
+
tgt_file = tempname(source_file, tgt_format)
|
139
|
+
temp_files << tgt_file
|
140
|
+
begin
|
141
|
+
source_file, converter = convert_file(source_file, tgt_file, source_format, tgt_format, o)
|
142
|
+
rescue Exception => e
|
143
|
+
prompt.error "File conversion of '%s' from '%s' to '%s' failed: %s @ %s" %
|
144
|
+
[source_file, source_format, tgt_format, e.message, e.backtrace.first]
|
145
|
+
exit
|
146
|
+
end
|
147
|
+
source_format = tgt_format
|
148
|
+
converterlist << converter
|
149
|
+
end
|
150
|
+
converter = converterlist.join(' + ')
|
151
|
+
if target_file
|
152
|
+
FileUtils.mkpath(File.dirname(target_file))
|
153
|
+
FileUtils.copy(source_file, target_file)
|
154
|
+
else
|
155
|
+
target_file = temp_files.pop
|
156
|
+
end
|
157
|
+
temp_files.each {|tmp_file| FileUtils.rm_f tmp_file}
|
158
|
+
[target_file, converter]
|
159
|
+
end
|
160
|
+
|
161
|
+
def convert_file(src_file, tgt_file, src_format, tgt_format, opts)
|
162
|
+
converter = Libis::Format::Converter::Repository.get_converter_chain(src_format, tgt_format, opts)
|
163
|
+
|
164
|
+
unless converter
|
165
|
+
prompt.error "Could not find converter for #{src_format} -> #{tgt_format} with #{opts}"
|
166
|
+
exit
|
167
|
+
end
|
168
|
+
|
169
|
+
converter_name = converter.to_s
|
170
|
+
prompt.say 'Converting file %s to %s with %s ' % [src_file, tgt_file, converter_name]
|
171
|
+
converted = converter.convert(src_file, tgt_file)
|
172
|
+
|
173
|
+
unless converted && converted == tgt_file
|
174
|
+
prompt.error 'File conversion failed (%s).', converter_name
|
175
|
+
return [nil, converter_name]
|
176
|
+
end
|
177
|
+
|
178
|
+
[tgt_file, converter_name]
|
179
|
+
end
|
180
|
+
|
181
|
+
def convert_image(source_file, target_file)
|
182
|
+
target_format = get_format(target_file)
|
183
|
+
converter = Libis::Format::Converter::ImageConverter.new
|
184
|
+
converter.page options[:page] if options[:page]
|
185
|
+
converter.quiet options[:quiet] if options[:quiet]
|
186
|
+
converter.scale options[:scale] if options[:scale]
|
187
|
+
converter.resize options[:resize] if options[:resize]
|
188
|
+
converter.resample options[:resample] if options[:resample]
|
189
|
+
converter.flatten if options[:flatten]
|
190
|
+
converter.delete_date if options[:delete_date]
|
191
|
+
converter.colorspace options[:colorspace] if options[:colorspace]
|
192
|
+
converter.profile options[:profile] if options[:profile]
|
193
|
+
if options[:wm_text] || options[:wm_file]
|
194
|
+
wm_options = {}
|
195
|
+
wm_options[:text] = options[:wm_text] if options[:wm_text]
|
196
|
+
wm_options[:file] = options[:wm_file] if options[:wm_file]
|
197
|
+
wm_options[:tiles] = options[:wm_tiles] if options[:wm_tiles]
|
198
|
+
wm_options[:resize] = options[:wm_resize] if options[:wm_resize]
|
199
|
+
wm_options[:gap] = options[:wm_gap] if options[:wm_gap]
|
200
|
+
wm_options[:rotation] = options[:wm_rotation] if options[:wm_rotation]
|
201
|
+
wm_options[:opacity] = options[:wm_opacity] if options[:wm_opacity]
|
202
|
+
wm_options[:gravity] = options[:wm_gravity] if options[:wm_gravity]
|
203
|
+
wm_options[:composition] = options[:wm_composition] if options[:wm_composition]
|
204
|
+
converter.watermark(wm_options)
|
205
|
+
end
|
206
|
+
converter.convert source_file, target_file, target_format
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def check_input(source_file, target_file, options_file = nil)
|
212
|
+
# Check if source file exists and can be read
|
213
|
+
unless source_file && File.exist?(source_file) && File.readable?(source_file)
|
214
|
+
prompt.error "Fatal error trying to access source file '#{source_file}'."
|
215
|
+
exit
|
216
|
+
end
|
217
|
+
# Check if target file argument is supplied
|
218
|
+
unless target_file
|
219
|
+
prompt.error "Fatal error: no target file name supplied."
|
220
|
+
exit
|
221
|
+
end
|
222
|
+
# Check if target file directory can be created
|
223
|
+
begin
|
224
|
+
FileUtils.mkdir_p(File.dirname(target_file))
|
225
|
+
rescue SystemCallError => e
|
226
|
+
prompt.error "Fatal error trying to create folder for target file: #{e.message}"
|
227
|
+
exit
|
228
|
+
end
|
229
|
+
|
230
|
+
return [{}] unless options_file
|
231
|
+
|
232
|
+
# Check if options file exists and can be read
|
233
|
+
unless File.exist?(options_file) && File.readable?(options_file)
|
234
|
+
prompt.error "Fatal error trying to access options file '#{options_file}'."
|
235
|
+
exit
|
236
|
+
end
|
237
|
+
|
238
|
+
begin
|
239
|
+
opts = ::YAML.load_file(options_file)
|
240
|
+
opts = case opts
|
241
|
+
when Hash
|
242
|
+
[opts]
|
243
|
+
when Array
|
244
|
+
opts
|
245
|
+
when NilClass
|
246
|
+
[{}]
|
247
|
+
else
|
248
|
+
prompt.error "Options file contents should be a Hash or an Array of Hashes."
|
249
|
+
exit
|
250
|
+
end
|
251
|
+
opts.each {|h| h.key_strings_to_symbols!(recursive: true)}
|
252
|
+
opts
|
253
|
+
rescue Exception => e
|
254
|
+
prompt.error "Fatal error trying to parse options file: #{e.message}"
|
255
|
+
exit
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
def tempname(source_file, target_format)
|
261
|
+
# noinspection RubyResolve
|
262
|
+
Dir::Tmpname.create(
|
263
|
+
[File.basename(source_file, '.*'), ".#{extname(target_format)}"]
|
264
|
+
) {}
|
265
|
+
end
|
266
|
+
|
267
|
+
def extname(format)
|
268
|
+
Libis::Format::TypeDatabase.type_extentions(format).first
|
269
|
+
end
|
270
|
+
|
271
|
+
def get_format(file_name)
|
272
|
+
Libis::Format::TypeDatabase.ext_types(File.extname(file_name)).first
|
273
|
+
end
|
274
|
+
|
275
|
+
def format_identifier(file)
|
276
|
+
prompt.say "Identifying format of file '#{file}'"
|
277
|
+
result = Libis::Format::Identifier.get(file) || {}
|
278
|
+
process_messages(result)
|
279
|
+
format = result[:formats][file]
|
280
|
+
unless format[:mimetype]
|
281
|
+
prompt.warn "Could not determine MIME type. Using default 'application/octet-stream'."
|
282
|
+
result[:puid] ||= 'fmt/unknown'
|
283
|
+
end
|
284
|
+
prompt.say "#{file} format: #{format[:GROUP]}, #{format[:TYPE]} (puid: #{format[:puid]}) mimetype: #{format[:mimetype]}"
|
285
|
+
format
|
286
|
+
end
|
287
|
+
|
288
|
+
def process_messages(format_result)
|
289
|
+
format_result[:messages].each do |msg|
|
290
|
+
case msg[0]
|
291
|
+
when :debug
|
292
|
+
prompt.say msg[1]
|
293
|
+
when :info
|
294
|
+
prompt.say msg[1]
|
295
|
+
when :warn
|
296
|
+
prompt.warn msg[1]
|
297
|
+
when :error
|
298
|
+
prompt.error msg[1]
|
299
|
+
when :fatal
|
300
|
+
prompt.error msg[1]
|
301
|
+
else
|
302
|
+
prompt.say "#{msg[0]}: #{msg[1]}"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
require 'libis-format'
|
3
|
+
require 'libis-tools'
|
4
|
+
|
5
|
+
require_relative 'sub_command'
|
6
|
+
|
7
|
+
module Libis
|
8
|
+
module Format
|
9
|
+
module Cli
|
10
|
+
class Format < SubCommand
|
11
|
+
|
12
|
+
no_commands do
|
13
|
+
def self.description(field)
|
14
|
+
"#{STRING_CONFIG[field]}." + (DEFAULT_CONFIG[field].nil? ? '' : " default: #{DEFAULT_CONFIG[field]}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
DEFAULT_CONFIG = {
|
19
|
+
droid: true,
|
20
|
+
fido: true,
|
21
|
+
file: true,
|
22
|
+
xml_validation: true
|
23
|
+
}
|
24
|
+
|
25
|
+
STRING_CONFIG = {
|
26
|
+
droid: 'Use Droid to identify the format',
|
27
|
+
fido: 'Use Fido to identify the format',
|
28
|
+
file: 'Use File to identify the format',
|
29
|
+
xml_validation: 'When XML file found, validate the file against known XML schemas',
|
30
|
+
}
|
31
|
+
|
32
|
+
desc 'identify FILE [options]', 'Identify the FILE using a combination of the tools.'
|
33
|
+
long_desc <<-DESC
|
34
|
+
|
35
|
+
'identify FILE [options]' will idnetify a file using a combination of tools.
|
36
|
+
|
37
|
+
The file will be identified by each of the selected tools in turn and the format with best score will be
|
38
|
+
selected to give the final result. The score is determined by the identification method and will be lowered if
|
39
|
+
a known weak format (e.g. zip file) is detected.
|
40
|
+
|
41
|
+
The tool will display as much information about the format as possible, including the format candidates that
|
42
|
+
were not selected.
|
43
|
+
|
44
|
+
DESC
|
45
|
+
|
46
|
+
method_option :droid, default: DEFAULT_CONFIG[:droid], type: :boolean, desc: STRING_CONFIG[:droid]
|
47
|
+
method_option :fido, default: DEFAULT_CONFIG[:fido], type: :boolean, desc: STRING_CONFIG[:fido]
|
48
|
+
method_option :file, default: DEFAULT_CONFIG[:file], type: :boolean, desc: STRING_CONFIG[:file]
|
49
|
+
method_option :xml_validation, default: DEFAULT_CONFIG[:xml_validation], type: :boolean, desc: STRING_CONFIG[:xml_validation]
|
50
|
+
|
51
|
+
def identify(source_file)
|
52
|
+
::Libis::Tools::Config.logger.level = :WARN
|
53
|
+
opts = options.inject({}) { |h, x| h[x.first.to_sym] = x.last; h}
|
54
|
+
opts[:keep_output] = true
|
55
|
+
result = ::Libis::Format::Identifier.get source_file, opts
|
56
|
+
puts '--- messages ---'
|
57
|
+
result[:messages].each do |message|
|
58
|
+
puts "#{message[0]} : #{message[1]}"
|
59
|
+
end
|
60
|
+
|
61
|
+
puts '--- formats ---'
|
62
|
+
result[:formats].each do |file, info|
|
63
|
+
puts "#{file}:"
|
64
|
+
ap info
|
65
|
+
end
|
66
|
+
|
67
|
+
puts '--- tool results ---'
|
68
|
+
result[:output].each do |file, info|
|
69
|
+
puts "#{file}:"
|
70
|
+
ap info
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'tty-prompt'
|
2
|
+
require 'pastel'
|
3
|
+
|
4
|
+
module Libis
|
5
|
+
module Format
|
6
|
+
module Cli
|
7
|
+
module PromptHelper
|
8
|
+
|
9
|
+
attr_reader :prompt, :pastel
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
@prompt = TTY::Prompt.new
|
13
|
+
@pastel = Pastel.new
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def index_of(list, value)
|
22
|
+
i = list.index(value)
|
23
|
+
i += 1 if i
|
24
|
+
i || 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def ask(question, bool: false, enum: nil, default: nil, mask: false)
|
28
|
+
cmd, args, opts = :ask, [question], {}
|
29
|
+
if enum
|
30
|
+
cmd = :select
|
31
|
+
args << enum
|
32
|
+
# Change default to its index in the enum
|
33
|
+
default = index_of(enum, default)
|
34
|
+
end
|
35
|
+
cmd = :mask if mask
|
36
|
+
opts[:default] = default if default
|
37
|
+
cmd = (opts[:default] ? :yes? : :no?) if bool
|
38
|
+
prompt.send(cmd, *args, opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def tree_select(path, question: nil, file: false, page_size: 22, filter: true, cycle: false, create: false,
|
42
|
+
default_choices: nil)
|
43
|
+
path = Pathname.new(path) unless path.is_a? Pathname
|
44
|
+
|
45
|
+
return path unless path.exist?
|
46
|
+
path = path.realpath
|
47
|
+
|
48
|
+
dirs = path.children.select(&:directory?).sort
|
49
|
+
files = file ? path.children.select(&:file?).sort : []
|
50
|
+
|
51
|
+
choices = []
|
52
|
+
choices << {name: "Folder: #{path}", value: path, disabled: file ? '' : false}
|
53
|
+
choices += default_choices if default_choices
|
54
|
+
choices << {name: '-- new directory --', value: -> do
|
55
|
+
new_name = prompt.ask('new directory name:', modify: :trim, required: true)
|
56
|
+
new_path = path + new_name
|
57
|
+
FileUtils.mkdir(new_path.to_path)
|
58
|
+
new_path
|
59
|
+
end
|
60
|
+
} if create
|
61
|
+
|
62
|
+
choices << {name: "-- new file --", value: -> do
|
63
|
+
new_name = prompt.ask('new file name:', modify: :trim, required: true)
|
64
|
+
path + new_name
|
65
|
+
end
|
66
|
+
} if file && create
|
67
|
+
|
68
|
+
choices << {name: '[..]', value: path.parent}
|
69
|
+
|
70
|
+
dirs.each {|d| choices << {name: "[#{d.basename}]", value: d}}
|
71
|
+
files.each {|f| choices << {name: f.basename.to_path, value: f}}
|
72
|
+
|
73
|
+
question ||= "Select #{'file or ' if files}directory"
|
74
|
+
selection = prompt.select question, choices,
|
75
|
+
per_page: page_size, filter: filter, cycle: cycle, default: file ? 2 : 1
|
76
|
+
|
77
|
+
return selection unless selection.is_a? Pathname
|
78
|
+
return selection.to_path if selection == path || selection.file?
|
79
|
+
|
80
|
+
tree_select selection, question: question, file: file, page_size: page_size, filter: filter,
|
81
|
+
cycle: cycle, create: create, default_choices: default_choices
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require_relative 'prompt_helper'
|
3
|
+
|
4
|
+
module Libis
|
5
|
+
module Format
|
6
|
+
module Cli
|
7
|
+
class SubCommand < Thor
|
8
|
+
|
9
|
+
include PromptHelper
|
10
|
+
|
11
|
+
def self.banner(command, namespace = nil, subcommand = false)
|
12
|
+
"#{basename} #{subcommand_prefix} #{command.usage}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.subcommand_prefix
|
16
|
+
self.name.gsub(%r{.*::}, '').gsub(%r{^[A-Z]}) { |match| match[0].downcase }.gsub(%r{[A-Z]}) { |match| "-#{match[0].downcase}" }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'tty-prompt'
|
3
|
+
require 'tty-config'
|
4
|
+
|
5
|
+
require 'libis/format/cli/convert'
|
6
|
+
require 'libis/format/cli/format'
|
7
|
+
|
8
|
+
module Libis
|
9
|
+
module Format
|
10
|
+
|
11
|
+
class CommandLine < Thor
|
12
|
+
|
13
|
+
def self.exit_on_failure?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'convert', 'perform format conversion on a given file'
|
18
|
+
subcommand 'convert', Cli::Convert
|
19
|
+
|
20
|
+
desc 'format', 'perform format identification on a given file or directory'
|
21
|
+
subcommand 'format', Cli::Format
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -6,18 +6,20 @@ require 'libis/format/identifier'
|
|
6
6
|
require 'mini_magick'
|
7
7
|
require 'fileutils'
|
8
8
|
|
9
|
-
MiniMagick.logger.level = ::Logger::
|
9
|
+
MiniMagick.logger.level = ::Logger::UNKNOWN
|
10
10
|
|
11
11
|
MiniMagick.configure do |config|
|
12
12
|
# config.cli = :graphicsmagick
|
13
13
|
config.validate_on_create = false
|
14
14
|
config.validate_on_write = false
|
15
|
+
config.whiny = false
|
15
16
|
end
|
16
17
|
|
17
18
|
module Libis
|
18
19
|
module Format
|
19
20
|
module Converter
|
20
21
|
|
22
|
+
# noinspection RubyTooManyInstanceVariablesInspection
|
21
23
|
class ImageConverter < Libis::Format::Converter::Base
|
22
24
|
|
23
25
|
def self.input_types
|
@@ -43,7 +45,7 @@ module Libis
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def quiet(v)
|
46
|
-
@
|
48
|
+
@quiet = !!v
|
47
49
|
end
|
48
50
|
|
49
51
|
def page(nr)
|
@@ -70,16 +72,16 @@ module Libis
|
|
70
72
|
@options[:resample] = value
|
71
73
|
end
|
72
74
|
|
73
|
-
def flatten
|
74
|
-
@
|
75
|
+
def flatten(value = true)
|
76
|
+
@options[:flatten] = !!value
|
75
77
|
end
|
76
78
|
|
77
79
|
def colorspace(value)
|
78
80
|
@options[:colorspace] = value
|
79
81
|
end
|
80
82
|
|
81
|
-
def delete_date
|
82
|
-
@delete_date =
|
83
|
+
def delete_date(value = true)
|
84
|
+
@delete_date = !!value
|
83
85
|
end
|
84
86
|
|
85
87
|
def profile(icc)
|
@@ -92,9 +94,16 @@ module Libis
|
|
92
94
|
# - file: watermark image to use
|
93
95
|
# - text: text to create a watermark from
|
94
96
|
# - rotation: rotation of the watermark text (counter clockwise in degrees; integer number) - default 30
|
95
|
-
# -
|
96
|
-
#
|
97
|
+
# - tiles: number of tiles of the watermark - default 4
|
98
|
+
# - 0: no tiling, so only 1 watermark will be placed with the original size
|
99
|
+
# - 1: 1 tile, so the watermark will be scaled up to fill the image
|
100
|
+
# - n > 1: minimum n tiles in both directions
|
101
|
+
# - n < 0: tile without scaling the watermark
|
102
|
+
# - size: same as tiles - for backwards compatibility
|
103
|
+
# - resize: fraction 0.0 - 1.0
|
97
104
|
# - gap: size of the gap between watermark instances. Fractions as percentage of widht/height. - default 0.2
|
105
|
+
# - opacity: opacity of the watermark (fraction 0.0 - 1.0) - default 0.1
|
106
|
+
# - gravity: center point of the overlay - default 'center'
|
98
107
|
# If both options are given, the file will be used as-is if it exists and is a valid image file. Otherwise the
|
99
108
|
# file will be created or overwritten with a newly created watermark image.
|
100
109
|
#
|
@@ -104,14 +113,17 @@ module Libis
|
|
104
113
|
# @param [Hash] options Hash of options for watermark creation.
|
105
114
|
def watermark(options = {})
|
106
115
|
text = options[:text] || '© LIBIS'
|
107
|
-
@
|
116
|
+
@wm_tiles = (options[:tiles] || '4').to_i
|
117
|
+
@wm_tiles ||= (options[:size] || '4').to_i
|
118
|
+
@wm_resize = ((options[:resize]).to_f * 100).to_i if options[:resize]
|
108
119
|
@wm_opacity = ((options[:opacity] || 0.1).to_f * 100).to_i
|
109
120
|
@wm_composition = options[:composition] || 'modulate'
|
110
|
-
|
121
|
+
@wm_gravity = options[:gravity] || 'center'
|
122
|
+
@wm_gap = ((options[:gap] || 0.2).to_f * 100).to_i
|
111
123
|
rotation = 360 - (options[:rotation] || 30).to_i
|
112
124
|
@wm_image = MiniMagick::Image.new(options[:file]) if options[:file]
|
113
125
|
unless @wm_image && @wm_image.valid?
|
114
|
-
image = options[:file] || (Dir::Tmpname.create(%w(wm_image .png)) {
|
126
|
+
image = options[:file] || (Dir::Tmpname.create(%w(wm_image .png)) {|_|})
|
115
127
|
# noinspection RubyResolve
|
116
128
|
MiniMagick::Tool::Convert.new do |convert|
|
117
129
|
# noinspection RubyLiteralArrayInspection
|
@@ -123,7 +135,6 @@ module Libis
|
|
123
135
|
convert << "label:#{text}"
|
124
136
|
convert.rotate rotation
|
125
137
|
convert.trim.repage.+
|
126
|
-
convert.bordercolor('transparent').border("#{gap}%")
|
127
138
|
convert << image
|
128
139
|
end
|
129
140
|
if options[:file]
|
@@ -150,19 +161,20 @@ module Libis
|
|
150
161
|
assemble_and_convert(source, target, format)
|
151
162
|
|
152
163
|
elsif File.directory?(source)
|
153
|
-
source_list = Dir[File.join(source, '**', '*')].reject {
|
164
|
+
source_list = Dir[File.join(source, '**', '*')].reject {|p| File.directory? p}
|
154
165
|
|
155
166
|
assemble_and_convert(source_list, target, format)
|
156
167
|
|
157
168
|
else
|
158
169
|
|
159
|
-
image = MiniMagick::Image.new(source)
|
170
|
+
image = MiniMagick::Image.new(source) { |b| b.quiet }
|
160
171
|
|
161
172
|
if image.pages.size > 1
|
162
173
|
if @page
|
163
174
|
convert_image(image.pages[@page].path, target, format)
|
164
175
|
else
|
165
|
-
|
176
|
+
# noinspection RubyBlockToMethodReference
|
177
|
+
assemble_and_convert(image.pages.map {|page| page.path}, target, format)
|
166
178
|
end
|
167
179
|
else
|
168
180
|
convert_image(source, target, format)
|
@@ -177,13 +189,14 @@ module Libis
|
|
177
189
|
|
178
190
|
warn 'Received multiple images as input and single page format as target.' unless self.class.multipage?(format)
|
179
191
|
converted_pages = sources.inject([]) do |list, path|
|
192
|
+
# noinspection RubyArgCount
|
180
193
|
converted = Tempfile.new(['page-', ".#{Libis::Format::TypeDatabase.type_extentions(format).first}"])
|
181
194
|
convert_image(path, converted.path, format)
|
182
195
|
list << converted
|
183
196
|
end
|
184
197
|
MiniMagick::Tool::Convert.new do |b|
|
185
198
|
b.append unless self.class.multipage?(format)
|
186
|
-
converted_pages.each {
|
199
|
+
converted_pages.each {|page| b << page.path}
|
187
200
|
b << target
|
188
201
|
end
|
189
202
|
converted_pages.each do |temp_file|
|
@@ -197,29 +210,37 @@ module Libis
|
|
197
210
|
def convert_image(source, target, format)
|
198
211
|
|
199
212
|
image_info = nil
|
200
|
-
image_info = MiniMagick::Image::Info.new(source) if @wm_image
|
213
|
+
image_info = MiniMagick::Image::Info.new(source) {|b| b.quiet} if @wm_image
|
201
214
|
|
202
215
|
MiniMagick::Tool::Convert.new do |convert|
|
216
|
+
convert.quiet if @quiet
|
203
217
|
if @wm_image
|
204
218
|
convert << @wm_image.path
|
219
|
+
convert.bordercolor('transparent').border("#{@wm_gap}%") if @wm_gap > 0
|
205
220
|
convert.filter('Lagrange')
|
206
|
-
convert.resize("#{image_info['width'] / @
|
221
|
+
convert.resize("#{image_info['width'] / @wm_tiles}x#{image_info['height'] / @wm_tiles}") if @wm_tiles > 0
|
222
|
+
convert.resize("#{@wm_resize}%") if @wm_resize
|
223
|
+
convert.write('mpr:watermark').delete.+
|
207
224
|
end
|
208
225
|
|
226
|
+
convert.quiet if @quiet
|
209
227
|
convert << source
|
210
|
-
convert.flatten if format == :JPG
|
228
|
+
convert.flatten if @options[:flatten].nil? && format == :JPG
|
211
229
|
if @wm_image
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
stack
|
216
|
-
|
217
|
-
|
230
|
+
if @wm_tiles >= 0 and @wm_tiles <= 1
|
231
|
+
convert << 'mpr:watermark'
|
232
|
+
else
|
233
|
+
convert.stack do |stack|
|
234
|
+
stack.size("#{image_info['width']}x#{image_info['height']}")
|
235
|
+
stack << 'xc:transparent'
|
236
|
+
stack.tile('mpr:watermark')
|
237
|
+
stack.draw "rectangle 0,0,#{image_info['width']},#{image_info['height']}"
|
238
|
+
end
|
218
239
|
end
|
219
|
-
convert.compose(@wm_composition).define("compose:args=#{@wm_opacity}").composite
|
240
|
+
convert.compose(@wm_composition).gravity(@wm_gravity).define("compose:args=#{@wm_opacity}%").composite
|
220
241
|
end
|
221
242
|
|
222
|
-
@flags.each {
|
243
|
+
@flags.each {|f, v| v.is_a?(TrueClass) ? convert.send(f).+ : convert.send(f)}
|
223
244
|
if @delete_date
|
224
245
|
convert << '+set' << 'modify-date' << '+set' << 'create-date'
|
225
246
|
end
|
@@ -227,13 +248,15 @@ module Libis
|
|
227
248
|
colorspace = @options.delete(:colorspace) || 'sRGB'
|
228
249
|
unless @options.empty?
|
229
250
|
convert.colorspace('RGB')
|
230
|
-
@options.each {
|
251
|
+
@options.each {|o, v| convert.send(o, v)}
|
231
252
|
end
|
232
253
|
convert.colorspace(colorspace)
|
233
254
|
convert.profile @profile if @profile
|
234
255
|
|
235
256
|
convert.format(format)
|
236
257
|
convert << target
|
258
|
+
|
259
|
+
debug "ImageMagick command: '#{convert.command.join(' ')}'"
|
237
260
|
end
|
238
261
|
|
239
262
|
target
|
@@ -42,10 +42,10 @@ module Libis
|
|
42
42
|
|
43
43
|
def get(file, options = {})
|
44
44
|
|
45
|
-
options[:droid] = true unless options.keys.include?(:droid)
|
46
|
-
options[:fido] = true unless options.keys.include?(:fido)
|
47
|
-
options[:file] = true unless options.keys.include?(:file)
|
48
|
-
options[:xml_validation] = true
|
45
|
+
options[:droid] = true unless options.keys.include?(:droid) or (options[:tool] and options[:tool] != :droid)
|
46
|
+
options[:fido] = true unless options.keys.include?(:fido) or (options[:tool] and options[:tool] != :fido)
|
47
|
+
options[:file] = true unless options.keys.include?(:file) or (options[:tool] and options[:tool] != :file)
|
48
|
+
options[:xml_validation] = true if options[:xml_validation].nil?
|
49
49
|
|
50
50
|
result = {messages: [], output: {}, formats: {}}
|
51
51
|
|
@@ -1,23 +1,18 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
require 'singleton'
|
4
3
|
require 'yaml'
|
5
4
|
|
6
5
|
require 'backports/rails/hash'
|
7
|
-
require 'libis/tools/logger'
|
8
|
-
require 'libis/tools/extend/string'
|
9
|
-
|
10
|
-
require_relative 'config'
|
11
6
|
|
12
7
|
module Libis
|
13
8
|
module Format
|
14
9
|
|
10
|
+
# noinspection RubyClassVariableUsageInspection
|
15
11
|
class TypeDatabase
|
16
|
-
|
17
|
-
include ::Libis::Tools::Logger
|
12
|
+
@implementation = Libis::Format::TypeDatabaseImpl.instance
|
18
13
|
|
19
|
-
def self.
|
20
|
-
|
14
|
+
def self.implementation(impl)
|
15
|
+
@implementation = impl
|
21
16
|
end
|
22
17
|
|
23
18
|
def self.enrich(info, map_keys = {})
|
@@ -34,7 +29,7 @@ module Libis
|
|
34
29
|
mapper.keys.each do |key|
|
35
30
|
info[mapper[key]] = get(type_name, key) || info[mapper[key]]
|
36
31
|
end
|
37
|
-
info[mapper[:GROUP]] =
|
32
|
+
info[mapper[:GROUP]] = type_group(type_name)
|
38
33
|
end
|
39
34
|
info
|
40
35
|
end
|
@@ -44,14 +39,14 @@ module Libis
|
|
44
39
|
mapper = Hash.new {|hash,key| hash[key] = key}
|
45
40
|
mapper.merge! map_keys
|
46
41
|
unless (puid = info[mapper[:PUID]]).blank?
|
47
|
-
info[mapper[:TYPE]] ||=
|
42
|
+
info[mapper[:TYPE]] ||= puid_infos(puid).first[:TYPE] rescue nil
|
48
43
|
end
|
49
44
|
unless (mime = info[mapper[:MIME]]).blank?
|
50
|
-
info[mapper[:TYPE]] ||=
|
45
|
+
info[mapper[:TYPE]] ||= mime_infos(mime).first[:TYPE] rescue nil
|
51
46
|
end
|
52
47
|
unless (type_name = info[mapper[:TYPE]]).nil?
|
53
|
-
info[mapper[:MIME]] =
|
54
|
-
info[mapper[:GROUP]] =
|
48
|
+
info[mapper[:MIME]] = type_mimetypes(type_name).first if type_mimetypes(type_name).first
|
49
|
+
info[mapper[:GROUP]] = type_group(type_name)
|
55
50
|
end
|
56
51
|
info
|
57
52
|
end
|
@@ -65,7 +60,7 @@ module Libis
|
|
65
60
|
when :EXTENSION
|
66
61
|
type_extentions(type_name).first
|
67
62
|
else
|
68
|
-
|
63
|
+
typeinfo(type_name)[key]
|
69
64
|
end
|
70
65
|
end
|
71
66
|
|
@@ -85,114 +80,52 @@ module Libis
|
|
85
80
|
typeinfo(t)[:EXTENSIONS] || []
|
86
81
|
end
|
87
82
|
|
83
|
+
def self.typeinfo(t)
|
84
|
+
@implementation.typeinfo(t)
|
85
|
+
end
|
86
|
+
|
88
87
|
def self.group_types(group)
|
89
|
-
|
90
|
-
v[:GROUP] == group.to_sym
|
91
|
-
end.keys
|
88
|
+
@implementation.group_types(group)
|
92
89
|
end
|
93
90
|
|
94
91
|
def self.puid_infos(puid)
|
95
|
-
|
96
|
-
v[:PUID].include? puid rescue false
|
97
|
-
end.values
|
92
|
+
@implementation.puid_infos(puid)
|
98
93
|
end
|
99
94
|
|
100
95
|
def self.puid_types(puid)
|
101
|
-
|
102
|
-
v[:PUID].include? puid rescue false
|
103
|
-
end.keys
|
96
|
+
@implementation.puid_types(puid)
|
104
97
|
end
|
105
98
|
|
106
99
|
def self.puid_groups(puid)
|
107
|
-
puid_types(puid).map
|
108
|
-
type_group t
|
109
|
-
end
|
100
|
+
puid_types(puid).map(&method(:type_group))
|
110
101
|
end
|
111
102
|
|
112
103
|
def self.mime_infos(mime)
|
113
|
-
|
114
|
-
v[:MIME].include? mime rescue false
|
115
|
-
end.values
|
104
|
+
@implementation.mime_infos(mime)
|
116
105
|
end
|
117
106
|
|
118
107
|
def self.mime_types(mime)
|
119
|
-
|
120
|
-
v[:MIME].include? mime rescue false
|
121
|
-
end.keys
|
108
|
+
@implementation.mime_types(mime)
|
122
109
|
end
|
123
110
|
|
124
111
|
def self.mime_groups(mime)
|
125
|
-
mime_types(mime).map
|
126
|
-
type_group t
|
127
|
-
end
|
112
|
+
mime_types(mime).map(&method(:type_group))
|
128
113
|
end
|
129
114
|
|
130
115
|
def self.ext_infos(ext)
|
131
|
-
ext
|
132
|
-
self.instance.types.select do |_, v|
|
133
|
-
v[:EXTENSIONS].include?(ext) rescue false
|
134
|
-
end.values
|
116
|
+
@implementation.ext_infos(ext)
|
135
117
|
end
|
136
118
|
|
137
119
|
def self.ext_types(ext)
|
138
|
-
ext
|
139
|
-
self.instance.types.select do |_, v|
|
140
|
-
v[:EXTENSIONS].include?(ext) rescue false
|
141
|
-
end.keys
|
120
|
+
@implementation.ext_types(ext)
|
142
121
|
end
|
143
122
|
|
144
123
|
def self.puid_typeinfo(puid)
|
145
|
-
|
146
|
-
return v if v[:PUID] and v[:PUID].include?(puid)
|
147
|
-
end
|
148
|
-
nil
|
124
|
+
@implementation.puid_typeinfo(puid)
|
149
125
|
end
|
150
126
|
|
151
127
|
def self.known_mime?(mime)
|
152
|
-
|
153
|
-
return true if v[:MIME].include? mime
|
154
|
-
end
|
155
|
-
false
|
156
|
-
end
|
157
|
-
|
158
|
-
attr_reader :types
|
159
|
-
|
160
|
-
def load_types(file_or_hash = {}, append = true)
|
161
|
-
hash = file_or_hash.is_a?(Hash) ? file_or_hash : YAML::load_file(file_or_hash)
|
162
|
-
# noinspection RubyResolve
|
163
|
-
hash.each do |group, type_info|
|
164
|
-
type_info.each do |type_name, info|
|
165
|
-
type_key = type_name.to_sym
|
166
|
-
info.symbolize_keys!
|
167
|
-
info[:TYPE] = type_key
|
168
|
-
info[:GROUP] = group.to_sym
|
169
|
-
info[:MIME] = info[:MIME].strip.split(/[\s,]+/).map { |v| v.strip } rescue []
|
170
|
-
info[:EXTENSIONS] = info[:EXTENSIONS].strip.split(/[\s,]+/).map { |v| v.strip } rescue []
|
171
|
-
info[:PUID] = info[:PUID].strip.split(/[\s,]+/).map { |v| v.strip } if info[:PUID]
|
172
|
-
if @types.has_key?(type_key)
|
173
|
-
warn 'Type %s already defined; merging with info from %s.', type_name.to_s, file_or_hash
|
174
|
-
info.merge!(@types[type_key]) do |_,v_new,v_old|
|
175
|
-
case v_old
|
176
|
-
when Array
|
177
|
-
append ? v_old + v_new : v_new + v_old
|
178
|
-
when Hash
|
179
|
-
append ? v_new.merge(v_old) : v_old.merge(v_new)
|
180
|
-
else
|
181
|
-
append ? v_old : v_new
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
@types[type_key] = info
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
protected
|
191
|
-
|
192
|
-
def initialize
|
193
|
-
@types = Hash.new
|
194
|
-
type_database = Libis::Format::Config[:type_database]
|
195
|
-
load_types(type_database)
|
128
|
+
@implementation.known_mime?(mime)
|
196
129
|
end
|
197
130
|
|
198
131
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require 'backports/rails/hash'
|
7
|
+
require 'libis/tools/logger'
|
8
|
+
require 'libis/tools/extend/string'
|
9
|
+
|
10
|
+
module Libis
|
11
|
+
module Format
|
12
|
+
|
13
|
+
class TypeDatabaseImpl
|
14
|
+
include Singleton
|
15
|
+
include ::Libis::Tools::Logger
|
16
|
+
|
17
|
+
def typeinfo(t)
|
18
|
+
@types[t.to_sym] || {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def group_types(group)
|
22
|
+
@types.select do |_, v|
|
23
|
+
v[:GROUP] == group.to_sym
|
24
|
+
end.keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def puid_infos(puid)
|
28
|
+
@types.select do |_, v|
|
29
|
+
v[:PUID].include? puid rescue false
|
30
|
+
end.values
|
31
|
+
end
|
32
|
+
|
33
|
+
def puid_types(puid)
|
34
|
+
@types.select do |_, v|
|
35
|
+
v[:PUID].include? puid rescue false
|
36
|
+
end.keys
|
37
|
+
end
|
38
|
+
|
39
|
+
def mime_infos(mime)
|
40
|
+
@types.select do |_, v|
|
41
|
+
v[:MIME].include? mime rescue false
|
42
|
+
end.values
|
43
|
+
end
|
44
|
+
|
45
|
+
def mime_types(mime)
|
46
|
+
@types.select do |_, v|
|
47
|
+
v[:MIME].include? mime rescue false
|
48
|
+
end.keys
|
49
|
+
end
|
50
|
+
|
51
|
+
def ext_infos(ext)
|
52
|
+
ext = ext.gsub /^\./, ''
|
53
|
+
@types.select do |_, v|
|
54
|
+
v[:EXTENSIONS].include?(ext) rescue false
|
55
|
+
end.values
|
56
|
+
end
|
57
|
+
|
58
|
+
def ext_types(ext)
|
59
|
+
ext = ext.gsub /^\./, ''
|
60
|
+
@types.select do |_, v|
|
61
|
+
v[:EXTENSIONS].include?(ext) rescue false
|
62
|
+
end.keys
|
63
|
+
end
|
64
|
+
|
65
|
+
def puid_typeinfo(puid)
|
66
|
+
@types.each do |_, v|
|
67
|
+
return v if v[:PUID] and v[:PUID].include?(puid)
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def known_mime?(mime)
|
73
|
+
@types.each do |_, v|
|
74
|
+
return true if v[:MIME].include? mime
|
75
|
+
end
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
def load_types(file_or_hash = {}, append = true)
|
80
|
+
hash = file_or_hash.is_a?(Hash) ? file_or_hash : YAML::load_file(file_or_hash)
|
81
|
+
# noinspection RubyResolve
|
82
|
+
hash.each do |group, type_info|
|
83
|
+
type_info.each do |type_name, info|
|
84
|
+
type_key = type_name.to_sym
|
85
|
+
info.symbolize_keys!
|
86
|
+
info[:TYPE] = type_key
|
87
|
+
info[:GROUP] = group.to_sym
|
88
|
+
info[:MIME] = info[:MIME].strip.split(/[\s,]+/).map(&:strip) rescue []
|
89
|
+
info[:EXTENSIONS] = info[:EXTENSIONS].strip.split(/[\s,]+/).map { |v| v.strip } rescue []
|
90
|
+
info[:PUID] = info[:PUID].strip.split(/[\s,]+/).map { |v| v.strip } if info[:PUID]
|
91
|
+
if @types.has_key?(type_key)
|
92
|
+
warn 'Type %s already defined; merging with info from %s.', type_name.to_s, file_or_hash
|
93
|
+
info.merge!(@types[type_key]) do |_,v_new,v_old|
|
94
|
+
case v_old
|
95
|
+
when Array
|
96
|
+
append ? v_old + v_new : v_new + v_old
|
97
|
+
when Hash
|
98
|
+
append ? v_new.merge(v_old) : v_old.merge(v_new)
|
99
|
+
else
|
100
|
+
append ? v_old : v_new
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
@types[type_key] = info
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
def initialize
|
112
|
+
@types = Hash.new
|
113
|
+
type_database = Libis::Format::Config[:type_database]
|
114
|
+
load_types(type_database)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
data/lib/libis/format/version.rb
CHANGED
data/libis-format.gemspec
CHANGED
@@ -22,7 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
|
-
spec.add_development_dependency 'bundler', '~> 1.6'
|
26
25
|
spec.add_development_dependency 'rake', '~> 10.3'
|
27
26
|
spec.add_development_dependency 'rspec', '~> 3.1'
|
28
27
|
spec.add_development_dependency 'awesome_print'
|
data/spec/data/multipage.tif
CHANGED
Binary file
|
data/spec/data/test.pdf
CHANGED
Binary file
|
data/spec/data/test.tif
CHANGED
Binary file
|
data/spec/identifier_spec.rb
CHANGED
@@ -12,7 +12,7 @@ formatlist =
|
|
12
12
|
'test.odt' => {mimetype: 'application/vnd.oasis.opendocument.text', puid: 'fmt/291'},
|
13
13
|
'test.doc' => {mimetype: 'application/msword', puid: 'fmt/40'},
|
14
14
|
'test.docx' => {mimetype: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', puid: 'fmt/412'},
|
15
|
-
'test.pdf' => {mimetype: 'application/pdf', puid: 'fmt/
|
15
|
+
'test.pdf' => {mimetype: 'application/pdf', puid: 'fmt/276'},
|
16
16
|
'test.rtf' => {mimetype: 'application/rtf', puid: 'fmt/45'},
|
17
17
|
'test.txt' => {mimetype: 'text/plain', puid: 'x-fmt/111'},
|
18
18
|
'test.ods' => {mimetype: 'application/vnd.oasis.opendocument.spreadsheet', puid: 'fmt/295'},
|
@@ -42,7 +42,7 @@ fidolist =
|
|
42
42
|
'test.odt' => {mimetype: 'application/vnd.oasis.opendocument.text', puid: 'fmt/290'},
|
43
43
|
'test.doc' => {mimetype: nil, puid: 'fmt/111'},
|
44
44
|
'test.docx' => {mimetype: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', puid: 'fmt/412'},
|
45
|
-
'test.pdf' => {mimetype: 'application/pdf', puid: 'fmt/
|
45
|
+
'test.pdf' => {mimetype: 'application/pdf', puid: 'fmt/276'},
|
46
46
|
'test.rtf' => {mimetype: 'application/rtf', puid: 'fmt/45'},
|
47
47
|
'test.txt' => {mimetype: 'text/plain', puid: 'x-fmt/111'},
|
48
48
|
'test.ods' => {mimetype: 'application/vnd.oasis.opendocument.spreadsheet', puid: 'fmt/294'},
|
data/spec/type_database_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require 'spec_helper'
|
|
4
4
|
describe 'Type Databse' do
|
5
5
|
|
6
6
|
before :all do
|
7
|
-
::Libis::Format::
|
7
|
+
::Libis::Format::TypeDatabaseImpl.instance.load_types File.join(File.dirname(__FILE__), 'test_types.yml')
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'should load types from file' do
|
@@ -93,7 +93,7 @@ describe 'Type Databse' do
|
|
93
93
|
|
94
94
|
it 'should load types from hash' do
|
95
95
|
|
96
|
-
::Libis::Format::
|
96
|
+
::Libis::Format::TypeDatabaseImpl.instance.load_types TESTMEDIUM: {
|
97
97
|
TESTREPORT: {
|
98
98
|
NAME: 'Test report format',
|
99
99
|
MIME: 'test/report application/test/report',
|
@@ -111,7 +111,7 @@ describe 'Type Databse' do
|
|
111
111
|
|
112
112
|
it 'should merge types from hash' do
|
113
113
|
|
114
|
-
::Libis::Format::
|
114
|
+
::Libis::Format::TypeDatabaseImpl.instance.load_types TESTMEDIUM: {
|
115
115
|
TESTREPORT: {
|
116
116
|
EXTENSIONS: 'rpt'
|
117
117
|
}
|
@@ -123,7 +123,7 @@ describe 'Type Databse' do
|
|
123
123
|
MIME: %w'test/report application/test/report',
|
124
124
|
EXTENSIONS: %w'rep rpt'
|
125
125
|
|
126
|
-
::Libis::Format::
|
126
|
+
::Libis::Format::TypeDatabaseImpl.instance.load_types({TESTMEDIUM: {
|
127
127
|
TESTREPORT: {
|
128
128
|
EXTENSIONS: 'report'
|
129
129
|
}
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: libis-format
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kris Dekeyser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.6'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.6'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -186,6 +172,7 @@ executables:
|
|
186
172
|
- droid
|
187
173
|
- fido
|
188
174
|
- formatinfo
|
175
|
+
- libis_format
|
189
176
|
- pdf_copy
|
190
177
|
extensions: []
|
191
178
|
extra_rdoc_files: []
|
@@ -200,6 +187,7 @@ files:
|
|
200
187
|
- bin/droid
|
201
188
|
- bin/fido
|
202
189
|
- bin/formatinfo
|
190
|
+
- bin/libis_format
|
203
191
|
- bin/pdf_copy
|
204
192
|
- data/ISOcoated_v2_eci.icc
|
205
193
|
- data/PDFA_def.ps
|
@@ -210,6 +198,11 @@ files:
|
|
210
198
|
- data/xlink.xsd
|
211
199
|
- lib/libis-format.rb
|
212
200
|
- lib/libis/format.rb
|
201
|
+
- lib/libis/format/cli/convert.rb
|
202
|
+
- lib/libis/format/cli/format.rb
|
203
|
+
- lib/libis/format/cli/prompt_helper.rb
|
204
|
+
- lib/libis/format/cli/sub_command.rb
|
205
|
+
- lib/libis/format/command_line.rb
|
213
206
|
- lib/libis/format/config.rb
|
214
207
|
- lib/libis/format/converter.rb
|
215
208
|
- lib/libis/format/converter/audio_converter.rb
|
@@ -242,6 +235,7 @@ files:
|
|
242
235
|
- lib/libis/format/tool/pdfa_validator.rb
|
243
236
|
- lib/libis/format/tool/spreadsheet_to_ods.rb
|
244
237
|
- lib/libis/format/type_database.rb
|
238
|
+
- lib/libis/format/type_database_impl.rb
|
245
239
|
- lib/libis/format/version.rb
|
246
240
|
- libis-format.gemspec
|
247
241
|
- spec/converter_audio_spec.rb
|