poleica 0.9.6

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 (46) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +15 -0
  4. data/CHANGELOG.md +16 -0
  5. data/Gemfile +19 -0
  6. data/Gemfile.lock +192 -0
  7. data/Guardfile +19 -0
  8. data/LICENSE +20 -0
  9. data/README.md +82 -0
  10. data/Rakefile +7 -0
  11. data/lib/poleica/converters/coercive.rb +68 -0
  12. data/lib/poleica/converters/convertible.rb +60 -0
  13. data/lib/poleica/converters/general.rb +18 -0
  14. data/lib/poleica/converters/graphics_magick.rb +110 -0
  15. data/lib/poleica/converters/libre_office.rb +103 -0
  16. data/lib/poleica/converters/null.rb +17 -0
  17. data/lib/poleica/converters/utils.rb +49 -0
  18. data/lib/poleica/pathable.rb +20 -0
  19. data/lib/poleica/polei.rb +24 -0
  20. data/lib/poleica/types/archive.rb +12 -0
  21. data/lib/poleica/types/document.rb +53 -0
  22. data/lib/poleica/types/image.rb +37 -0
  23. data/lib/poleica/types/null.rb +12 -0
  24. data/lib/poleica/types/pdf.rb +19 -0
  25. data/lib/poleica/types/typeable.rb +50 -0
  26. data/lib/poleica/version.rb +4 -0
  27. data/lib/poleica.rb +27 -0
  28. data/poleica.gemspec +18 -0
  29. data/test/poleica/converters/coercive_test.rb +77 -0
  30. data/test/poleica/converters/convertible_test.rb +44 -0
  31. data/test/poleica/converters/general_test.rb +11 -0
  32. data/test/poleica/converters/graphics_magick_test.rb +102 -0
  33. data/test/poleica/converters/libre_office_test.rb +55 -0
  34. data/test/poleica/converters/null_test.rb +18 -0
  35. data/test/poleica/converters/utils_test.rb +5 -0
  36. data/test/poleica/pathable_test.rb +11 -0
  37. data/test/poleica/polei_test.rb +15 -0
  38. data/test/poleica/types/typeable_test.rb +57 -0
  39. data/test/poleica_test.rb +10 -0
  40. data/test/support/files/1px.gif +0 -0
  41. data/test/support/files/example.doc +0 -0
  42. data/test/support/files/example.mp3 +0 -0
  43. data/test/support/files/example.pdf +0 -0
  44. data/test/support/files/example.png +0 -0
  45. data/test/test_helper.rb +38 -0
  46. metadata +134 -0
