libis-format 1.0.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +32 -24
  3. data/README.md +2 -2
  4. data/base/Dockerfile +5 -3
  5. data/base/rework_path +5 -10
  6. data/lib/libis/format.rb +5 -2
  7. data/lib/libis/format/cli/convert.rb +4 -4
  8. data/lib/libis/format/config.rb +3 -1
  9. data/lib/libis/format/converter/audio_converter.rb +2 -36
  10. data/lib/libis/format/converter/base.rb +21 -8
  11. data/lib/libis/format/converter/chain.rb +3 -3
  12. data/lib/libis/format/converter/image_assembler.rb +82 -0
  13. data/lib/libis/format/converter/image_converter.rb +20 -138
  14. data/lib/libis/format/converter/image_splitter.rb +80 -0
  15. data/lib/libis/format/converter/image_watermarker.rb +261 -0
  16. data/lib/libis/format/converter/jp2_converter.rb +1 -1
  17. data/lib/libis/format/converter/office_converter.rb +2 -2
  18. data/lib/libis/format/converter/pdf_assembler.rb +66 -0
  19. data/lib/libis/format/converter/pdf_converter.rb +27 -85
  20. data/lib/libis/format/converter/pdf_optimizer.rb +70 -0
  21. data/lib/libis/format/converter/pdf_splitter.rb +65 -0
  22. data/lib/libis/format/converter/pdf_watermarker.rb +110 -0
  23. data/lib/libis/format/converter/spreadsheet_converter.rb +2 -2
  24. data/lib/libis/format/converter/video_converter.rb +1 -1
  25. data/lib/libis/format/identifier.rb +3 -3
  26. data/lib/libis/format/info.rb +27 -0
  27. data/lib/libis/format/library.rb +147 -0
  28. data/lib/libis/format/tool/extension_identification.rb +4 -4
  29. data/lib/libis/format/tool/identification_tool.rb +6 -6
  30. data/lib/libis/format/tool/spreadsheet_to_ods.rb +1 -0
  31. data/lib/libis/format/version.rb +1 -1
  32. data/lib/libis/format/yaml_loader.rb +71 -0
  33. data/libis-format.gemspec +2 -1
  34. data/tools/fop/fop.bat +75 -75
  35. data/tools/fop/fop.cmd +31 -31
  36. data/tools/fop/fop.js +341 -341
  37. data/tools/fop/lib/avalon-framework.NOTICE.TXT +11 -11
  38. data/tools/fop/lib/xml-apis.LICENSE-SAX.html +17 -17
  39. data/tools/fop/lib/xml-apis.LICENSE.DOM-documentation.html +74 -74
  40. data/tools/fop/lib/xml-apis.LICENSE.DOM-software.html +66 -66
  41. metadata +13 -6
  42. data/lib/libis/format/type_database.rb +0 -133
  43. data/lib/libis/format/type_database_impl.rb +0 -120
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'base'
4
+ require 'libis/format/identifier'
5
+
6
+ require 'mini_magick'
7
+
8
+ MiniMagick.logger.level = ::Logger::UNKNOWN
9
+
10
+ MiniMagick.configure do |config|
11
+ # config.cli = :graphicsmagick
12
+ config.validate_on_create = false
13
+ config.validate_on_write = false
14
+ config.whiny = false
15
+ end
16
+
17
+ module Libis
18
+ module Format
19
+ module Converter
20
+
21
+ class ImageSplitter < Libis::Format::Converter::Base
22
+
23
+ def self.input_types
24
+ [:PDF, :TIFF, :GIF, :PBM, :PGM, :PPM]
25
+ end
26
+
27
+ def self.output_types(format = nil)
28
+ return [] unless input_types.include?(format) if format
29
+ [:TIFF, :JPG, :PNG, :BMP, :GIF, :PDF, :JP2]
30
+ end
31
+
32
+ def self.multipage?(format)
33
+ [:PDF, :TIFF, :GIF, :PBM, :PGM, :PPM].include?(format)
34
+ end
35
+
36
+ def image_split(_)
37
+ #force usage of this converter
38
+ end
39
+
40
+ def quiet(v)
41
+ @quiet = !!v
42
+ end
43
+
44
+ def convert(source, target, format, opts = {})
45
+ super
46
+
47
+ FileUtils.mkpath(File.dirname(target))
48
+
49
+ if self.class.multipage?(format)
50
+ target = File.join(File.dirname(target), "#{File.basename(target, '.*')}-%d#{File.extname(target)}")
51
+ end
52
+
53
+ result = split_image(source, target, format)
54
+ return nil unless result
55
+ target
56
+
57
+ end
58
+
59
+ private
60
+
61
+ def split_image(source, target, format)
62
+
63
+ MiniMagick::Tool::Convert.new do |convert|
64
+ convert.quiet if @quiet
65
+ convert << source
66
+ convert.format(format)
67
+ convert << target
68
+
69
+ debug "ImageMagick command: '#{convert.command.join(' ')}'"
70
+ end
71
+
72
+ target
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,261 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'base'
4
+ require 'libis/format/identifier'
5
+
6
+ require 'mini_magick'
7
+ # noinspection RubyResolve
8
+ require 'fileutils'
9
+
10
+ MiniMagick.logger.level = ::Logger::UNKNOWN
11
+
12
+ MiniMagick.configure do |config|
13
+ # config.cli = :graphicsmagick
14
+ config.validate_on_create = false
15
+ config.validate_on_write = false
16
+ config.whiny = false
17
+ end
18
+
19
+ module Libis
20
+ module Format
21
+ module Converter
22
+
23
+ # noinspection RubyTooManyInstanceVariablesInspection
24
+ class ImageWatermarker < Libis::Format::Converter::Base
25
+
26
+ def self.input_types
27
+ [:TIFF, :JPG, :PNG, :BMP, :GIF, :PDF, :JP2]
28
+ end
29
+
30
+ def self.output_types(format = nil)
31
+ return [] unless input_types.include?(format) if format
32
+ [format]
33
+ end
34
+
35
+ def self.multipage?(format)
36
+ [:PDF, :TIFF, :GIF, :PBM, :PGM, :PPM].include?(format)
37
+ end
38
+
39
+ def initialize
40
+ super
41
+ @quiet = true
42
+ @wm_image = nil
43
+ @wm_file = nil
44
+ @wm_text = '© LIBIS'
45
+ @wm_tiles = 4
46
+ @wm_resize = nil
47
+ @wm_gap = 20
48
+ @wm_gravity = 'Center'
49
+ @wm_rotation = 30
50
+ @wm_composition ='modulate'
51
+ @wm_composition_args = '10'
52
+ end
53
+
54
+ def image_watermark(_)
55
+ #force usage of this converter
56
+ self
57
+ end
58
+
59
+ def quiet(v)
60
+ @quiet = !!v
61
+ self
62
+ end
63
+
64
+ def page(v)
65
+ @page = v.to_i
66
+ self
67
+ end
68
+
69
+ # watermark image to use
70
+ def file(v)
71
+ @wm_file = v.blank? ? nil : v
72
+ self
73
+ end
74
+
75
+ # text to create a watermark from
76
+ def text(v)
77
+ @wm_text = v.blank? ? nil : v
78
+ self
79
+ end
80
+
81
+ # rotation of the watermark text (counter clockwise in degrees; integer number)
82
+ # default 30
83
+ def rotation(v)
84
+ @wm_rotation = v.to_i
85
+ self
86
+ end
87
+
88
+ # number of tiles of the watermark
89
+ # default 4
90
+ # 0: no tiling, so only 1 watermark will be placed with the original size
91
+ # 1: 1 tile, so the watermark will be scaled up to fill the image
92
+ # n > 1: minimum n tiles in both directions
93
+ # n < 0: tile without scaling the watermark
94
+ def tiles(v)
95
+ @wm_tiles = v.to_i
96
+ self
97
+ end
98
+
99
+ # fraction 0.0 - 1.0
100
+ def resize(v)
101
+ @wm_resize = (v.to_f * 100).to_i
102
+ self
103
+ end
104
+
105
+ # size of the gap between watermark instances. Fractions as percentage of widht/height
106
+ # default 0.2
107
+ def gap(v)
108
+ @wm_gap = (v.to_f * 100).to_i
109
+ self
110
+ end
111
+
112
+ # center point for the watermark overlay
113
+ # default 'center'
114
+ def gravity(v)
115
+ @wm_gravity = v.blank? ? nil : v
116
+ self
117
+ end
118
+
119
+ # the image composition method for merging the watermark image
120
+ # default 'modulate'
121
+ # See https://imagemagick.org/script/compose.php for more information
122
+ def composition(v)
123
+ @wm_composition = v.blank? ? nil : v
124
+ self
125
+ end
126
+
127
+ # arguments for the composition method
128
+ # default '10'
129
+ # See https://imagemagick.org/script/compose.php for more information
130
+ def composition_args(v)
131
+ @wm_composition_args = v.blank? ? nil : v
132
+ self
133
+ end
134
+
135
+
136
+ def convert(source, target, format, opts = {})
137
+ super
138
+
139
+ FileUtils.mkpath(File.dirname(target))
140
+
141
+ if source.is_a?(Array) || File.directory?(source)
142
+ error 'Only a single image file is allowed for input'
143
+ else
144
+ image = MiniMagick::Image.open(source) { |b| b.quiet }
145
+
146
+ if image.pages.size > 1
147
+ if @page
148
+ convert_image(image.pages[@page].path, target, format)
149
+ else
150
+ error 'multipage input file detecte; you need to supply a page number'
151
+ end
152
+ else
153
+ convert_image(source, target, format)
154
+ end
155
+ end
156
+
157
+ target
158
+
159
+ end
160
+
161
+ protected
162
+
163
+ # noinspection DuplicatedCode
164
+ def convert_image(source, target, format)
165
+
166
+ wm_image = watermark_image
167
+ return nil unless wm_image
168
+ image_info = MiniMagick::Image::Info.new(source) {|b| b.quiet}
169
+
170
+ MiniMagick::Tool::Convert.new do |convert|
171
+ convert.quiet if @quiet
172
+
173
+ # adapt watermark image to tile size and apply gap and resize if necessary
174
+ convert << @wm_image.path
175
+ # noinspection RubyResolve
176
+ convert.bordercolor('transparent').border("#{@wm_gap}%") if @wm_gap > 0
177
+ convert.filter('Lagrange')
178
+ convert.resize("#{image_info['width'] / @wm_tiles}x#{image_info['height'] / @wm_tiles}") if @wm_tiles > 0
179
+ convert.resize("#{@wm_resize}%") if @wm_resize
180
+ convert.write('mpr:watermark').delete.+
181
+
182
+ # convert the source image
183
+ convert << source
184
+ if @wm_tiles >= 0 and @wm_tiles <= 1
185
+ # only 1 watermark required (tiles = 0/1 => scaled no/yes)
186
+ convert << 'mpr:watermark'
187
+ else
188
+ # fill the image size with a pattern of the watermark image
189
+ convert.stack do |stack|
190
+ stack.size("#{image_info['width']}x#{image_info['height']}")
191
+ stack << 'xc:transparent'
192
+ # noinspection RubyResolve
193
+ stack.tile('mpr:watermark')
194
+ # noinspection RubyResolve
195
+ stack.draw "rectangle 0,0,#{image_info['width']},#{image_info['height']}"
196
+ end
197
+ end
198
+ # perform the blending operation
199
+ convert.compose(@wm_composition).gravity(@wm_gravity).define("compose:args=#{@wm_composition_args}").composite
200
+
201
+ convert.format(format)
202
+ convert << target
203
+
204
+ debug "ImageMagick command: '#{convert.command.join(' ')}'"
205
+ end
206
+
207
+ target
208
+
209
+ end
210
+
211
+ private
212
+
213
+ # Create or use a watermark image.
214
+ #
215
+ # If both text and image are set, the file will be used as-is if it exists and is a valid image file. Otherwise the
216
+ # file will be created or overwritten with a newly created watermark image.
217
+ #
218
+ # The created watermark file will be a 2000x2000 pixels PNG image with transparent background
219
+ #
220
+ # The text will be slanted by given rotation degrees counter-clockwise
221
+ def watermark_image
222
+ rotation = 360 - @wm_rotation
223
+ @wm_image = MiniMagick::Image.new(@wm_file) if @wm_file
224
+ # only create image if file is not an image
225
+ unless @wm_image&.valid?
226
+ # noinspection RubyResolve
227
+ # Create image file (as given or temp file)
228
+ image = @wm_file || (Dir::Tmpname.create(%w(wm_image .png)) {|_|})
229
+ # noinspection RubyResolve
230
+ MiniMagick::Tool::Convert.new do |convert|
231
+ convert.quiet # allways quiet
232
+ convert.background 'transparent'
233
+ convert.size('2000x2000')
234
+ convert.gravity 'Center'
235
+ convert.font('Helvetica').fill('black').pointsize(72) #.stroke('black').strokewidth(1)
236
+ convert << "label:#{@wm_text}"
237
+ convert.rotate rotation
238
+ convert.trim.repage.+
239
+ convert << image
240
+ end
241
+ if @wm_file
242
+ @wm_image = MiniMagick::Image.new(image)
243
+ else # delete temp file
244
+ @wm_image = MiniMagick::Image.open(image)
245
+ File.delete(image)
246
+ end
247
+ # noinspection RubyResolve
248
+ unless @wm_image.valid?
249
+ error "Problem creating watermark image '#{image}'."
250
+ @wm_image = nil
251
+ end
252
+ @wm_image
253
+ end
254
+ end
255
+
256
+
257
+ end
258
+
259
+ end
260
+ end
261
+ end
@@ -15,7 +15,7 @@ module Libis
15
15
  end
