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
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
# Uses one or more pages of one PDF and underlays/overlays it/them onto another.
|
|
43
|
+
class Watermark < Command
|
|
44
|
+
|
|
45
|
+
def initialize #:nodoc:
|
|
46
|
+
super('watermark', takes_commands: false)
|
|
47
|
+
short_desc("Put one or more PDF pages onto another PDF")
|
|
48
|
+
long_desc(<<~EOF)
|
|
49
|
+
This command uses one ore more pages from a PDF file and applies them as background or
|
|
50
|
+
stamp on another PDF file.
|
|
51
|
+
|
|
52
|
+
If multiple pages are selected from the watermark PDF, the --repeat option can be used to
|
|
53
|
+
specify how they should be applied: 'last' (the default) will only repeat the last
|
|
54
|
+
watermark page whereas 'all' will cyclically repeat all watermark pages.
|
|
55
|
+
EOF
|
|
56
|
+
|
|
57
|
+
options.on("-w", "--watermark-file FILE", "The PDF used as watermark") do |watermark_file|
|
|
58
|
+
@watermark_file = watermark_file
|
|
59
|
+
end
|
|
60
|
+
options.on("-i", "--pages PAGES", "The pages of the watermark file that should be used " \
|
|
61
|
+
"(default: 1)") do |pages|
|
|
62
|
+
@pages = pages
|
|
63
|
+
end
|
|
64
|
+
options.on("-r", "--repeat REPEAT_MODE", [:last, :all],
|
|
65
|
+
"Specifies how the watermark pages should be repeated. Either last or " \
|
|
66
|
+
"all (default: last)") do |repeat|
|
|
67
|
+
@repeat = repeat
|
|
68
|
+
end
|
|
69
|
+
options.on("-t", "--type WATERMARK_TYPE", [:background, :stamp],
|
|
70
|
+
"Specifies how the watermark is applied: background applies it below the page " \
|
|
71
|
+
"contents and stamp applies it above. Default: background") do |type|
|
|
72
|
+
@type = (type == :background ? :underlay : :overlay)
|
|
73
|
+
end
|
|
74
|
+
options.on("--password PASSWORD", "-p", String,
|
|
75
|
+
"The password for decrypting the input PDF. Use - for reading from " \
|
|
76
|
+
"standard input.") do |pwd|
|
|
77
|
+
@password = (pwd == '-' ? read_password : pwd)
|
|
78
|
+
end
|
|
79
|
+
define_optimization_options
|
|
80
|
+
define_encryption_options
|
|
81
|
+
|
|
82
|
+
@watermark_file = nil
|
|
83
|
+
@pages = "1"
|
|
84
|
+
@repeat = :last
|
|
85
|
+
@type = :underlay
|
|
86
|
+
@password = nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def execute(in_file, out_file) #:nodoc:
|
|
90
|
+
maybe_raise_on_existing_file(out_file)
|
|
91
|
+
watermark = HexaPDF::Document.open(@watermark_file)
|
|
92
|
+
indices = page_index_generator(watermark)
|
|
93
|
+
xobject_map = {}
|
|
94
|
+
with_document(in_file, password: @password, out_file: out_file) do |doc|
|
|
95
|
+
doc.pages.each do |page|
|
|
96
|
+
index = indices.next
|
|
97
|
+
xobject = xobject_map[index] ||= doc.import(watermark.pages[index].to_form_xobject)
|
|
98
|
+
pw = page.box(:media).width.to_f
|
|
99
|
+
ph = page.box(:media).height.to_f
|
|
100
|
+
xw = xobject.width.to_f
|
|
101
|
+
xh = xobject.height.to_f
|
|
102
|
+
canvas = page.canvas(type: @type)
|
|
103
|
+
ratio = [pw / xw, ph / xh].min
|
|
104
|
+
xw, xh = xw * ratio, xh * ratio
|
|
105
|
+
x, y = (pw - xw) / 2, (ph - xh) / 2
|
|
106
|
+
canvas.xobject(xobject, at: [x, y], width: xw, height: xh)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
# Returns an Enumerator instance that returns the indices of the watermark pages that should
|
|
114
|
+
# be used.
|
|
115
|
+
def page_index_generator(watermark)
|
|
116
|
+
pages = parse_pages_specification(@pages, watermark.pages.count)
|
|
117
|
+
Enumerator.new do |y|
|
|
118
|
+
loop do
|
|
119
|
+
pages.each {|index, _rotation| y << index }
|
|
120
|
+
if @repeat == :last
|
|
121
|
+
y << pages.last[0] while true
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
end
|
|
130
|
+
end
|
data/lib/hexapdf/composer.rb
CHANGED
|
@@ -93,7 +93,7 @@ module HexaPDF
|
|
|
93
93
|
# ...
|
|
94
94
|
# end
|
|
95
95
|
def self.create(output, **options, &block)
|
|
96
|
-
new(options, &block).write(output)
|
|
96
|
+
new(**options, &block).write(output)
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
# The PDF document that is created.
|
|
@@ -295,7 +295,7 @@ module HexaPDF
|
|
|
295
295
|
# options to make it work in all cases.
|
|
296
296
|
def update_style(style, options = {})
|
|
297
297
|
style ||= base_style
|
|
298
|
-
style = style.dup.update(options) unless options.empty?
|
|
298
|
+
style = style.dup.update(**options) unless options.empty?
|
|
299
299
|
style.font(base_style.font) unless style.font?
|
|
300
300
|
style.font(@document.fonts.add(style.font)) unless style.font.respond_to?(:dict)
|
|
301
301
|
style
|
|
@@ -199,7 +199,7 @@ module HexaPDF
|
|
|
199
199
|
# {"font_name": {variant: file_name, variant2: file_name2, ...}, ...}
|
|
200
200
|
#
|
|
201
201
|
# Once a font is registered in this way, the font name together with a variant name can be used
|
|
202
|
-
# with the HexaPDF::Document::Fonts#
|
|
202
|
+
# with the HexaPDF::Document::Fonts#add method to load the font.
|
|
203
203
|
#
|
|
204
204
|
# For best compatibility, the following variant names should be used:
|
|
205
205
|
#
|
|
@@ -447,6 +447,10 @@ module HexaPDF
|
|
|
447
447
|
Action: 'HexaPDF::Type::Action',
|
|
448
448
|
XXLaunchActionWinParameters: 'HexaPDF::Type::Actions::Launch::WinParameters',
|
|
449
449
|
Annot: 'HexaPDF::Type::Annotation',
|
|
450
|
+
XXAppearanceCharacteristics: 'HexaPDF::Type::Annotations::Widget::AppearanceCharacteristics',
|
|
451
|
+
XXIconFit: 'HexaPDF::Type::IconFit',
|
|
452
|
+
XXAcroForm: 'HexaPDF::Type::AcroForm::Form',
|
|
453
|
+
XXAcroFormField: 'HexaPDF::Type::AcroForm::Field',
|
|
450
454
|
},
|
|
451
455
|
'object.subtype_map' => {
|
|
452
456
|
nil => {
|
|
@@ -463,6 +467,7 @@ module HexaPDF
|
|
|
463
467
|
URI: 'HexaPDF::Type::Actions::URI',
|
|
464
468
|
Text: 'HexaPDF::Type::Annotations::Text',
|
|
465
469
|
Link: 'HexaPDF::Type::Annotations::Link',
|
|
470
|
+
Widget: 'HexaPDF::Type::Annotations::Widget',
|
|
466
471
|
},
|
|
467
472
|
XObject: {
|
|
468
473
|
Image: 'HexaPDF::Type::Image',
|
|
@@ -485,6 +490,7 @@ module HexaPDF
|
|
|
485
490
|
Annot: {
|
|
486
491
|
Text: 'HexaPDF::Type::Annotations::Text',
|
|
487
492
|
Link: 'HexaPDF::Type::Annotations::Link',
|
|
493
|
+
Widget: 'HexaPDF::Type::Annotations::Widget',
|
|
488
494
|
},
|
|
489
495
|
})
|
|
490
496
|
|
|
@@ -1088,7 +1088,7 @@ module HexaPDF
|
|
|
1088
1088
|
unless obj.respond_to?(:configure)
|
|
1089
1089
|
obj = context.document.config.constantize('graphic_object.map', obj)
|
|
1090
1090
|
end
|
|
1091
|
-
obj = obj.configure(options) unless options.empty? && obj.respond_to?(:draw)
|
|
1091
|
+
obj = obj.configure(**options) unless options.empty? && obj.respond_to?(:draw)
|
|
1092
1092
|
obj
|
|
1093
1093
|
end
|
|
1094
1094
|
|
|
@@ -1607,7 +1607,7 @@ module HexaPDF
|
|
|
1607
1607
|
# See: PDF1.7 s9.2.2
|
|
1608
1608
|
def font(name = nil, size: nil, **options)
|
|
1609
1609
|
if name
|
|
1610
|
-
@font = (name.respond_to?(:dict) ? name : context.document.fonts.add(name, options))
|
|
1610
|
+
@font = (name.respond_to?(:dict) ? name : context.document.fonts.add(name, **options))
|
|
1611
1611
|
if size
|
|
1612
1612
|
font_size(size)
|
|
1613
1613
|
else
|
|
@@ -54,7 +54,7 @@ module HexaPDF
|
|
|
54
54
|
#
|
|
55
55
|
# See #configure for the allowed keyword arguments.
|
|
56
56
|
def self.configure(**kwargs)
|
|
57
|
-
new.configure(kwargs)
|
|
57
|
+
new.configure(**kwargs)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# The maximal number of curves used for approximating a complete ellipse.
|
|
@@ -162,7 +162,7 @@ module HexaPDF
|
|
|
162
162
|
def draw(canvas, move_to_start: true)
|
|
163
163
|
@max_curves = canvas.context.document.config['graphic_object.arc.max_curves']
|
|
164
164
|
canvas.move_to(*start_point) if move_to_start
|
|
165
|
-
curves.each {|
|
|
165
|
+
curves.each {|x, y, hash| canvas.curve_to(x, y, **hash) }
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
# Returns an array of arrays that contain the points for the Bezier curves which are used
|
|
@@ -54,7 +54,7 @@ module HexaPDF
|
|
|
54
54
|
#
|
|
55
55
|
# See #configure for the allowed keyword arguments.
|
|
56
56
|
def self.configure(**kwargs)
|
|
57
|
-
new.configure(kwargs)
|
|
57
|
+
new.configure(**kwargs)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# x-coordinate of endpoint
|
|
@@ -133,7 +133,7 @@ module HexaPDF
|
|
|
133
133
|
canvas.line_to(@x, @y)
|
|
134
134
|
else
|
|
135
135
|
values = compute_arc_values(x1, y1)
|
|
136
|
-
arc = canvas.graphic_object(:arc, values)
|
|
136
|
+
arc = canvas.graphic_object(:arc, **values)
|
|
137
137
|
arc.draw(canvas, move_to_start: false)
|
|
138
138
|
end
|
|
139
139
|
end
|
data/lib/hexapdf/dictionary.rb
CHANGED
|
@@ -70,7 +70,7 @@ module HexaPDF
|
|
|
70
70
|
# String:: String (for text strings), PDFByteString (for binary strings)
|
|
71
71
|
# Date:: PDFDate
|
|
72
72
|
# Name:: Symbol
|
|
73
|
-
# Array:: Array
|
|
73
|
+
# Array:: PDFArray or Array
|
|
74
74
|
# Dictionary:: Dictionary (or any subclass) or Hash
|
|
75
75
|
# Stream:: Stream (or any subclass)
|
|
76
76
|
# Null:: NilClass
|
|
@@ -93,11 +93,14 @@ module HexaPDF
|
|
|
93
93
|
# to be an indirect object (+true+), a direct object (+false+) or if it doesn't
|
|
94
94
|
# matter (unspecified or +nil+).
|
|
95
95
|
#
|
|
96
|
+
# allowed_values:: An array of allowed values for this field.
|
|
97
|
+
#
|
|
96
98
|
# version:: Specifies the minimum version of the PDF specification needed for this value.
|
|
97
99
|
def self.define_field(name, type:, required: false, default: nil, indirect: nil,
|
|
98
|
-
version: '1.2')
|
|
100
|
+
allowed_values: nil, version: '1.2')
|
|
99
101
|
@fields ||= {}
|
|
100
|
-
@fields[name] = Field.new(type, required, default, indirect,
|
|
102
|
+
@fields[name] = Field.new(type, required: required, default: default, indirect: indirect,
|
|
103
|
+
allowed_values: allowed_values, version: version)
|
|
101
104
|
end
|
|
102
105
|
|
|
103
106
|
# Returns the field entry for the given field name.
|
|
@@ -296,6 +299,11 @@ module HexaPDF
|
|
|
296
299
|
end
|
|
297
300
|
end
|
|
298
301
|
|
|
302
|
+
# Check the value of the field against the allowed values.
|
|
303
|
+
if field.allowed_values && !field.allowed_values.include?(obj)
|
|
304
|
+
yield("Field #{name} does not contain an allowed value")
|
|
305
|
+
end
|
|
306
|
+
|
|
299
307
|
# Check if field value needs to be (in)direct
|
|
300
308
|
unless field.indirect.nil?
|
|
301
309
|
obj = value[name] # we need the unwrapped object!
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
require 'time'
|
|
38
38
|
require 'date'
|
|
39
39
|
require 'hexapdf/object'
|
|
40
|
+
require 'hexapdf/pdf_array'
|
|
40
41
|
require 'hexapdf/rectangle'
|
|
41
42
|
require 'hexapdf/configuration'
|
|
42
43
|
require 'hexapdf/utils/pdf_doc_encoding'
|
|
@@ -101,6 +102,10 @@ module HexaPDF
|
|
|
101
102
|
# needs to be a direct object or +nil+ if it can be either.
|
|
102
103
|
attr_reader :indirect
|
|
103
104
|
|
|
105
|
+
# Returns an array with the allowed values for this field, or +nil+ if the values are not
|
|
106
|
+
# constrained.
|
|
107
|
+
attr_reader :allowed_values
|
|
108
|
+
|
|
104
109
|
# Returns the PDF version that is required for this field.
|
|
105
110
|
attr_reader :version
|
|
106
111
|
|
|
@@ -108,10 +113,12 @@ module HexaPDF
|
|
|
108
113
|
#
|
|
109
114
|
# Depending on the +type+ entry an appropriate field converter object is chosen from the
|
|
110
115
|
# available converters.
|
|
111
|
-
def initialize(type, required
|
|
116
|
+
def initialize(type, required: false, default: nil, indirect: nil, allowed_values: nil,
|
|
117
|
+
version: nil)
|
|
112
118
|
@type = [type].flatten
|
|
113
119
|
@type_mapped = false
|
|
114
120
|
@required, @default, @indirect, @version = required, default, indirect, version
|
|
121
|
+
@allowed_values = allowed_values && [allowed_values].flatten
|
|
115
122
|
@converters = @type.map {|t| self.class.converter_for(t) }.compact
|
|
116
123
|
end
|
|
117
124
|
|
|
@@ -199,6 +206,27 @@ module HexaPDF
|
|
|
199
206
|
|
|
200
207
|
end
|
|
201
208
|
|
|
209
|
+
# Converter module for fields of type PDFArray.
|
|
210
|
+
module ArrayConverter
|
|
211
|
+
|
|
212
|
+
# This converter is usable if the +type+ is PDFArray.
|
|
213
|
+
def self.usable_for?(type)
|
|
214
|
+
type == PDFArray
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# PDFArray fields can also contain simple arrays.
|
|
218
|
+
def self.additional_types
|
|
219
|
+
Array
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Wraps a given array in the PDFArray class. Otherwise returns +nil+.
|
|
223
|
+
def self.convert(data, _type, document)
|
|
224
|
+
return unless data.kind_of?(Array)
|
|
225
|
+
document.wrap(data, type: PDFArray)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
end
|
|
229
|
+
|
|
202
230
|
# Converter module for string fields to automatically convert a string into UTF-8 encoding.
|
|
203
231
|
module StringConverter
|
|
204
232
|
|
|
@@ -329,8 +357,9 @@ module HexaPDF
|
|
|
329
357
|
|
|
330
358
|
end
|
|
331
359
|
|
|
332
|
-
Field.converters.replace([FileSpecificationConverter, DictionaryConverter,
|
|
333
|
-
PDFByteStringConverter, DateConverter,
|
|
360
|
+
Field.converters.replace([FileSpecificationConverter, DictionaryConverter, ArrayConverter,
|
|
361
|
+
StringConverter, PDFByteStringConverter, DateConverter,
|
|
362
|
+
RectangleConverter])
|
|
334
363
|
|
|
335
364
|
end
|
|
336
365
|
|
data/lib/hexapdf/document.rb
CHANGED
|
@@ -40,6 +40,7 @@ require 'hexapdf/content'
|
|
|
40
40
|
require 'hexapdf/configuration'
|
|
41
41
|
require 'hexapdf/reference'
|
|
42
42
|
require 'hexapdf/object'
|
|
43
|
+
require 'hexapdf/pdf_array'
|
|
43
44
|
require 'hexapdf/stream'
|
|
44
45
|
require 'hexapdf/revisions'
|
|
45
46
|
require 'hexapdf/type'
|
|
@@ -135,7 +136,7 @@ module HexaPDF
|
|
|
135
136
|
|
|
136
137
|
@revisions = Revisions.from_io(self, io)
|
|
137
138
|
@security_handler = if encrypted? && @config['document.auto_decrypt']
|
|
138
|
-
Encryption::SecurityHandler.set_up_decryption(self, decryption_opts)
|
|
139
|
+
Encryption::SecurityHandler.set_up_decryption(self, **decryption_opts)
|
|
139
140
|
else
|
|
140
141
|
nil
|
|
141
142
|
end
|
|
@@ -199,7 +200,7 @@ module HexaPDF
|
|
|
199
200
|
# If the +revision+ option is +:current+, the current revision is used. Otherwise +revision+
|
|
200
201
|
# should be a revision index.
|
|
201
202
|
def add(obj, revision: :current, **wrap_opts)
|
|
202
|
-
obj = wrap(obj, wrap_opts) unless obj.kind_of?(HexaPDF::Object)
|
|
203
|
+
obj = wrap(obj, **wrap_opts) unless obj.kind_of?(HexaPDF::Object)
|
|
203
204
|
|
|
204
205
|
revision = (revision == :current ? @revisions.current : @revisions.revision(revision))
|
|
205
206
|
if revision.nil?
|
|
@@ -294,7 +295,8 @@ module HexaPDF
|
|
|
294
295
|
# prevent invalid mappings when only partial knowledge (:Type key is missing) is available.
|
|
295
296
|
#
|
|
296
297
|
# * If there is no valid class after the above steps, HexaPDF::Stream is used if a stream is
|
|
297
|
-
# given, HexaPDF::Dictionary if the given
|
|
298
|
+
# given, HexaPDF::Dictionary if the given object is a hash, HexaPDF::PDFArray if it is an
|
|
299
|
+
# array or else HexaPDF::Object is used.
|
|
298
300
|
#
|
|
299
301
|
# Options:
|
|
300
302
|
#
|
|
@@ -346,6 +348,8 @@ module HexaPDF
|
|
|
346
348
|
HexaPDF::Stream
|
|
347
349
|
elsif data.value.kind_of?(Hash)
|
|
348
350
|
HexaPDF::Dictionary
|
|
351
|
+
elsif data.value.kind_of?(Array)
|
|
352
|
+
HexaPDF::PDFArray
|
|
349
353
|
else
|
|
350
354
|
HexaPDF::Object
|
|
351
355
|
end
|
|
@@ -83,7 +83,7 @@ module HexaPDF
|
|
|
83
83
|
raise ArgumentError, "The name argument is mandatory when given an IO object"
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
spec = @document.add(Type: :Filespec)
|
|
86
|
+
spec = @document.add({Type: :Filespec})
|
|
87
87
|
spec.path = name
|
|
88
88
|
spec[:Desc] = description if description
|
|
89
89
|
spec.embed(file_or_io, name: name, register: true) if embed || !file_or_io.kind_of?(String)
|
|
@@ -70,11 +70,31 @@ module HexaPDF
|
|
|
70
70
|
if font
|
|
71
71
|
@loaded_fonts_cache[[name, options]] = font
|
|
72
72
|
else
|
|
73
|
+
font_list = configured_fonts.sort.map do |font_name, variants|
|
|
74
|
+
"#{font_name} (#{variants.join(', ')})"
|
|
75
|
+
end.join(', ')
|
|
73
76
|
raise HexaPDF::Error, "The requested font '#{name}' in variant '#{options[:variant]}' " \
|
|
74
|
-
"couldn't be found"
|
|
77
|
+
"couldn't be found. Configured fonts: #{font_list}"
|
|
75
78
|
end
|
|
76
79
|
end
|
|
77
80
|
|
|
81
|
+
# Returns a hash of the form 'font_name => [variants, ...]' with all the fonts that are
|
|
82
|
+
# configured. These fonts can be added to the document by using the #add method.
|
|
83
|
+
def configured_fonts
|
|
84
|
+
result = {}
|
|
85
|
+
each_font_loader do |loader|
|
|
86
|
+
next unless loader.respond_to?(:available_fonts)
|
|
87
|
+
loader.available_fonts(@document).each do |name, variants|
|
|
88
|
+
if result.key?(name)
|
|
89
|
+
result[name].concat(variants).uniq!
|
|
90
|
+
else
|
|
91
|
+
result[name] = variants
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
result
|
|
96
|
+
end
|
|
97
|
+
|
|
78
98
|
private
|
|
79
99
|
|
|
80
100
|
# :call-seq:
|