pludoni_pdfutils 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e2d234dcf736cf33aab2fe4a858f41e35c25c62648cdddb73b8e25fe1f81a805
4
+ data.tar.gz: 6a76b1a8a6bda7e02fea6860f4efcce96c72174a1efcdfca8db3caf237a14d6a
5
+ SHA512:
6
+ metadata.gz: 2f31fd04e951b53a70f0e44f18bfc24aeaf5b80385fe83a8faf219a55c34bd6bfa81868c7ecf2d2f75928364504588e9a1e1669811e3cd34f117c9fe8d6d2305
7
+ data.tar.gz: 9842f36bb4db2b57781a9445ec4da3fcceb2dd861e15c08d4e5f07f09613d82d2566e87feeb8417368a2f2ef30d7a68f365b9361ae119bdf43be0ae9cfecfa7b
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in pludoni-pdfutils.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,88 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pludoni-pdfutils (0.1.0)
5
+ activestorage (>= 5.2.0)
6
+ mimemagic
7
+ zeitwerk
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionpack (6.1.7)
13
+ actionview (= 6.1.7)
14
+ activesupport (= 6.1.7)
15
+ rack (~> 2.0, >= 2.0.9)
16
+ rack-test (>= 0.6.3)
17
+ rails-dom-testing (~> 2.0)
18
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
19
+ actionview (6.1.7)
20
+ activesupport (= 6.1.7)
21
+ builder (~> 3.1)
22
+ erubi (~> 1.4)
23
+ rails-dom-testing (~> 2.0)
24
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
25
+ activejob (6.1.7)
26
+ activesupport (= 6.1.7)
27
+ globalid (>= 0.3.6)
28
+ activemodel (6.1.7)
29
+ activesupport (= 6.1.7)
30
+ activerecord (6.1.7)
31
+ activemodel (= 6.1.7)
32
+ activesupport (= 6.1.7)
33
+ activestorage (6.1.7)
34
+ actionpack (= 6.1.7)
35
+ activejob (= 6.1.7)
36
+ activerecord (= 6.1.7)
37
+ activesupport (= 6.1.7)
38
+ marcel (~> 1.0)
39
+ mini_mime (>= 1.1.0)
40
+ activesupport (6.1.7)
41
+ concurrent-ruby (~> 1.0, >= 1.0.2)
42
+ i18n (>= 1.6, < 2)
43
+ minitest (>= 5.1)
44
+ tzinfo (~> 2.0)
45
+ zeitwerk (~> 2.3)
46
+ builder (3.2.4)
47
+ concurrent-ruby (1.1.10)
48
+ crass (1.0.6)
49
+ erubi (1.11.0)
50
+ globalid (1.0.0)
51
+ activesupport (>= 5.0)
52
+ i18n (1.12.0)
53
+ concurrent-ruby (~> 1.0)
54
+ loofah (2.19.0)
55
+ crass (~> 1.0.2)
56
+ nokogiri (>= 1.5.9)
57
+ marcel (1.0.2)
58
+ mimemagic (0.4.3)
59
+ nokogiri (~> 1)
60
+ rake
61
+ mini_mime (1.1.2)
62
+ minitest (5.16.3)
63
+ nokogiri (1.13.8-x86_64-linux)
64
+ racc (~> 1.4)
65
+ racc (1.6.0)
66
+ rack (2.2.4)
67
+ rack-test (2.0.2)
68
+ rack (>= 1.3)
69
+ rails-dom-testing (2.0.3)
70
+ activesupport (>= 4.2.0)
71
+ nokogiri (>= 1.6)
72
+ rails-html-sanitizer (1.4.3)
73
+ loofah (~> 2.3)
74
+ rake (13.0.6)
75
+ tzinfo (2.0.5)
76
+ concurrent-ruby (~> 1.0)
77
+ zeitwerk (2.6.0)
78
+
79
+ PLATFORMS
80
+ x86_64-linux
81
+
82
+ DEPENDENCIES
83
+ bundler
84
+ pludoni-pdfutils!
85
+ rake
86
+
87
+ BUNDLED WITH
88
+ 2.3.15
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Stefan Wienert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Pludoni::Pdfutils
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'pludoni-pdfutils'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install pludoni-pdfutils
18
+
19
+ requires ghostscript installed.
20
+
21
+ ## Usage
22
+
23
+ All method take either ActiveStorage::Blob or File/Tempfile as argument and return a Tempfile/File
24
+
25
+ ```ruby
26
+ # compresses the given file if larger than given max_size
27
+ # @returns [Pludoni::Pdfutils::FileWrapper]
28
+ Pludoni::Pdfutils.compress_if_large(file, max_size: 5.megabytes)
29
+
30
+ # converts all images to pdf (keep pdf) and joins them if there are more than max_files
31
+ # @param blobs [Array<ActiveStorage::Blob>]
32
+ # @param max_files [Integer] keep max_files - 1 files, and join the rest
33
+ # @param max_size [Integer] in bytes Convert individual file if larger than max_size
34
+ # @returns [Array<Pludoni::Pdfutils::FileWrapper>]
35
+ Pludoni::Pdfutils.convert_all_to_pdf_and_join_max_size(blobs, max_files: 3, max_size: 5.megabytes)
36
+ ```
37
+
38
+ Individual classes:
39
+
40
+ ```ruby
41
+ tempfile = Pludoni::Pdfutils::ConvertToPdf.new(image_file).run
42
+
43
+
44
+ tempfile = Pludoni::Pdfutils::Joiner.new(job_application.uploads.map(&:blob)).run
45
+
46
+ # compresses with Ghostscript + /ebook Profile
47
+ tempfile = Pludoni::Pdfutils::Compressor.new(pdf_file).run
48
+ ```
49
+
50
+
51
+ ## License
52
+
53
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pludoni/pdfutils"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ module Pludoni
2
+ module Pdfutils
3
+ class ActiveStorageWrapper < FileWrapper
4
+ def filesize
5
+ @file.byte_size
6
+ end
7
+
8
+ def to_tf
9
+ file = Tempfile.new(["ActiveStorage-#{@file.id}-", @file.filename.extension_with_delimiter])
10
+ ActiveStorage::Downloader.new(@file.service).send(:download, @file.key, file)
11
+ file
12
+ end
13
+
14
+ def filename
15
+ @file.filename.to_s
16
+ end
17
+
18
+ def open(&block)
19
+ @file.open(&block)
20
+ end
21
+
22
+ def content_type
23
+ @file.content_type
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ require 'open3'
2
+
3
+ module Pludoni::Pdfutils
4
+ class Compressor
5
+ def initialize(blob)
6
+ @blob = FileWrapper.make(blob)
7
+ end
8
+
9
+ def run
10
+ tf = Tempfile.new(['joiner', '.pdf'])
11
+ tf.binmode
12
+ input = @blob.to_tf
13
+ cli = "gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=#{Shellwords.escape tf.path} #{Shellwords.escape input.path}"
14
+
15
+ stdout, stderr, status = Open3.capture3(cli)
16
+ unless status.success?
17
+ raise CompressionFailed, "PDF Compression failed: \nStdout: #{stdout}\nStderr: #{stderr}"
18
+ end
19
+
20
+ FileWrapper.make(tf)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ require 'open3'
2
+
3
+ module Pludoni::Pdfutils
4
+ class ConvertToPdf
5
+ def initialize(blob)
6
+ @blob = FileWrapper.make(blob)
7
+ end
8
+
9
+ def run(&block)
10
+ @blob.open do |source|
11
+ # convert image to pdf
12
+ tf = Tempfile.new(['convert', '.pdf'])
13
+ tf.binmode
14
+ cli = "gs -dNOSAFER -dPDFSETTINGS=/prepress -sDEVICE=pdfwrite -o #{tf.path} viewjpeg.ps -c \\(#{source.path}\\) viewJPEG"
15
+
16
+ stdout, stderr, status = Open3.capture3(cli)
17
+ unless status.success?
18
+ raise ConversionFailedError, "PDF convertion failed: Command: #{cli}\nStdout: #{stdout}\nStderr: #{stderr}"
19
+ end
20
+
21
+ FileWrapper.make(tf)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ module Pludoni
2
+ module Pdfutils
3
+ class FileWrapper
4
+ def initialize(file)
5
+ @file = file
6
+ end
7
+
8
+ def self.make(blob_or_file)
9
+ case blob_or_file
10
+ when ActiveStorage::Blob
11
+ ActiveStorageWrapper.new(blob_or_file)
12
+ when File, Tempfile
13
+ LocalFileWrapper.new(blob_or_file)
14
+ when FileWrapper
15
+ blob_or_file
16
+ else
17
+ raise NotImplementedError, blob_or_file.class
18
+ end
19
+ end
20
+
21
+ def filesize
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def open(&block)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def content_type
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+
@@ -0,0 +1,23 @@
1
+ require 'open3'
2
+
3
+ module Pludoni::Pdfutils
4
+ class Joiner
5
+ def initialize(blobs)
6
+ @blobs = blobs.map { |i| FileWrapper.make(i) }
7
+ end
8
+
9
+ def run
10
+ tf = Tempfile.new(['joiner', '.pdf'])
11
+ tf.binmode
12
+ tfs = @blobs.map { |i| i.to_tf }
13
+ cli = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=#{tf.path} #{tfs.map(&:path).join(' ')}"
14
+
15
+ stdout, stderr, status = Open3.capture3(cli)
16
+ unless status.success?
17
+ raise JoiningFailedError, "PDF Joining failed: \nStdout: #{stdout}\nStderr: #{stderr}"
18
+ end
19
+
20
+ FileWrapper.make(tf)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ module Pludoni
2
+ module Pdfutils
3
+ class LocalFileWrapper < FileWrapper
4
+ def filesize
5
+ @file.size
6
+ end
7
+
8
+ def filename
9
+ @file.path.split("/").last
10
+ end
11
+
12
+ def to_tf
13
+ @file.rewind
14
+ @file
15
+ end
16
+
17
+ def open(&block)
18
+ @file.open(&block)
19
+ end
20
+
21
+ def content_type
22
+ MimeMagic.by_magic(@file.read).type
23
+ ensure
24
+ @file.rewind
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module Pludoni
2
+ module Pdfutils
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,59 @@
1
+ require "pludoni/pdfutils/version"
2
+ require 'mimemagic'
3
+
4
+ module Pludoni
5
+ module Pdfutils
6
+ class Error < StandardError; end
7
+ class JoiningFailedError < Error; end
8
+ class ConversionFailedError < Error; end
9
+ class CompressionFailed < Error; end
10
+
11
+ module_function
12
+
13
+ # compresses the given file if larger than given max_size
14
+ # @param file [File, Tempfile, ActiveStorage::Blob]
15
+ # @param max_size [Integer] in bytes
16
+ # @return [File, Tempfile]
17
+ def compress_if_large(file, max_size:)
18
+ file = FileWrapper.make(file)
19
+ if max_size && file.filesize > max_size
20
+ yield(file) if block_given?
21
+ Compressor.new(file).run
22
+ else
23
+ file
24
+ end
25
+ end
26
+
27
+ # converts all images to pdf and joins them if there are more than max_files
28
+ # @param blobs [Array<ActiveStorage::Blob>]
29
+ # @param max_files [Integer] keep max_files - 1 files, and join the rest
30
+ # @param max_size [Integer] in bytes Convert individual file if larger than max_size
31
+ def convert_all_to_pdf_and_join_max_size(blobs, max_files:, max_size: nil, &block)
32
+ files = blobs.map { |upload| convert_to_pdf(upload, &block) }
33
+ if files.length > max_files
34
+ if block_given?
35
+ yield("More than #{max_files}; joining, keeping first #{max_files - 1} files; joining the rest")
36
+ end
37
+ keep = files.take(max_files - 1)
38
+ join = Joiner.new(files.drop(max_files - 1)).run(&block)
39
+ files = keep + [join]
40
+ end
41
+ files.map do |file|
42
+ compress_if_large(file, max_size: max_size) do
43
+ yield("Compressing file") if block_given?
44
+ end
45
+ end
46
+ end
47
+
48
+ def convert_to_pdf(blob, &block)
49
+ blob = FileWrapper.make(blob)
50
+ # TODO: convert odt docx
51
+ if blob.content_type.to_s['image']
52
+ block.call("converting #{blob.filename.to_s} to pdf") if block_given?
53
+ ConvertToPdf.new(blob).run(&block)
54
+ else
55
+ blob
56
+ end
57
+ end
58
+ end
59
+ end
data/lib/pludoni.rb ADDED
@@ -0,0 +1,2 @@
1
+ module Pludoni
2
+ end
@@ -0,0 +1,8 @@
1
+ require "zeitwerk"
2
+ require 'pludoni'
3
+
4
+ loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
5
+ loader.ignore("#{ __FILE__ }")
6
+ loader.setup
7
+
8
+ require 'pludoni/pdfutils'
@@ -0,0 +1,30 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "pludoni/pdfutils/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pludoni_pdfutils"
8
+ spec.version = Pludoni::Pdfutils::VERSION
9
+ spec.authors = ["Stefan Wienert"]
10
+ spec.email = ["info@stefanwienert.de"]
11
+
12
+ spec.summary = %q{Convert to pdf, compress pdf, join pdf using Ghostscript}
13
+ spec.description = %q{Convert to pdf, compress pdf, join pdf using Ghostscript}
14
+ spec.homepage = "https://github.com/pludoni/pdfutils"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_dependency "activestorage", ">= 5.2.0"
25
+ spec.add_dependency "zeitwerk"
26
+ spec.add_dependency "mimemagic"
27
+
28
+ spec.add_development_dependency "bundler"
29
+ spec.add_development_dependency "rake"
30
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pludoni_pdfutils
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stefan Wienert
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-09-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activestorage
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeitwerk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mimemagic
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Convert to pdf, compress pdf, join pdf using Ghostscript
84
+ email:
85
+ - info@stefanwienert.de
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/console
97
+ - bin/setup
98
+ - lib/pludoni.rb
99
+ - lib/pludoni/pdfutils.rb
100
+ - lib/pludoni/pdfutils/active_storage_wrapper.rb
101
+ - lib/pludoni/pdfutils/compressor.rb
102
+ - lib/pludoni/pdfutils/convert_to_pdf.rb
103
+ - lib/pludoni/pdfutils/file_wrapper.rb
104
+ - lib/pludoni/pdfutils/joiner.rb
105
+ - lib/pludoni/pdfutils/local_file_wrapper.rb
106
+ - lib/pludoni/pdfutils/version.rb
107
+ - lib/pludoni_pdfutils.rb
108
+ - pludoni_pdfutils.gemspec
109
+ homepage: https://github.com/pludoni/pdfutils
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.0.3
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Convert to pdf, compress pdf, join pdf using Ghostscript
132
+ test_files: []