16
16
 
17
17
  def self.output_types(format = nil)
18
- return [] unless input_types.include?(format)
18
+ return [] unless input_types.include?(format) if format
19
19
  [:JP2]
20
20
  end
21
21
 
@@ -3,7 +3,7 @@
3
3
  require_relative 'base'
4
4
 
5
5
  require 'libis/format/tool/office_to_pdf'
6
- require 'libis/format/type_database'
6
+ require 'libis/format/library'
7
7
 
8
8
  module Libis
9
9
  module Format
@@ -32,7 +32,7 @@ module Libis
32
32
  end
33
33
 
34
34
  def self.output_types(format = nil)
35
- return [] unless input_types.include?(format)
35
+ return [] unless input_types.include?(format) if format
36
36
  [:PDF]
37
37
  end
38
38
 
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'base'
4
+
5
+ require 'libis/format/tool/pdf_merge'
6
+
7
+ module Libis
8
+ module Format
9
+ module Converter
10
+
11
+ # noinspection DuplicatedCode
12
+ class PdfAssembler < Libis::Format::Converter::Base
13
+
14
+ def self.input_types
15
+ [:PDF]
16
+ end
17
+
18
+ def self.output_types(format = nil)
19
+ return [] unless input_types.include?(format) if format
20
+ [:PDF]
21
+ end
22
+
23
+ def self.category
24
+ :assembler
25
+ end
26
+
27
+ def pdf_assemnble(_)
28
+ #force usage of this converter
29
+ end
30
+
31
+ def convert(source, target, format, opts = {})
32
+ super
33
+
34
+ result = if source.is_a? Array
35
+ assemble(source, target)
36
+ elsif File.directory?(source)
37
+ source_list = Dir[File.join(source, '**', '*')].reject {|p| File.directory? p}
38
+ assemble(source_list, target)
39
+ else
40
+ assemble([source], target)
41
+ end
42
+ return nil unless result
43
+
44
+ result
45
+ end
46
+
47
+ private
48
+
49
+ def assemble(source, target)
50
+
51
+ result = Libis::Format::Tool::PdfMerge.run(source, target)
52
+
53
+ unless result[:err].empty?
54
+ error("PdfMerge encountered errors:\n%s", result[:err].join(join("\n")))
55
+ return nil
56
+ end
57
+
58
+ target
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end
@@ -11,6 +11,7 @@ module Libis
11
11
  module Format
