hexapdf 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|