@@ -0,0 +1,103 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'fileutils'
3
+
4
+ module Poleica
5
+ module Converters
6
+ # The LibreOffice converter, use the 'soffice' command to convert documents
7
+ class LibreOffice
8
+ include Poleica::Converters::Utils
9
+
10
+ BIN_PATHS = {
11
+ linux: '/usr/bin/soffice',
12
+ osx: '/Applications/LibreOffice.app/Contents/MacOS/soffice'
13
+ }
14
+
15
+ COMPATIBLE_TYPES = [
16
+ Types::Document
17
+ ]
18
+
19
+ attr_reader :polei
20
+
21
+ def initialize(polei)
22
+ @polei = polei
23
+ end
24
+
25
+ def to_pdf(options = {})
26
+ opts_gen = OptionsGenerator.new(polei, options, :pdf)
27
+ cmd = "#{bin_path} #{opts_gen.generate}"
28
+ `#{cmd}`
29
+ expected_file_path = opts_gen[:path] || polei.path_with_md5(:pdf)
30
+ File.exists?(expected_file_path) ? expected_file_path : nil
31
+ ensure
32
+ temp_file_path = opts_gen.temp_path
33
+ File.delete(temp_file_path) if File.exists?(temp_file_path)
34
+ end
35
+
36
+ private
37
+
38
+ # Generate options for the soffice command
39
+ class OptionsGenerator
40
+ attr_reader :options, :format, :polei
41
+
42
+ def initialize(polei, options = {}, format = :pdf)
43
+ defaults = {}
44
+ @options = defaults.merge(options)
45
+ @polei = polei
46
+ @format = format
47
+ end
48
+
49
+ def generate
50
+ "#{default_options} #{format} #{output_options}"
51
+ end
52
+
53
+ def [](key)
54
+ options[key]
55
+ end
56
+
57
+ # Generate a temp path, and create the file this is needed in order to
58
+ # have the right filename, LibreOffice just copy the original filename
59
+ # in the choosen directory, it doesn't accept filename params.
60
+ # @return temp_path [String]
61
+ def temp_path
62
+ @temp_path ||= generate_temp_path
63
+ FileUtils.cp(polei.path, @temp_path) unless File.exists?(@temp_path)
64
+ @temp_path
65
+ end
66
+
67
+ private
68
+
69
+ def generate_temp_path
70
+ if options[:path]
71
+ if File.directory?(options[:path])
72
+ basename = File.basename(polei.path_with_md5)
73
+ return File.join(options[:path], basename)
74
+ end
75
+ extension = File.extname(polei.path)
76
+ return pathable_object.path_for_extension(extension[1..-1])
77
+ else
78
+ return polei.path_with_md5(polei.file_extension)
79
+ end
80
+ end
81
+
82
+ def pathable_object
83
+ pathable_object = Struct.new(:path).new(options[:path])
84
+ pathable_object.extend(Poleica::Pathable)
85
+ end
86
+
87
+ def default_options
88
+ '--headless --invisible --norestore --nolockcheck --convert-to'
89
+ end
90
+
91
+ def output_options
92
+ dir_path = File.dirname(temp_path)
93
+ "--outdir #{Utils.escape(dir_path)} #{Utils.escape(temp_path)}"
94
+ end
95
+
96
+ def pages_options
97
+ @pages_options ||= Array(options[:page]).
98
+ flatten.compact.uniq.sort.to_s
99
+ end
100
+ end # class OptionsGenerator
101
+ end # class LibreOffice
102
+ end # module Converters
103
+ end # module Poleica
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ module Converters
4
+ # Null Object Pattern, this is the converter returned if no compatible
5
+ # converters are found
6
+ class Null
7
+ attr_reader :polei
8
+ def initialize(polei)
9
+ @polei = polei
10
+ end
11
+
12
+ def method_missing(method, *args, &block)
13
+ super unless method =~ /^to_/
14
+ end
15
+ end # class Null
16
+ end # module Converters
17
+ end # module Poleica
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'digest/md5'
3
+ require 'shellwords'
4
+
5
+ module Poleica
6
+ module Converters
7
+ # An Utility module for the converters needs to be include
8
+ module Utils
9
+ HOST_OS ||= (defined?('RbConfig') ? RbConfig : Config)::CONFIG['host_os']
10
+
11
+ def windows?
12
+ !!HOST_OS.match(/mswin|windows|cygwin/i)
13
+ end
14
+
15
+ def osx?
16
+ !!HOST_OS.match(/darwin/i)
17
+ end
18
+
19
+ def linux?
20
+ !!HOST_OS.match(/linux/i)
21
+ end
22
+
23
+ def host_os
24
+ [:windows, :osx, :linux].find { |os| self.send(:"#{os}?") }
25
+ end
26
+
27
+ def bin_path
28
+ bin_path_key = :BIN_PATHS # rubocop:disable SymbolName
29
+ bin_paths = self.class.const_get(bin_path_key)
30
+ path = bin_paths[host_os] || bin_paths[:linux]
31
+ raise "#{self.class} not found @ #{path}" unless File.exists?(path)
32
+ path
33
+ end
34
+
35
+ module_function
36
+
37
+ def extract_extension_and_options(method, args = [])
38
+ extension = method.to_s.split(/^to_(.*)/)[1]
39
+ options = args.last if args.last.is_a?(Hash)
40
+ options ||= {}
41
+ [extension, options]
42
+ end
43
+
44
+ def escape(string)
45
+ Shellwords.shellescape(string)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ # Path methods
4
+ module Pathable
5
+ def path_for_extension(extension)
6
+ "#{path_without_extension}.#{extension}"
7
+ end
8
+
9
+ def path_without_extension
10
+ File.join(File.dirname(self.path), File.basename(self.path, '.*'))
11
+ end
12
+
13
+ def path_with_md5(extension = self.extension)
14
+ data = File.read(self.path)
15
+ md5 = Digest::MD5.new
16
+ digest = md5.hexdigest(data)
17
+ "#{path_without_extension}-#{digest}.#{extension}"
18
+ end
19
+ end # class Pathable
20
+ end # module Poleica
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ # Strange name for a simple object, it represents a File
4
+ class Polei
5
+ include Poleica::Typeable
6
+ include Poleica::Pathable
7
+ include Poleica::Convertible
8
+
9
+ attr_reader :path, :name
10
+
11
+ def initialize(path)
12
+ @path = path.strip
13
+ raise "No file @ #{path}" unless File.exists?(path)
14
+ end
15
+
16
+ def name
17
+ File.basename(path, ".#{extension}")
18
+ end
19
+
20
+ def extension
21
+ File.extname(path)[1..-1]
22
+ end
23
+ end # class Polei
24
+ end # module Poleica
@@ -0,0 +1,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ module Types
4
+ # Archive Type
5
+ class Archive
6
+ COMPATIBLE_MIMETYPES = [
7
+ 'application/zip' # .zip or a range of docs like .key, .pptx or .docx
8
+ ]
9
+
10
+ end # class Archive
11
+ end # module Types
12
+ end # module Poleica
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ module Types
4
+ # Document Type
5
+ class Document
6
+ COMPATIBLE_MIMETYPES = [
7
+ 'applicationiapplication/vnd.oasis.opendocument.image', # .odi
8
+ 'application/vnd.oasis.opendocument.presentation', # .opd
9
+ 'application/vnd.oasis.opendocument.text-master', # .odm
10
+ 'application/vnd.oasis.opendocument.spreadsheet', # .ods
11
+ 'application/vnd.oasis.opendocument.graphics', # .odg
12
+ 'application/vnd.oasis.opendocument.formula', # .odf
13
+ 'application/vnd.oasis.opendocument.chart', # .odc
14
+ 'application/vnd.oasis.opendocument.text', # .odt
15
+ 'application/vnd.ms-office', # .doc
16
+ 'application/vnd.ms-excel', # .xls
17
+ 'application/vnd.ms-office', # .ppt, .pps
18
+ 'application/msword', # .doc
19
+ 'text/html', # .html, .htm
20
+ 'text/plain', # .txt
21
+ 'text/rtf' # .rft
22
+ ]
23
+
24
+ # Unsupported :( : key, pages
25
+ COMPATIBLE_EXTENSIONS = %w{
26
+ html
27
+ htm
28
+ odt
29
+ doc
30
+ ods
31
+ odp
32
+ odg
33
+ odc
34
+ odf
35
+ odb
36
+ odc
37
+ odm
38
+ docx
39
+ xls
40
+ xlsx
41
+ ppt
42
+ pptx
43
+ pps
44
+ txt
45
+ rft
46
+ }
47
+
48
+ def initialize(file_path)
49
+
50
+ end
51
+ end # class Document
52
+ end # module Types
53
+ end # module Poleica
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ module Types
4
+ # Image Type
5
+ class Image
6
+ COMPATIBLE_MIMETYPES = [
7
+ 'image/x-portable-pixmap', # .ppm, .pgm, .pbm, .pnm
8
+ 'image/x-portable-bitmap', # .pbm
9
+ 'image/x-ms-bmp', # .bmp
10
+ 'image/svg+xml', # .svg
11
+ 'image/jpeg', # .jpeg, .jpg
12
+ 'image/tiff', # .tiff, .tif
13
+ 'image/gif', # .gif
14
+ 'image/png' # .png
15
+ ]
16
+
17
+ COMPATIBLE_EXTENSIONS = %w{
18
+ tiff
19
+ jpeg
20
+ ppm
21
+ pgm
22
+ pnm
23
+ pbm
24
+ bmp
25
+ svg
26
+ jpg
27
+ tif
28
+ gif
29
+ png
30
+ }
31
+
32
+ def initialize(file_path)
33
+
34
+ end
35
+ end # class Image
36
+ end # module Types
37
+ end # module Poleica
@@ -0,0 +1,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ module Types
4
+ # Null Object Pattern Type, this is the type returned if no compatible
5
+ # types are found
6
+ class Null
7
+ def initialize(file_path)
8
+
9
+ end
10
+ end # class Null
11
+ end # module Types
12
+ end # module Poleica
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ module Types
4
+ # PDF Type
5
+ class PDF
6
+ COMPATIBLE_MIMETYPES = [
7
+ 'application/pdf' # pdf
8
+ ]
9
+
10
+ COMPATIBLE_EXTENSIONS = %w{
11
+ pdf
12
+ }
13
+
14
+ def initialize(file_path)
15
+
16
+ end
17
+ end # class PDF
18
+ end # module Types
19
+ end # module Poleica
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ # Retrieve the mimetype, the extension and the type of a file, it needs a
4
+ # "path" method
5
+ module Typeable
6
+ def file_extension
7
+ @file_extension ||= extract_extension
8
+ end
9
+
10
+ def file_mimetype
11
+ @file_mimetype ||= extract_mimetype
12
+ end
13
+
14
+ def file_type
15
+ @file_type ||= (detect_type_with_extension ||
16
+ detect_type_with_mimetype ||
17
+ Types::Null).new(path)
18
+ end
19
+
20
+ private
21
+
22
+ MIMETYPE_EXTRACT_REGEX = /([^;:]+)/
23
+
24
+ TYPES = [
25
+ Types::Image,
26
+ Types::Document,
27
+ Types::PDF
28
+ ]
29
+
30
+ def extract_mimetype
31
+ `file -b --mime '#{path}'`.strip[MIMETYPE_EXTRACT_REGEX] || ''
32
+ end
33
+
34
+ def extract_extension
35
+ (::File.extname(path)[1..-1] || '').strip
36
+ end
37
+
38
+ def detect_type_with_extension
39
+ TYPES.find do |type|
40
+ type::COMPATIBLE_EXTENSIONS.include?(file_extension)
41
+ end
42
+ end
43
+
44
+ def detect_type_with_mimetype
45
+ TYPES.find do |type|
46
+ type::COMPATIBLE_MIMETYPES.include?(file_mimetype)
47
+ end
48
+ end
49
+ end # class Typeable
50
+ end # module Poleica
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ VERSION ||= '0.9.6'
4
+ end # module Poleica
data/lib/poleica.rb ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Poleica
3
+ def self.new(file_or_path)
4
+ file_or_path = file_or_path.path if file_or_path.respond_to?(:path)
5
+ Polei.new(file_or_path)
6
+ end
7
+ end # module Poleica
8
+
9
+ require 'poleica/version'
10
+
11
+ require 'poleica/types/pdf'
12
+ require 'poleica/types/null'
13
+ require 'poleica/types/image'
14
+ require 'poleica/types/document'
15
+ require 'poleica/types/typeable'
16
+
17
+ require 'poleica/converters/utils'
18
+ require 'poleica/converters/general'
19
+ require 'poleica/converters/null'
20
+ require 'poleica/converters/coercive'
21
+ require 'poleica/converters/graphics_magick'
22
+ require 'poleica/converters/libre_office'
23
+ require 'poleica/converters/convertible'
24
+
25
+ require 'poleica/pathable'
26
+
27
+ require 'poleica/polei'
data/poleica.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/poleica/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'poleica'
6
+ s.version = Poleica::VERSION
7
+ s.license = 'MIT'
8
+ s.summary = 'A general converter and thumbnail creator'
9
+ s.description = 'Poleica can convert docs and images to PDF and PNG, it can be extended to handle a lot of converters'
10
+ s.authors = ['Antoine Lyset']
11
+ s.email = 'antoinelyset@gmail.com'
12
+ s.homepage = 'https://github.com/antoinelyset/poleica'
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.add_development_dependency 'bundler', '~> 1.3'
17
+ s.add_development_dependency 'rake'
18
+ end
@@ -0,0 +1,77 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'test_helper'
3
+ # Test the Coercive Converter Module
4
+
5
+ class CoerciveTest < Minitest::Test
6
+ DOC_PATH = "#{Support.support_path}/files/example.doc"
7
+
8
+ def test_find_next_converter_by_method
9
+ conv_hash = coercive_conv.send(:find_next_converter_by_method, :to_png)
10
+ assert_equal({ to_pdf: Poleica::Converters::GraphicsMagick }, conv_hash)
11
+ end
12
+
13
+ def test_next_converters_by_method
14
+ next_convs = coercive_conv.send(:next_converters_by_method)
15
+ expected_convs = [{ to_pdf: Poleica::Converters::GraphicsMagick }]
16
+ assert_equal(expected_convs, next_convs)
17
+ end
18
+
19
+ def test_try_convert_intermediary_file_creation
20
+ coercive_conv.send(:coerce, :to_png, {})
21
+ assert(File.exists?(expected_pdf_path))
22
+ clean_png_file
23
+ clean_pdf_file
24
+ end
25
+
26
+ def test_try_convert_file_creation
27
+ coercive_conv.send(:try_convert, :to_png)
28
+ assert(File.exists?(find_png_path))
29
+ clean_png_file(find_png_path)
30
+ clean_pdf_file
31
+ end
32
+
33
+ def test_coercive_conversion
34
+ polei = Poleica.new(DOC_PATH)
35
+ returned_path = polei.to_png
36
+ assert(File.exists?(returned_path))
37
+ clean_png_file(find_png_path)
38
+ end
39
+
40
+ def test_coercive_null_return
41
+ polei = Poleica.new("#{Support.support_path}/files/example.mp3")
42
+ returned_path = polei.to_png
43
+ assert_equal(nil, returned_path)
44
+ end
45
+
46
+ private
47
+
48
+ def clean_pdf_file
49
+ File.exists?(expected_pdf_path) && File.delete(expected_pdf_path)
50
+ end
51
+
52
+ def clean_png_file(png_path = expected_png_path)
53
+ File.exists?(png_path) && File.delete(png_path)
54
+ end
55
+
56
+ def expected_pdf_path
57
+ Support.expected_converted_path(DOC_PATH, :pdf)
58
+ end
59
+
60
+ def expected_png_path
61
+ Support.expected_converted_path(expected_pdf_path, :png)
62
+ end
63
+
64
+ # Find a the path of the converted png. It should be used when we
65
+ # don't have the intermediate file since we need to calculate its
66
+ # md5
67
+ def find_png_path
68
+ files = Dir["#{Support.support_path}/files/*"]
69
+ start_path = Support.path_without_extension(expected_pdf_path)
70
+ files.grep(/#{start_path}-\w+\.png$/).first
71
+ end
72
+
73
+ def coercive_conv
74
+ doc_polei = Poleica::Polei.new("#{DOC_PATH}")
75
+ Poleica::Converters::Coercive.new(doc_polei)
76
+ end
77
+ end # class CoerciveTest
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'test_helper'
3
+ # Test the Convertible Module
4
+ class ConvertibleTest < Minitest::Test
5
+ def test_that_it_extracts_converters
6
+ mp3_path = "#{Support.support_path}/files/example.mp3"
7
+ mp3_polei = Poleica::Polei.new(mp3_path)
8
+ # FIXME
9
+ expected_converters = [Poleica::Converters::General]
10
+ assert_equal(expected_converters, mp3_polei.send(:compatible_converters))
11
+ end
12
+
13
+ def test_that_it_extracts_converters_2
14
+ png_path = "#{Support.support_path}/files/example.png"
15
+ png_polei = Poleica::Polei.new(png_path)
16
+ expected_converters = [
17
+ Poleica::Converters::GraphicsMagick,
18
+ Poleica::Converters::General
19
+ ]
20
+ # FIXME
21
+ assert_equal(expected_converters, png_polei.send(:compatible_converters))
22
+ end
23
+
24
+ def test_convert_to_extension
25
+ png_path = "#{Support.support_path}/files/example.png"
26
+ png_polei = Poleica::Polei.new(png_path)
27
+ converter = png_polei.send(:converter_to_extension, :png)
28
+ assert_equal(Poleica::Converters::GraphicsMagick, converter)
29
+ end
30
+
31
+ def test_default_coercive_converter
32
+ png_path = "#{Support.support_path}/files/example.png"
33
+ png_polei = Poleica::Polei.new(png_path)
34
+ converter = png_polei.send(:converter_to_extension, :mp3)
35
+ assert_equal(Poleica::Converters::Coercive, converter)
36
+ end
37
+
38
+ def test_method_missing_raises_exception
39
+ method_name = :random
40
+ png_path = "#{Support.support_path}/files/example.png"
41
+ png_polei = Poleica::Polei.new(png_path)
42
+ assert_raises(NoMethodError) { png_polei.send(method_name) }
43
+ end
44
+ end # class ConvertibleTest
@@ -0,0 +1,11 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'test_helper'
3
+ # Test the General Converter Module
4
+ class GeneralTest < Minitest::Test
5
+ def test_to_bin
6
+ gif_path = "#{Support.support_path}/files/1px.gif"
7
+ gif_data = File.binread(gif_path)
8
+ gif_polei = Poleica::Polei.new(gif_path)
9
+ assert_equal(gif_data, gif_polei.to_bin)
10
+ end
11
+ end # class GeneralTest