12
12
  module Converter
13
13
 
14
+ # noinspection DuplicatedCode
14
15
  class PdfConverter < Libis::Format::Converter::Base
15
16
 
16
17
  def self.input_types
@@ -18,84 +19,34 @@ module Libis
18
19
  end
19
20
 
20
21
  def self.output_types(format = nil)
21
- return [] unless input_types.include?(format)
22
+ return [] unless input_types.include?(format) if format
22
23
  [:PDF, :PDFA]
23
24
  end
24
25
 
25
- def pdf_convert(_)
26
- #force usage of this converter
26
+ def title(v)
27
+ @options[:title] = v.blank? ? nil : v
27
28
  end
28
29
 
29
- # Set metadata for Pdf file
30
- #
31
- # valid metadata keys are):
32
- # - title
33
- # - author
34
- # - creator
35
- # - keywords
36
- # - subject
37
- #
38
- # @param [Hash] values list of metadata values to set
39
- def metadata(values = {})
40
- values.key_strings_to_symbols!
41
- values.each do |k, v|
42
- next unless [:title, :author, :creator, :keywords, :subject].include?(k)
43
- @options["md_#{k}"] = v
44
- end
30
+ def author(v)
31
+ @options[:author] = v.blank? ? nil : v
45
32
  end
46
33
 
