libis-format 1.0.2 → 1.0.4
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.
- 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
|