pludoni_pdfutils 0.1.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.
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: []