47
- # Select a partial list of pages
48
- # @param [String] selection as described in com.itextpdf.text.pdf.SequenceList: [!][o][odd][e][even]start-end
49
- def range(selection)
50
- @options[:ranges] = selection
34
+ def creator(v)
35
+ @options[:creator] = v.blank? ? nil : v
51
36
  end
52
37
 
53
- # Create or use a watermark image.
54
- #
55
- # The watermark options are (use symbols):
56
- # - text: text to create a watermark from
57
- # - file: watermark image to use
58
- # - rotation: rotation of the watermark text (in degrees; integer number)
59
- # - size: font size of the watermark text
60
- # - opacity: opacity of the watermark (fraction 0.0 - 1.0)
61
- # - gap: size of the gap between watermark instances. Integer value is absolute size in points (1/72 inch). Fractions are percentage of widht/height.
62
- # If both options are given, the file will be used as-is if it exists and is a valid image file. Otherwise the
63
- # file will be created or overwritten with a newly created watermark image.
64
- #
65
- # The created watermark file will be a PNG image with transparent background containing the supplied text
66
- # slanted by 30 degrees counter-clockwise.
67
- #
68
- # @param [Hash] options Hash of options for watermark creation.
69
- def watermark(options = {})
70
- options.key_strings_to_symbols!
71
- if options[:file] && File.exist?(options[:file])
72
- @options['wm_image'] = options[:file]
73
- else
74
- @options['wm_text'] = (options[:text] || '© LIBIS').split('\n')
75
- @options['wm_text_rotation'] = options[:rotation] if options[:rotation]
76
- @options['wm_font_size'] = options[:size] if options[:size]
77
- end
78
- @options['wm_opacity'] = options[:opacity] || '0.3'
79
- @options['wm_gap_ratio'] = options[:gap] if options[:gap].to_s =~ /^\s*(0+\.\d+|1\.0+)\s*$/
80
- @options['wm_gap_size'] = options[:gap] if options[:gap].to_s =~ /^\s*\d+\s*$/
38
+ def keywords(v)
39
+ @options[:keywords] = v.blank? ? nil : v
81
40
  end
