hexapdf 0.42.0 → 0.44.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 +46 -0
- data/Rakefile +1 -1
- data/examples/030-pdfa.rb +1 -0
- data/lib/hexapdf/composer.rb +1 -0
- data/lib/hexapdf/dictionary.rb +3 -3
- data/lib/hexapdf/document/files.rb +7 -2
- data/lib/hexapdf/document/metadata.rb +12 -1
- data/lib/hexapdf/document.rb +14 -1
- data/lib/hexapdf/encryption.rb +17 -0
- data/lib/hexapdf/layout/box.rb +161 -61
- data/lib/hexapdf/layout/box_fitter.rb +4 -3
- data/lib/hexapdf/layout/column_box.rb +23 -25
- data/lib/hexapdf/layout/container_box.rb +3 -3
- data/lib/hexapdf/layout/frame.rb +13 -95
- data/lib/hexapdf/layout/image_box.rb +4 -4
- data/lib/hexapdf/layout/line.rb +4 -0
- data/lib/hexapdf/layout/list_box.rb +12 -20
- data/lib/hexapdf/layout/style.rb +5 -1
- data/lib/hexapdf/layout/table_box.rb +48 -55
- data/lib/hexapdf/layout/text_box.rb +38 -39
- data/lib/hexapdf/parser.rb +23 -17
- data/lib/hexapdf/type/acro_form/form.rb +78 -27
- data/lib/hexapdf/type/file_specification.rb +9 -5
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_files.rb +5 -0
- data/test/hexapdf/document/test_metadata.rb +21 -0
- data/test/hexapdf/layout/test_box.rb +82 -37
- data/test/hexapdf/layout/test_box_fitter.rb +10 -3
- data/test/hexapdf/layout/test_column_box.rb +7 -13
- data/test/hexapdf/layout/test_container_box.rb +1 -1
- data/test/hexapdf/layout/test_frame.rb +0 -48
- data/test/hexapdf/layout/test_image_box.rb +14 -6
- data/test/hexapdf/layout/test_list_box.rb +25 -26
- data/test/hexapdf/layout/test_table_box.rb +39 -53
- data/test/hexapdf/layout/test_text_box.rb +65 -67
- data/test/hexapdf/test_composer.rb +6 -0
- data/test/hexapdf/test_dictionary.rb +6 -4
- data/test/hexapdf/test_parser.rb +20 -0
- data/test/hexapdf/type/acro_form/test_form.rb +63 -2
- data/test/hexapdf/type/test_file_specification.rb +2 -1
- metadata +2 -2
@@ -43,6 +43,11 @@ module HexaPDF
|
|
43
43
|
# objects of a Frame.
|
44
44
|
#
|
45
45
|
# This class uses TextLayouter behind the scenes to do the hard work.
|
46
|
+
#
|
47
|
+
# == Used Box Properties
|
48
|
+
#
|
49
|
+
# The spacing after the last line can be controlled via the style property +last_line_gap+. Also
|
50
|
+
# see TextLayouter#style for other style properties taken into account.
|
46
51
|
class TextBox < Box
|
47
52
|
|
48
53
|
# Creates a new TextBox object with the given inline items (e.g. TextFragment and InlineBox
|
@@ -52,6 +57,7 @@ module HexaPDF
|
|
52
57
|
@tl = TextLayouter.new(style)
|
53
58
|
@items = items
|
54
59
|
@result = nil
|
60
|
+
@x_offset = 0
|
55
61
|
end
|
56
62
|
|
57
63
|
# Returns the text that will be drawn.
|
@@ -66,21 +72,27 @@ module HexaPDF
|
|
66
72
|
true
|
67
73
|
end
|
68
74
|
|
75
|
+
# :nodoc:
|
76
|
+
def draw(canvas, x, y)
|
77
|
+
super(canvas, x + @x_offset, y)
|
78
|
+
end
|
79
|
+
|
80
|
+
# :nodoc:
|
81
|
+
def empty?
|
82
|
+
super && (!@result || @result.lines.empty?)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
69
87
|
# Fits the text box into the Frame.
|
70
88
|
#
|
71
89
|
# Depending on the 'position' style property, the text is either fit into the current region
|
72
90
|
# of the frame using +available_width+ and +available_height+, or fit to the shape of the
|
73
91
|
# frame starting from the top (when 'position' is set to :flow).
|
74
|
-
|
75
|
-
# The spacing after the last line can be controlled via the style property +last_line_gap+.
|
76
|
-
#
|
77
|
-
# Also see TextLayouter#style for other style properties taken into account.
|
78
|
-
def fit(available_width, available_height, frame)
|
79
|
-
return false if (@initial_width > 0 && @initial_width > available_width) ||
|
80
|
-
(@initial_height > 0 && @initial_height > available_height)
|
81
|
-
|
92
|
+
def fit_content(available_width, available_height, frame)
|
82
93
|
frame = frame.child_frame(box: self)
|
83
|
-
@width = @height = 0
|
94
|
+
@width = @x_offset = @height = 0
|
95
|
+
|
84
96
|
@result = if style.position == :flow
|
85
97
|
@tl.fit(@items, frame.width_specification, frame.shape.bbox.height,
|
86
98
|
apply_first_text_indent: !split_box?, frame: frame)
|
@@ -91,8 +103,17 @@ module HexaPDF
|
|
91
103
|
height = (@initial_height > 0 ? @initial_height : available_height) - @height
|
92
104
|
@tl.fit(@items, width, height, apply_first_text_indent: !split_box?, frame: frame)
|
93
105
|
end
|
106
|
+
|
94
107
|
@width += if @initial_width > 0 || style.text_align == :center || style.text_align == :right
|
95
108
|
width
|
109
|
+
elsif style.position == :flow
|
110
|
+
min_x = +Float::INFINITY
|
111
|
+
max_x = -Float::INFINITY
|
112
|
+
@result.lines.each do |line|
|
113
|
+
min_x = [min_x, line.x_offset].min
|
114
|
+
max_x = [max_x, line.x_offset + line.width].max
|
115
|
+
end
|
116
|
+
min_x.finite? ? (@x_offset = min_x; max_x - min_x) : 0
|
96
117
|
else
|
97
118
|
@result.lines.max_by(&:width)&.width || 0
|
98
119
|
end
|
@@ -105,44 +126,22 @@ module HexaPDF
|
|
105
126
|
@height += style.line_spacing.gap(@result.lines.last, @result.lines.last)
|
106
127
|
end
|
107
128
|
|
108
|
-
@result.status == :success
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
# Splits the text box into two boxes if necessary and possible.
|
113
|
-
def split(available_width, available_height, frame)
|
114
|
-
fit(available_width, available_height, frame) unless @result
|
115
|
-
|
116
|
-
if style.position != :flow && (float_compare(@width, available_width) > 0 ||
|
117
|
-
float_compare(@height, available_height) > 0)
|
118
|
-
[nil, self]
|
119
|
-
elsif @result.remaining_items.empty?
|
120
|
-
[self]
|
121
|
-
elsif @result.lines.empty?
|
122
|
-
[nil, self]
|
123
|
-
else
|
124
|
-
[self, create_box_for_remaining_items]
|
129
|
+
if @result.status == :success
|
130
|
+
fit_result.success!
|
131
|
+
elsif @result.status == :height && !@result.lines.empty?
|
132
|
+
fit_result.overflow!
|
125
133
|
end
|
126
134
|
end
|
127
135
|
|
128
|
-
#
|
129
|
-
def
|
130
|
-
|
136
|
+
# Splits the text box into two.
|
137
|
+
def split_content
|
138
|
+
[self, create_box_for_remaining_items]
|
131
139
|
end
|
132
140
|
|
133
|
-
private
|
134
|
-
|
135
141
|
# Draws the text into the box.
|
136
142
|
def draw_content(canvas, x, y)
|
137
|
-
return unless @result
|
138
|
-
|
139
|
-
if @result.status == :height && @initial_height > 0 && style.overflow == :error
|
140
|
-
raise HexaPDF::Error, "Text doesn't fit into box with limited height and " \
|
141
|
-
"style property overflow is set to :error"
|
142
|
-
end
|
143
|
-
|
144
143
|
return if @result.lines.empty?
|
145
|
-
@result.draw(canvas, x, y + content_height)
|
144
|
+
@result.draw(canvas, x - @x_offset, y + content_height)
|
146
145
|
end
|
147
146
|
|
148
147
|
# Creates a new TextBox instance for the items remaining after fitting the box.
|
data/lib/hexapdf/parser.rb
CHANGED
@@ -362,29 +362,35 @@ module HexaPDF
|
|
362
362
|
pos = @io.pos
|
363
363
|
lines = @io.read(step_size + 40).split(/[\r\n]+/)
|
364
364
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
365
|
+
# Need to iterate through the whole lines array in case there are multiple %%EOF to try
|
366
|
+
eof_index = 0
|
367
|
+
while (eof_index = lines[0..(eof_index - 1)].rindex {|l| l.strip == '%%EOF' })
|
368
|
+
if eof_index > 0 && lines[eof_index - 1].strip =~ /\Astartxref\s(\d+)\z/
|
369
|
+
startxref_offset = $1.to_i
|
370
|
+
startxref_mangled = true
|
371
|
+
break # we found it even if it the syntax is not entirely correct
|
372
|
+
elsif eof_index < 2
|
373
|
+
startxref_missing = true
|
374
|
+
break
|
375
|
+
elsif lines[eof_index - 2].strip != "startxref"
|
376
|
+
startxref_missing = true
|
377
|
+
else
|
378
|
+
startxref_offset = lines[eof_index - 1].to_i
|
379
|
+
break # we found it
|
380
|
+
end
|
377
381
|
end
|
382
|
+
eof_not_found ||= !eof_index
|
383
|
+
break if startxref_offset
|
378
384
|
end
|
379
385
|
|
380
|
-
if
|
381
|
-
maybe_raise("PDF file trailer with end-of-file marker not found", pos: pos,
|
382
|
-
force: !eof_index)
|
383
|
-
elsif startxref_mangled
|
386
|
+
if startxref_mangled
|
384
387
|
maybe_raise("PDF file trailer keyword startxref on same line as value", pos: pos)
|
385
388
|
elsif startxref_missing
|
386
389
|
maybe_raise("PDF file trailer is missing startxref keyword", pos: pos,
|
387
|
-
force:
|
390
|
+
force: !startxref_offset)
|
391
|
+
elsif eof_not_found
|
392
|
+
maybe_raise("PDF file trailer with end-of-file marker not found", pos: pos,
|
393
|
+
force: !startxref_offset)
|
388
394
|
end
|
389
395
|
|
390
396
|
@startxref_offset = startxref_offset
|
@@ -163,10 +163,21 @@ module HexaPDF
|
|
163
163
|
field
|
164
164
|
end
|
165
165
|
|
166
|
+
# Creates an untyped namespace field for creating hierarchies.
|
167
|
+
#
|
168
|
+
# Example:
|
169
|
+
#
|
170
|
+
# form.create_namespace_field('text')
|
171
|
+
# form.create_text_field('text.a1')
|
172
|
+
def create_namespace_field(name)
|
173
|
+
create_field(name)
|
174
|
+
end
|
175
|
+
|
166
176
|
# Creates a new text field with the given name and adds it to the form.
|
167
177
|
#
|
168
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
169
|
-
#
|
178
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
179
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
180
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
170
181
|
#
|
171
182
|
# The optional keyword arguments allow setting often used properties of the field:
|
172
183
|
#
|
@@ -202,8 +213,9 @@ module HexaPDF
|
|
202
213
|
|
203
214
|
# Creates a new multiline text field with the given name and adds it to the form.
|
204
215
|
#
|
205
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
206
|
-
#
|
216
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
217
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
218
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
207
219
|
#
|
208
220
|
# The optional keyword arguments allow setting often used properties of the field, see
|
209
221
|
# #create_text_field for details.
|
@@ -221,8 +233,9 @@ module HexaPDF
|
|
221
233
|
# The +max_chars+ argument defines the maximum number of characters the comb text field can
|
222
234
|
# accommodate.
|
223
235
|
#
|
224
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
225
|
-
#
|
236
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
237
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
238
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
226
239
|
#
|
227
240
|
# The optional keyword arguments allow setting often used properties of the field, see
|
228
241
|
# #create_text_field for details.
|
@@ -238,8 +251,9 @@ module HexaPDF
|
|
238
251
|
|
239
252
|
# Creates a new file select field with the given name and adds it to the form.
|
240
253
|
#
|
241
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
242
|
-
#
|
254
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
255
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
256
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
243
257
|
#
|
244
258
|
# The optional keyword arguments allow setting often used properties of the field, see
|
245
259
|
# #create_text_field for details.
|
@@ -254,8 +268,9 @@ module HexaPDF
|
|
254
268
|
|
255
269
|
# Creates a new password field with the given name and adds it to the form.
|
256
270
|
#
|
257
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
258
|
-
#
|
271
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
272
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
273
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
259
274
|
#
|
260
275
|
# The optional keyword arguments allow setting often used properties of the field, see
|
261
276
|
# #create_text_field for details.
|
@@ -270,8 +285,9 @@ module HexaPDF
|
|
270
285
|
|
271
286
|
# Creates a new check box with the given name and adds it to the form.
|
272
287
|
#
|
273
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
274
|
-
#
|
288
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
289
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
290
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
275
291
|
#
|
276
292
|
# Before a field value other than +false+ can be assigned to the check box, a widget needs
|
277
293
|
# to be created.
|
@@ -281,8 +297,9 @@ module HexaPDF
|
|
281
297
|
|
282
298
|
# Creates a radio button with the given name and adds it to the form.
|
283
299
|
#
|
284
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
285
|
-
#
|
300
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
301
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
302
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
286
303
|
#
|
287
304
|
# Before a field value other than +nil+ can be assigned to the radio button, at least one
|
288
305
|
# widget needs to be created.
|
@@ -292,8 +309,9 @@ module HexaPDF
|
|
292
309
|
|
293
310
|
# Creates a combo box with the given name and adds it to the form.
|
294
311
|
#
|
295
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
296
|
-
#
|
312
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
313
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
314
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
297
315
|
#
|
298
316
|
# The optional keyword arguments allow setting often used properties of the field:
|
299
317
|
#
|
@@ -319,8 +337,9 @@ module HexaPDF
|
|
319
337
|
|
320
338
|
# Creates a list box with the given name and adds it to the form.
|
321
339
|
#
|
322
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
323
|
-
#
|
340
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
341
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
342
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
324
343
|
#
|
325
344
|
# The optional keyword arguments allow setting often used properties of the field:
|
326
345
|
#
|
@@ -345,10 +364,38 @@ module HexaPDF
|
|
345
364
|
|
346
365
|
# Creates a signature field with the given name and adds it to the form.
|
347
366
|
#
|
348
|
-
# The +name+ may contain dots to signify a field hierarchy. If
|
349
|
-
#
|
367
|
+
# The +name+ may contain dots to signify a field hierarchy. If the parent fields don't
|
368
|
+
# already exist, they are created as pure namespace fields (see #create_namespace_field). If
|
369
|
+
# the +name+ doesn't contain dots, a top-level field is created.
|
350
370
|
def create_signature_field(name)
|
351
|
-
create_field(name, :Sig)
|
371
|
+
create_field(name, :Sig)
|
372
|
+
end
|
373
|
+
|
374
|
+
# :call-seq:
|
375
|
+
# form.delete_field(name)
|
376
|
+
# form.delete_field(field)
|
377
|
+
#
|
378
|
+
# Deletes the field specified by the given name or via the given field object.
|
379
|
+
#
|
380
|
+
# If the field is a signature field, the associated signature dictionary is also deleted.
|
381
|
+
def delete_field(name_or_field)
|
382
|
+
field = (name_or_field.kind_of?(String) ? field_by_name(name_or_field) : name_or_field)
|
383
|
+
document.delete(field[:V]) if field.field_type == :Sig
|
384
|
+
|
385
|
+
to_delete = field.each_widget(direct_only: false).to_a
|
386
|
+
document.pages.each do |page|
|
387
|
+
next unless page.key?(:Annots)
|
388
|
+
page_annots = page[:Annots].to_a - to_delete
|
389
|
+
page[:Annots].value.replace(page_annots)
|
390
|
+
end
|
391
|
+
to_delete.each {|widget| document.delete(widget) }
|
392
|
+
|
393
|
+
if field[:Parent]
|
394
|
+
field[:Parent][:Kids].delete(field)
|
395
|
+
else
|
396
|
+
self[:Fields].delete(field)
|
397
|
+
end
|
398
|
+
document.delete(field)
|
352
399
|
end
|
353
400
|
|
354
401
|
# Fills form fields with the values from the given +data+ hash.
|
@@ -485,23 +532,27 @@ module HexaPDF
|
|
485
532
|
|
486
533
|
private
|
487
534
|
|
488
|
-
# Creates a new field with the full name +name+ and the field type +type+.
|
489
|
-
def create_field(name, type)
|
535
|
+
# Creates a new field with the full name +name+ and the optional field type +type+.
|
536
|
+
def create_field(name, type = nil)
|
490
537
|
parent_name, _, name = name.rpartition('.')
|
491
538
|
parent_field = parent_name.empty? ? nil : field_by_name(parent_name)
|
492
539
|
if !parent_name.empty? && !parent_field
|
493
|
-
|
540
|
+
parent_field = create_namespace_field(parent_name)
|
494
541
|
end
|
495
542
|
|
496
|
-
field =
|
497
|
-
|
543
|
+
field = if type
|
544
|
+
document.add({FT: type, T: name, Parent: parent_field},
|
545
|
+
type: :XXAcroFormField, subtype: type)
|
546
|
+
else
|
547
|
+
document.add({T: name, Parent: parent_field}, type: :XXAcroFormField)
|
548
|
+
end
|
498
549
|
if parent_field
|
499
550
|
(parent_field[:Kids] ||= []) << field
|
500
551
|
else
|
501
552
|
(self[:Fields] ||= []) << field
|
502
553
|
end
|
503
554
|
|
504
|
-
yield(field)
|
555
|
+
yield(field) if block_given?
|
505
556
|
|
506
557
|
field
|
507
558
|
end
|
@@ -158,11 +158,11 @@ module HexaPDF
|
|
158
158
|
end
|
159
159
|
|
160
160
|
# :call-seq:
|
161
|
-
# file_spec.embed(filename, name: File.basename(filename), register: true) -> ef_stream
|
162
|
-
# file_spec.embed(io, name:, register: true) -> ef_stream
|
161
|
+
# file_spec.embed(filename, name: File.basename(filename), mime_type: nil, register: true) -> ef_stream
|
162
|
+
# file_spec.embed(io, name:, mime_type: nil, register: true) -> ef_stream
|
163
163
|
#
|
164
|
-
# Embeds the given file or IO stream into the PDF file, sets the path
|
165
|
-
# the created stream object.
|
164
|
+
# Embeds the given file or IO stream into the PDF file, sets the path and MIME type
|
165
|
+
# accordingly and returns the created stream object.
|
166
166
|
#
|
167
167
|
# If a file is given, the +name+ option defaults to the basename of the file. However, if an
|
168
168
|
# IO object is given, the +name+ argument is mandatory.
|
@@ -177,13 +177,16 @@ module HexaPDF
|
|
177
177
|
# name::
|
178
178
|
# The name that should be used as path value and when registering.
|
179
179
|
#
|
180
|
+
# mime_type::
|
181
|
+
# Optionally specifies the MIME type of the file.
|
182
|
+
#
|
180
183
|
# register::
|
181
184
|
# Specifies whether the embedded file will be added to the EmbeddedFiles name tree under
|
182
185
|
# the +name+. If the name is already taken, it's value is overwritten.
|
183
186
|
#
|
184
187
|
# The file has to be available until the PDF document gets written because reading and
|
185
188
|
# writing is done lazily.
|
186
|
-
def embed(file_or_io, name: nil, register: true)
|
189
|
+
def embed(file_or_io, name: nil, mime_type: nil, register: true)
|
187
190
|
name ||= File.basename(file_or_io) if file_or_io.kind_of?(String)
|
188
191
|
if name.nil?
|
189
192
|
raise ArgumentError, "The name argument is mandatory when given an IO object"
|
@@ -194,6 +197,7 @@ module HexaPDF
|
|
194
197
|
|
195
198
|
self[:EF] ||= {}
|
196
199
|
ef_stream = self[:EF][:UF] = self[:EF][:F] = document.add({Type: :EmbeddedFile})
|
200
|
+
ef_stream[:Subtype] = mime_type.to_sym if mime_type
|
197
201
|
stat = if file_or_io.kind_of?(String)
|
198
202
|
File.stat(file_or_io)
|
199
203
|
elsif file_or_io.respond_to?(:stat)
|
@@ -51,7 +51,7 @@ module HexaPDF
|
|
51
51
|
|
52
52
|
define_type :ExtGState
|
53
53
|
|
54
|
-
define_field :Type, type: Symbol,
|
54
|
+
define_field :Type, type: Symbol, default: type
|
55
55
|
define_field :LW, type: Numeric, version: "1.3"
|
56
56
|
define_field :LC, type: Integer, version: "1.3"
|
57
57
|
define_field :LJ, type: Integer, version: "1.3"
|
data/lib/hexapdf/version.rb
CHANGED
@@ -43,6 +43,11 @@ describe HexaPDF::Document::Files do
|
|
43
43
|
assert_equal('Some file', spec[:Desc])
|
44
44
|
end
|
45
45
|
|
46
|
+
it "optionally sets the MIME type of an embedded file" do
|
47
|
+
spec = @doc.files.add(@file.path, mime_type: 'application/pdf')
|
48
|
+
assert_equal(:'application/pdf', spec.embedded_file_stream[:Subtype])
|
49
|
+
end
|
50
|
+
|
46
51
|
it "requires the name argument when given an IO object" do
|
47
52
|
assert_raises(ArgumentError) { @doc.files.add(StringIO.new) }
|
48
53
|
end
|
@@ -187,6 +187,27 @@ describe HexaPDF::Document::Metadata do
|
|
187
187
|
assert_equal(metadata, @doc.catalog[:Metadata].stream.sub(/(?<=id=")\w+/, ''))
|
188
188
|
end
|
189
189
|
|
190
|
+
it "writes the custom metadata" do
|
191
|
+
@metadata.delete
|
192
|
+
@metadata.custom_metadata("<rdf:Description>Test</rdf:Description>")
|
193
|
+
@metadata.custom_metadata("<rdf:Description>Test2</rdf:Description>")
|
194
|
+
@doc.write(StringIO.new, update_fields: false)
|
195
|
+
metadata = <<~XMP
|
196
|
+
<?xpacket begin="" id=""?>
|
197
|
+
<x:xmpmeta xmlns:x="adobe:ns:meta/">
|
198
|
+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
199
|
+
<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
|
200
|
+
<pdf:Producer>HexaPDF version #{HexaPDF::VERSION}</pdf:Producer>
|
201
|
+
</rdf:Description>
|
202
|
+
<rdf:Description>Test</rdf:Description>
|
203
|
+
<rdf:Description>Test2</rdf:Description>
|
204
|
+
</rdf:RDF>
|
205
|
+
</x:xmpmeta>
|
206
|
+
<?xpacket end="r"?>
|
207
|
+
XMP
|
208
|
+
assert_equal(metadata, @doc.catalog[:Metadata].stream.sub(/(?<=id=")\w+/, ''))
|
209
|
+
end
|
210
|
+
|
190
211
|
it "writes the XMP metadata" do
|
191
212
|
title = HexaPDF::Document::Metadata::LocalizedString.new('Der Titel')
|
192
213
|
title.language = 'de'
|