hexapdf 0.10.0 → 0.11.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 +4 -4
- data/CHANGELOG.md +33 -0
- data/CONTRIBUTERS +1 -1
- data/Rakefile +35 -50
- data/VERSION +1 -1
- data/lib/hexapdf/cli.rb +4 -0
- data/lib/hexapdf/cli/command.rb +6 -2
- data/lib/hexapdf/cli/image2pdf.rb +141 -0
- data/lib/hexapdf/cli/info.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +32 -2
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/cli/watermark.rb +130 -0
- data/lib/hexapdf/composer.rb +2 -2
- data/lib/hexapdf/configuration.rb +7 -1
- data/lib/hexapdf/content/canvas.rb +2 -2
- data/lib/hexapdf/content/graphic_object/arc.rb +2 -2
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +2 -2
- data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/dictionary.rb +11 -3
- data/lib/hexapdf/dictionary_fields.rb +32 -3
- data/lib/hexapdf/document.rb +7 -3
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +21 -1
- data/lib/hexapdf/document/pages.rb +2 -2
- data/lib/hexapdf/encryption/standard_security_handler.rb +2 -2
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/true_type/table/head.rb +2 -2
- data/lib/hexapdf/font/true_type/table/os2.rb +4 -4
- data/lib/hexapdf/font/true_type_wrapper.rb +16 -16
- data/lib/hexapdf/font/type1_wrapper.rb +16 -16
- data/lib/hexapdf/font_loader.rb +2 -0
- data/lib/hexapdf/font_loader/from_configuration.rb +5 -0
- data/lib/hexapdf/font_loader/standard14.rb +5 -0
- data/lib/hexapdf/image_loader/png.rb +1 -1
- data/lib/hexapdf/layout/box.rb +2 -2
- data/lib/hexapdf/layout/image_box.rb +1 -1
- data/lib/hexapdf/layout/style.rb +50 -24
- data/lib/hexapdf/layout/text_box.rb +1 -1
- data/lib/hexapdf/layout/text_fragment.rb +2 -2
- data/lib/hexapdf/layout/text_layouter.rb +14 -10
- data/lib/hexapdf/name_tree_node.rb +3 -3
- data/lib/hexapdf/number_tree_node.rb +3 -3
- data/lib/hexapdf/pdf_array.rb +207 -0
- data/lib/hexapdf/rectangle.rb +12 -12
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/stream.rb +6 -4
- data/lib/hexapdf/task/optimize.rb +3 -3
- data/lib/hexapdf/type.rb +2 -0
- data/lib/hexapdf/type/acro_form.rb +51 -0
- data/lib/hexapdf/type/acro_form/field.rb +129 -0
- data/lib/hexapdf/type/acro_form/form.rb +124 -0
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -0
- data/lib/hexapdf/type/annotations/link.rb +4 -15
- data/lib/hexapdf/type/annotations/markup_annotation.rb +2 -1
- data/lib/hexapdf/type/annotations/text.rb +3 -6
- data/lib/hexapdf/type/annotations/widget.rb +90 -0
- data/lib/hexapdf/type/catalog.rb +12 -9
- data/lib/hexapdf/type/cid_font.rb +3 -3
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/font_descriptor.rb +5 -2
- data/lib/hexapdf/type/font_simple.rb +1 -1
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/type/graphics_state_parameter.rb +11 -6
- data/lib/hexapdf/type/icon_fit.rb +58 -0
- data/lib/hexapdf/type/image.rb +14 -8
- data/lib/hexapdf/type/info.rb +2 -1
- data/lib/hexapdf/type/page.rb +4 -4
- data/lib/hexapdf/type/page_tree_node.rb +3 -7
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +4 -4
- data/lib/hexapdf/type/viewer_preferences.rb +7 -4
- data/lib/hexapdf/type/xref_stream.rb +2 -2
- data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/man/man1/hexapdf.1 +77 -8
- data/test/hexapdf/content/test_canvas.rb +2 -2
- data/test/hexapdf/content/test_processor.rb +3 -3
- data/test/hexapdf/document/test_files.rb +4 -4
- data/test/hexapdf/document/test_fonts.rb +13 -1
- data/test/hexapdf/document/test_images.rb +6 -6
- data/test/hexapdf/document/test_pages.rb +8 -8
- data/test/hexapdf/encryption/test_security_handler.rb +7 -7
- data/test/hexapdf/encryption/test_standard_security_handler.rb +5 -5
- data/test/hexapdf/font/test_true_type_wrapper.rb +2 -2
- data/test/hexapdf/font_loader/test_from_configuration.rb +4 -0
- data/test/hexapdf/font_loader/test_standard14.rb +10 -0
- data/test/hexapdf/image_loader/test_jpeg.rb +1 -1
- data/test/hexapdf/image_loader/test_png.rb +3 -3
- data/test/hexapdf/layout/test_box.rb +2 -2
- data/test/hexapdf/layout/test_frame.rb +1 -1
- data/test/hexapdf/layout/test_image_box.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +18 -13
- data/test/hexapdf/layout/test_text_box.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +11 -6
- data/test/hexapdf/task/test_dereference.rb +2 -2
- data/test/hexapdf/task/test_optimize.rb +11 -11
- data/test/hexapdf/test_composer.rb +1 -1
- data/test/hexapdf/test_dictionary.rb +10 -2
- data/test/hexapdf/test_dictionary_fields.rb +27 -3
- data/test/hexapdf/test_document.rb +16 -15
- data/test/hexapdf/test_importer.rb +4 -4
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_pdf_array.rb +162 -0
- data/test/hexapdf/test_rectangle.rb +3 -5
- data/test/hexapdf/test_serializer.rb +1 -1
- data/test/hexapdf/test_stream.rb +1 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_field.rb +85 -0
- data/test/hexapdf/type/acro_form/test_form.rb +69 -0
- data/test/hexapdf/type/annotations/test_text.rb +2 -6
- data/test/hexapdf/type/annotations/test_widget.rb +24 -0
- data/test/hexapdf/type/test_annotation.rb +1 -1
- data/test/hexapdf/type/test_catalog.rb +1 -1
- data/test/hexapdf/type/test_cid_font.rb +3 -3
- data/test/hexapdf/type/test_font.rb +2 -2
- data/test/hexapdf/type/test_font_descriptor.rb +2 -1
- data/test/hexapdf/type/test_font_simple.rb +3 -3
- data/test/hexapdf/type/test_font_true_type.rb +6 -6
- data/test/hexapdf/type/test_font_type0.rb +5 -5
- data/test/hexapdf/type/test_font_type1.rb +8 -8
- data/test/hexapdf/type/test_font_type3.rb +4 -4
- data/test/hexapdf/type/test_image.rb +16 -12
- data/test/hexapdf/type/test_page.rb +11 -11
- data/test/hexapdf/type/test_page_tree_node.rb +20 -20
- data/test/hexapdf/type/test_resources.rb +6 -6
- data/test/hexapdf/type/test_trailer.rb +5 -2
- data/test/hexapdf/type/test_xref_stream.rb +1 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +35 -35
- metadata +23 -7
- data/test/hexapdf/type/annotations/test_link.rb +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d9c9409476d9ff9efaf2a8c0fb2b2623f660676c19e0d920659f6d719a783ee1
|
|
4
|
+
data.tar.gz: cb266922c5cdb6749e7a9954141e2b8b2094fcd44968bfa384e62478a184161c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c7d5fb9e817d5a4a8fc7ad3f26deb075afa9dd504fda9dd7801dabbb76bcf819734cb86918ecb1c553e767125edfd47672cfeacdd648afbda81ab198e02f96f
|
|
7
|
+
data.tar.gz: bafff7b14203d534cacf94fdfaf493657c05255fb1caf431fd0cc8b7ebdcfb4470d49a0d02a5d72c96fea51f0c4bc0fa6b9aaca89caf0a190e6879be1b49fbb3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
## 0.11.0 - 2019-11-19
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
* [HexaPDF::PDFArray] to wrap arrays and allow automatic resolution of
|
|
6
|
+
references like with [HexaPDF::Dictionary] - MAY BREAK THINGS!
|
|
7
|
+
* CLI command `hexapdf watermark` to apply a watermark PDF as background or
|
|
8
|
+
stamp onto another PDF file
|
|
9
|
+
* CLI command `hexapdf image2pdf` to convert images into a PDF file
|
|
10
|
+
* [HexaPDF::DictionaryFields::Field#allowed_values] to allow constraining a
|
|
11
|
+
field to certain allowed values
|
|
12
|
+
* [HexaPDF::Document::Fonts#configured_fonts] to return all font variants that
|
|
13
|
+
are configured and available for adding to a document
|
|
14
|
+
* [HexaPDF::Type::Annotations::Widget] and associated classes
|
|
15
|
+
* [HexaPDF::Type::AcroForm::Form] and [HexaPDF::Type::AcroForm::Field] for basic
|
|
16
|
+
AcroForm support
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
* Use Reline for interactive mode of `hexapdf inspect` if available
|
|
21
|
+
* [HexaPDF::DictionaryFields::Field::new] to use keyword arguments
|
|
22
|
+
* Update the field information for implemented PDF types to include the allowed
|
|
23
|
+
values where possible
|
|
24
|
+
* Interface of font loader objects to allow another method `available_fonts` for
|
|
25
|
+
returning all available fonts
|
|
26
|
+
* [HexaPDF::Layout::Style] to check for valid values where possible
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
* Line spacing of empty lines for [HexaPDF::Layout::TextLayouter]
|
|
31
|
+
* Handling of `/DecodeParms` when exporting to PNG images
|
|
32
|
+
|
|
33
|
+
|
|
1
34
|
## 0.10.0 - 2019-10-02
|
|
2
35
|
|
|
3
36
|
### Added
|
data/CONTRIBUTERS
CHANGED
data/Rakefile
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
1
2
|
require 'rake/testtask'
|
|
2
3
|
require 'rake/clean'
|
|
3
4
|
require 'rubygems/package_task'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
require 'hexapdf'
|
|
6
|
+
require_relative 'lib/hexapdf/version'
|
|
7
7
|
|
|
8
8
|
Rake::TestTask.new do |t|
|
|
9
9
|
t.libs << 'test'
|
|
@@ -13,19 +13,6 @@ Rake::TestTask.new do |t|
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
namespace :dev do
|
|
16
|
-
PKG_FILES = FileList.new([
|
|
17
|
-
'Rakefile',
|
|
18
|
-
'LICENSE', 'agpl-3.0.txt',
|
|
19
|
-
'README.md', 'CHANGELOG.md',
|
|
20
|
-
'VERSION', 'CONTRIBUTERS',
|
|
21
|
-
'bin/*',
|
|
22
|
-
'lib/**/*.rb',
|
|
23
|
-
'man/man1/hexapdf.1',
|
|
24
|
-
'data/**/*',
|
|
25
|
-
'examples/*',
|
|
26
|
-
'test/**/*'
|
|
27
|
-
])
|
|
28
|
-
|
|
29
16
|
CLOBBER << "man/man1/hexapdf.1"
|
|
30
17
|
file 'man/man1/hexapdf.1' => ['man/man1/hexapdf.1.md'] do
|
|
31
18
|
puts "Generating hexapdf man page"
|
|
@@ -35,7 +22,7 @@ namespace :dev do
|
|
|
35
22
|
CLOBBER << "VERSION"
|
|
36
23
|
file 'VERSION' do
|
|
37
24
|
puts "Generating VERSION file"
|
|
38
|
-
File.open('VERSION', 'w+') {|file| file.write(HexaPDF::VERSION + "\n")}
|
|
25
|
+
File.open('VERSION', 'w+') {|file| file.write(HexaPDF::VERSION + "\n") }
|
|
39
26
|
end
|
|
40
27
|
|
|
41
28
|
CLOBBER << 'CONTRIBUTERS'
|
|
@@ -46,35 +33,8 @@ namespace :dev do
|
|
|
46
33
|
`git log | grep ^Author: | sed 's/^Author: //' | sort | uniq -c | sort -nr >> CONTRIBUTERS`
|
|
47
34
|
end
|
|
48
35
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
s.version = HexaPDF::VERSION
|
|
52
|
-
s.summary = "HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby"
|
|
53
|
-
s.description = "HexaPDF is a pure Ruby library with an accompanying application for " \
|
|
54
|
-
"working with PDF files.\n\nIn short, it allows creating new PDF files, manipulating " \
|
|
55
|
-
"existing PDF files, merging multiple PDF files into one, extracting meta information, " \
|
|
56
|
-
"text, images and files from PDF files, securing PDF files by encrypting them and " \
|
|
57
|
-
"optimizing PDF files for smaller file size or other criteria.\n\nHexaPDF was designed " \
|
|
58
|
-
"with ease of use and performance in mind. It uses lazy loading and lazy computing when " \
|
|
59
|
-
"possible and tries to produce small PDF files by default."
|
|
60
|
-
s.license = 'AGPL-3.0'
|
|
61
|
-
|
|
62
|
-
s.files = PKG_FILES.to_a
|
|
63
|
-
|
|
64
|
-
s.require_path = 'lib'
|
|
65
|
-
s.executables = ['hexapdf']
|
|
66
|
-
s.add_dependency('cmdparse', '~> 3.0', '>= 3.0.3')
|
|
67
|
-
s.add_dependency('geom2d', '~> 0.2')
|
|
68
|
-
s.add_development_dependency('kramdown', '~> 1.0', '>= 1.13.0')
|
|
69
|
-
s.add_development_dependency('rubocop', '~> 0.58', '>= 0.58.2')
|
|
70
|
-
s.required_ruby_version = '>= 2.4'
|
|
71
|
-
|
|
72
|
-
s.author = 'Thomas Leitner'
|
|
73
|
-
s.email = 't_leitner@gmx.at'
|
|
74
|
-
s.homepage = "https://hexapdf.gettalong.org"
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
Gem::PackageTask.new(spec) do |pkg|
|
|
36
|
+
ENV['REAL_GEM'] = "true"
|
|
37
|
+
Gem::PackageTask.new(eval(File.read('hexapdf.gemspec'), binding, 'hexapdf.gemspec')) do |pkg|
|
|
78
38
|
pkg.need_zip = true
|
|
79
39
|
pkg.need_tar = true
|
|
80
40
|
end
|
|
@@ -88,11 +48,36 @@ namespace :dev do
|
|
|
88
48
|
desc 'Release HexaPDF version ' + HexaPDF::VERSION
|
|
89
49
|
task release: [:clobber, :package, :publish_files]
|
|
90
50
|
|
|
91
|
-
|
|
92
|
-
task :
|
|
93
|
-
puts "
|
|
94
|
-
|
|
95
|
-
|
|
51
|
+
desc "Set-up everything for development"
|
|
52
|
+
task :setup do
|
|
53
|
+
puts "Installing required runtime and development gems:"
|
|
54
|
+
resolver = Gem::Resolver.for_current_gems(spec.dependencies)
|
|
55
|
+
resolver.ignore_dependencies = true
|
|
56
|
+
resolver.soft_missing = true
|
|
57
|
+
resolver.resolve
|
|
58
|
+
spec.dependencies.each do |dependency|
|
|
59
|
+
if resolver.missing.find {|dep_request| dep_request.dependency == dependency }
|
|
60
|
+
print "✗ #{dependency.name} - installing it..."
|
|
61
|
+
Gem.install(dependency.name, dependency.requirement, prerelease: true)
|
|
62
|
+
puts " done"
|
|
63
|
+
else
|
|
64
|
+
puts "✓ #{dependency.name}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
puts
|
|
69
|
+
puts "The following binaries are needed for the tests:"
|
|
70
|
+
{
|
|
71
|
+
'pngtopnm' => 'sudo apt install netpbm',
|
|
72
|
+
'pngcheck' => 'sudo apt install pngcheck',
|
|
73
|
+
}.each do |name, install_command|
|
|
74
|
+
`which #{name} 2>&1`
|
|
75
|
+
if $?.exitstatus == 0
|
|
76
|
+
puts "✓ #{name}"
|
|
77
|
+
else
|
|
78
|
+
puts "✗ #{name} (#{install_command})"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
96
81
|
end
|
|
97
82
|
|
|
98
83
|
CODING_LINE = "# -*- encoding: utf-8; frozen_string_literal: true -*-\n"
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.11.0
|
data/lib/hexapdf/cli.rb
CHANGED
|
@@ -44,6 +44,8 @@ require 'hexapdf/cli/optimize'
|
|
|
44
44
|
require 'hexapdf/cli/images'
|
|
45
45
|
require 'hexapdf/cli/batch'
|
|
46
46
|
require 'hexapdf/cli/split'
|
|
47
|
+
require 'hexapdf/cli/watermark'
|
|
48
|
+
require 'hexapdf/cli/image2pdf'
|
|
47
49
|
require 'hexapdf/version'
|
|
48
50
|
require 'hexapdf/document'
|
|
49
51
|
|
|
@@ -95,6 +97,8 @@ module HexaPDF
|
|
|
95
97
|
add_command(HexaPDF::CLI::Merge.new)
|
|
96
98
|
add_command(HexaPDF::CLI::Batch.new)
|
|
97
99
|
add_command(HexaPDF::CLI::Split.new)
|
|
100
|
+
add_command(HexaPDF::CLI::Watermark.new)
|
|
101
|
+
add_command(HexaPDF::CLI::Image2PDF.new)
|
|
98
102
|
add_command(CmdParse::HelpCommand.new)
|
|
99
103
|
version_command = CmdParse::VersionCommand.new(add_switches: false)
|
|
100
104
|
add_command(version_command)
|
data/lib/hexapdf/cli/command.rb
CHANGED
|
@@ -57,7 +57,7 @@ module HexaPDF
|
|
|
57
57
|
|
|
58
58
|
include Extensions
|
|
59
59
|
|
|
60
|
-
def initialize(*args, &block) #:nodoc:
|
|
60
|
+
def initialize(*args, **kwargs, &block) #:nodoc:
|
|
61
61
|
super
|
|
62
62
|
@out_options = OpenStruct.new
|
|
63
63
|
@out_options.compact = true
|
|
@@ -82,7 +82,7 @@ module HexaPDF
|
|
|
82
82
|
# If +out_file+ is given, the document is written to it after yielding.
|
|
83
83
|
def with_document(file, password: nil, out_file: nil) #:yield: document
|
|
84
84
|
if file == out_file
|
|
85
|
-
doc = HexaPDF::Document.open(file, pdf_options(password))
|
|
85
|
+
doc = HexaPDF::Document.open(file, **pdf_options(password))
|
|
86
86
|
else
|
|
87
87
|
file_io = File.open(file, 'rb')
|
|
88
88
|
doc = HexaPDF::Document.new(io: file_io, **pdf_options(password))
|
|
@@ -143,6 +143,8 @@ module HexaPDF
|
|
|
143
143
|
#
|
|
144
144
|
# See: #out_options, #apply_optimization_options
|
|
145
145
|
def define_optimization_options
|
|
146
|
+
options.separator("")
|
|
147
|
+
options.separator("Optimization options:")
|
|
146
148
|
options.on("--[no-]compact", "Delete unnecessary PDF objects (default: " \
|
|
147
149
|
"#{@out_options.compact})") do |c|
|
|
148
150
|
@out_options.compact = c
|
|
@@ -176,6 +178,8 @@ module HexaPDF
|
|
|
176
178
|
#
|
|
177
179
|
# See: #out_options, #apply_encryption_options
|
|
178
180
|
def define_encryption_options
|
|
181
|
+
options.separator("")
|
|
182
|
+
options.separator("Encryption options:")
|
|
179
183
|
options.on("--decrypt", "Remove any encryption") do
|
|
180
184
|
@out_options.encryption = :remove
|
|
181
185
|
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
|
2
|
+
#
|
|
3
|
+
#--
|
|
4
|
+
# This file is part of HexaPDF.
|
|
5
|
+
#
|
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
+
# Copyright (C) 2014-2019 Thomas Leitner
|
|
8
|
+
#
|
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
16
|
+
#
|
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
20
|
+
# License for more details.
|
|
21
|
+
#
|
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
|
24
|
+
#
|
|
25
|
+
# The interactive user interfaces in modified source and object code
|
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
|
28
|
+
#
|
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
|
31
|
+
# is created or manipulated using HexaPDF.
|
|
32
|
+
#
|
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
|
35
|
+
#++
|
|
36
|
+
|
|
37
|
+
require 'hexapdf/cli/command'
|
|
38
|
+
|
|
39
|
+
module HexaPDF
|
|
40
|
+
module CLI
|
|
41
|
+
|
|
42
|
+
# Converts one or more images into a PDF file.
|
|
43
|
+
class Image2PDF < Command
|
|
44
|
+
|
|
45
|
+
def initialize #:nodoc:
|
|
46
|
+
super('image2pdf', takes_commands: false)
|
|
47
|
+
short_desc("Convert one or more images into a PDF file")
|
|
48
|
+
long_desc(<<~EOF)
|
|
49
|
+
This command converts one or more images into a single PDF file. The various options allow
|
|
50
|
+
setting a page size, scaling the images and defining margins.
|
|
51
|
+
EOF
|
|
52
|
+
|
|
53
|
+
options.on("-p", "--page-size SIZE", "The PDF page size. Either auto which chooses a " \
|
|
54
|
+
"size based on the image size or a valid page size like A4, A4-landscape " \
|
|
55
|
+
"or 595x842. Default: auto") do |page_size|
|
|
56
|
+
@media_box = case page_size
|
|
57
|
+
when 'auto'
|
|
58
|
+
:auto
|
|
59
|
+
when /(\d+(?:\.\d+)?)x(\d+(?:\.\d+)?)/
|
|
60
|
+
[0, 0, $1.to_f, $2.to_f]
|
|
61
|
+
else
|
|
62
|
+
orientation = :portrait
|
|
63
|
+
if page_size.end_with?('-landscape')
|
|
64
|
+
orientation = :landscape
|
|
65
|
+
page_size.delete_suffix!('-landscape')
|
|
66
|
+
end
|
|
67
|
+
HexaPDF::Type::Page.media_box(page_size.to_sym, orientation: orientation)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
options.on("--[no-]auto-rotate", "Automatically rotate pages based on image dimesions. " \
|
|
71
|
+
"Default: true") do |auto_rotate|
|
|
72
|
+
@auto_rotate = auto_rotate
|
|
73
|
+
end
|
|
74
|
+
options.on("-s", "--scale SCALE", Integer, "Defines how the images should be scaled. " \
|
|
75
|
+
"Either fit to fit the image to the page size or a number specifying the " \
|
|
76
|
+
"minimum pixels per inch. Default: fit") do |scale|
|
|
77
|
+
@scale = case scale
|
|
78
|
+
when 'fit' then :fit
|
|
79
|
+
else scale.to_f
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
options.on("-m", "--margins MARGINS", Array, "Defines the margins around the image, " \
|
|
83
|
+
"either with a single number or four numbers (top, right, bottom, left) " \
|
|
84
|
+
"separated by commas. Default: 0") do |margins|
|
|
85
|
+
@margins = case margins.size
|
|
86
|
+
when 1 then margins.map!(&:to_f) * 4
|
|
87
|
+
when 4 then margins.map!(&:to_f)
|
|
88
|
+
else
|
|
89
|
+
raise OptionParser::InvalidArgument, "#{margins.join(',')} (1 or 4 " \
|
|
90
|
+
"numbers needed)"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
define_optimization_options
|
|
94
|
+
define_encryption_options
|
|
95
|
+
|
|
96
|
+
@media_box = :auto
|
|
97
|
+
@auto_rotate = true
|
|
98
|
+
@scale = :fit
|
|
99
|
+
@margins = [0, 0, 0, 0]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def execute(out_file, *images) #:nodoc:
|
|
103
|
+
maybe_raise_on_existing_file(out_file)
|
|
104
|
+
|
|
105
|
+
out = HexaPDF::Document.new
|
|
106
|
+
|
|
107
|
+
images.each do |image_file|
|
|
108
|
+
image = out.images.add(image_file)
|
|
109
|
+
iw = image.info.width.to_f
|
|
110
|
+
ih = image.info.height.to_f
|
|
111
|
+
if @scale != :fit
|
|
112
|
+
iw *= 72 / @scale
|
|
113
|
+
ih *= 72 / @scale
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
media_box = (@media_box == :auto ? [0, 0, iw, ih] : @media_box.dup)
|
|
117
|
+
if @auto_rotate && (ih > iw) != (media_box[3] > media_box[2]) &&
|
|
118
|
+
(iw > media_box[2] || ih > media_box[3])
|
|
119
|
+
media_box[2], media_box[3] = media_box[3], media_box[2]
|
|
120
|
+
end
|
|
121
|
+
page = out.pages.add(media_box)
|
|
122
|
+
|
|
123
|
+
pw = page.box(:media).width.to_f - @margins[1] - @margins[3]
|
|
124
|
+
ph = page.box(:media).height.to_f - @margins[0] - @margins[2]
|
|
125
|
+
if @scale == :fit || iw > pw || ih > ph
|
|
126
|
+
ratio = [pw / iw, ph / ih].min
|
|
127
|
+
iw, ih = iw * ratio, ih * ratio
|
|
128
|
+
end
|
|
129
|
+
x, y = @margins[3] + (pw - iw) / 2, @margins[2] + (ph - ih) / 2
|
|
130
|
+
page.canvas.image(image, at: [x, y], width: iw, height: ih)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
apply_encryption_options(out)
|
|
134
|
+
apply_optimization_options(out)
|
|
135
|
+
write_document(out, out_file)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
end
|
|
141
|
+
end
|
data/lib/hexapdf/cli/info.rb
CHANGED
|
@@ -78,7 +78,7 @@ module HexaPDF
|
|
|
78
78
|
def output_info(file) # :nodoc:
|
|
79
79
|
options = pdf_options(@password)
|
|
80
80
|
options[:config]['document.auto_decrypt'] = @auto_decrypt
|
|
81
|
-
HexaPDF::Document.open(file, options) do |doc|
|
|
81
|
+
HexaPDF::Document.open(file, **options) do |doc|
|
|
82
82
|
output_line("File name", file)
|
|
83
83
|
output_line("File size", File.stat(file).size.to_s + " bytes")
|
|
84
84
|
@auto_decrypt && INFO_KEYS.each do |name|
|
data/lib/hexapdf/cli/inspect.rb
CHANGED
|
@@ -70,9 +70,17 @@ module HexaPDF
|
|
|
70
70
|
with_document(file, password: @password) do |doc|
|
|
71
71
|
@doc = doc
|
|
72
72
|
if commands.empty?
|
|
73
|
+
begin
|
|
74
|
+
require 'reline'
|
|
75
|
+
Reline.completion_proc = RELINE_COMPLETION_PROC
|
|
76
|
+
Reline.completion_append_character = " "
|
|
77
|
+
rescue LoadError
|
|
78
|
+
if command_parser.verbosity_info?
|
|
79
|
+
$stderr.puts("Library reline not available, history and line editing not available")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
73
82
|
while true
|
|
74
|
-
|
|
75
|
-
input = $stdin.gets
|
|
83
|
+
input = read_input
|
|
76
84
|
(puts; break) unless input
|
|
77
85
|
commands = input.scan(/(["'])(.+?)\1|(\S+)/).map {|a| a[1] || a[2] }
|
|
78
86
|
break if execute_commands(commands)
|
|
@@ -85,6 +93,28 @@ module HexaPDF
|
|
|
85
93
|
|
|
86
94
|
private
|
|
87
95
|
|
|
96
|
+
# :nodoc:
|
|
97
|
+
COMMAND_LIST = %w[object recursive stream raw-stream xref catalog trailer pages
|
|
98
|
+
page-count search quit help]
|
|
99
|
+
# :nodoc:
|
|
100
|
+
RELINE_COMPLETION_PROC = proc do |s|
|
|
101
|
+
if s.empty?
|
|
102
|
+
COMMAND_DESCRIPTIONS.map {|cmd, desc| cmd.ljust(35) << desc }
|
|
103
|
+
else
|
|
104
|
+
COMMAND_LIST.grep(/^#{Regexp.escape(s)}/)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Returns one line of input, using Reline if available.
|
|
109
|
+
def read_input
|
|
110
|
+
if Object.const_defined?("Reline")
|
|
111
|
+
Reline.readline("cmd> ", true)
|
|
112
|
+
else
|
|
113
|
+
print "cmd> "
|
|
114
|
+
$stdin.gets
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
88
118
|
def execute_commands(data) #:nodoc:
|
|
89
119
|
data.map! {|item| item == ";" ? nil : item }
|
|
90
120
|
until data.empty?
|
data/lib/hexapdf/cli/modify.rb
CHANGED
|
@@ -93,7 +93,7 @@ module HexaPDF
|
|
|
93
93
|
# Arranges the pages of the document as specified with the --pages option.
|
|
94
94
|
def arrange_pages(doc)
|
|
95
95
|
all_pages = doc.pages.to_a
|
|
96
|
-
new_page_tree = doc.add(Type: :Pages)
|
|
96
|
+
new_page_tree = doc.add({Type: :Pages})
|
|
97
97
|
parse_pages_specification(@pages, all_pages.length).each do |index, rotation|
|
|
98
98
|
page = all_pages[index]
|
|
99
99
|
page.value.update(page.copy_inherited_values)
|
data/lib/hexapdf/cli/optimize.rb
CHANGED
|
@@ -81,7 +81,7 @@ module HexaPDF
|
|
|
81
81
|
|
|
82
82
|
# Optimizes the page tree by flattening it and deleting unsed objects.
|
|
83
83
|
def optimize_page_tree(doc)
|
|
84
|
-
page_tree = doc.add(Type: :Pages)
|
|
84
|
+
page_tree = doc.add({Type: :Pages})
|
|
85
85
|
retained = {page_tree.data => true}
|
|
86
86
|
doc.pages.each do |page|
|
|
87
87
|
page.value.update(page.copy_inherited_values)
|