82
41
 
83
- # Optimize the PDF
84
- #
85
- # This reduces the graphics quality to a level in order to limit file size. This option relies on the
86
- # presence of ghostscript and takes one argument: the quality level. It should be one of:
87
- #
88
- # - 0 : lowest quality (Acrobat Distiller 'Screen Optimized' equivalent)
89
- # - 1 : medium quality (Acrobat Distiller 'eBook' equivalent)
90
- # - 2 : good quality
91
- # - 3 : high quality (Acrobat Distiller 'Print Optimized' equivalent)
92
- # - 4 : highest quality (Acrobat Distiller 'Prepress Optimized' equivalent)
93
- #
94
- # Note that the optimization is intended to be used with PDF's containing high-resolution images.
95
- #
96
- # @param [Integer] setting quality setting. [0-4]
97
- def optimize(setting = 1)
98
- @options['optimize'] = %w(screen ebook default printer prepress)[setting] if (0..4) === setting
42
+ def subject(v)
43
+ @options[:subject] = v.blank? ? nil : v
44
+ end
45
+
46
+ # Select a partial list of pages
47
+ # @param [String] selection as described in com.itextpdf.text.pdf.SequenceList: [!][o][odd][e][even]start-end
48
+ def range(selection)
49
+ @options[:ranges] = selection.blank? ? nil : selection
99
50
  end
100
51
 
101
52
  def convert(source, target, format, opts = {})
@@ -103,12 +54,6 @@ module Libis
103
54
 
104
55
  result = nil
105
56
 
106
- if (quality = @options.delete('optimize'))
107
- result = optimize_pdf(source, target, quality)
108
- return nil unless result
109
- source = result
110
- end
111
-
112
57
  unless @options.empty?
113
58
  result = convert_pdf(source, target)
114
59
  return nil unless result
@@ -123,17 +68,13 @@ module Libis
123
68
 
124
69
  end
125
70
 
126
- def optimize_pdf(source, target, quality)
127
-
128
- using_temp(target) do |tmpname|
129
- result = Libis::Format::Tool::PdfOptimizer.run(source, tmpname, quality)
130
- unless result[:status] == 0
131
- error("Pdf optimization encountered errors:\n%s", (result[:err] + result[:out]).join("\n"))
132
- next nil
133
- end
134
- tmpname
135
- end
136
- end
71
+ OPTIONS_TABLE = {
72
+ title: 'md_title',
73
+ author: 'md_author',
74
+ creator: 'md_creator',
75
+ keywords: 'md_keywords',
76
+ subject: 'md_subject'
77
+ }
137
78
 
138
79
  def convert_pdf(source, target)
139
80
 
@@ -144,8 +85,9 @@ module Libis
144
85
  if v.nil?
145
86
  nil
146
87
  else
88
+ k = OPTIONS_TABLE[k] || k
147
89
  ["--#{k}", (v.is_a?(Array) ? v : v.to_s)]
148
- end}.flatten
90
+ end}.compact.flatten
149
91
  )
150
92
  unless result[:err].empty?
151
93
  error("Pdf conversion encountered errors:\n%s", result[:err].join(join("\n")))