hexapdf 0.26.1 → 0.26.2
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 +20 -0
- data/lib/hexapdf/document/layout.rb +24 -9
- data/lib/hexapdf/layout/box.rb +9 -2
- data/lib/hexapdf/layout/line.rb +4 -3
- data/lib/hexapdf/layout/style.rb +12 -6
- data/lib/hexapdf/layout/text_fragment.rb +21 -1
- data/lib/hexapdf/layout/text_layouter.rb +15 -21
- data/lib/hexapdf/revision.rb +9 -2
- data/lib/hexapdf/type/object_stream.rb +6 -8
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_layout.rb +19 -3
- data/test/hexapdf/layout/test_box.rb +5 -0
- data/test/hexapdf/layout/test_line.rb +6 -4
- data/test/hexapdf/layout/test_style.rb +20 -7
- data/test/hexapdf/layout/test_text_fragment.rb +26 -1
- data/test/hexapdf/layout/test_text_layouter.rb +9 -6
- data/test/hexapdf/test_composer.rb +18 -142
- data/test/hexapdf/test_revision.rb +18 -3
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/test_object_stream.rb +6 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f7ff4db8b6417cf6f0101a2e72e2481571a40c9542713be82753376d58c047c
|
|
4
|
+
data.tar.gz: 0b62a42fe5b91bdd8f11c4c31105e560cd4cf2f27259a86b9fd07db2f2280d6e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ef60dd2da6c1c233768e564c5afb9330fb07065a33f293a2fbca6c71b8948d2ab754b2c9383ed4a84400e944e98f6c5a3638bf0c970c2b819eb53a0e024a2ca
|
|
7
|
+
data.tar.gz: 9c0190529f0d25d9ec655bef27d25054b9bfb5dc2ec980742807e73e0eb953550033f5362249cad1ec83ff8e78a1f1ad91785614cecdbaed113ad6365a77ade5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## 0.26.2 - 2022-10-22
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
* Support for setting custom properties on [HexaPDF::Layout::Box] and
|
|
6
|
+
[HexaPDF::Layout::TextFragment]
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
* [HexaPDF::Layout::Style::LinkLayer] to use the 'link' custom box property if
|
|
11
|
+
no target is set
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* [HexaPDF::Layout::Style::Layers] to allow named layers without options
|
|
16
|
+
* [HexaPDF::Revision#each_modified_object] to not yield signature objects
|
|
17
|
+
* [HexaPDF::Revision#each_modified_object] to force comparison of direct objects
|
|
18
|
+
* [HexaPDF::Type::ObjectStream] to work for encrypted documents again
|
|
19
|
+
|
|
20
|
+
|
|
1
21
|
## 0.26.1 - 2022-10-14
|
|
2
22
|
|
|
3
23
|
### Changed
|
|
@@ -249,6 +249,10 @@ module HexaPDF
|
|
|
249
249
|
# +style_properties+ are specified, the style is duplicated and the additional styles are
|
|
250
250
|
# applied.
|
|
251
251
|
#
|
|
252
|
+
# +properties+::
|
|
253
|
+
# This can be used to set custom properties on the created text box. See Box#properties
|
|
254
|
+
# for details and usage.
|
|
255
|
+
#
|
|
252
256
|
# +box_style+::
|
|
253
257
|
# Sometimes it is necessary for the box to have a different style than the text, e.g. when
|
|
254
258
|
# using overlays. In such a case use +box_style+ for specifiying the style of the box (a
|
|
@@ -266,11 +270,13 @@ module HexaPDF
|
|
|
266
270
|
# })
|
|
267
271
|
#
|
|
268
272
|
# See: #formatted_text_box, HexaPDF::Layout::TextBox, HexaPDF::Layout::TextFragment
|
|
269
|
-
def text_box(text, width: 0, height: 0, style: nil, box_style: nil,
|
|
273
|
+
def text_box(text, width: 0, height: 0, style: nil, properties: nil, box_style: nil,
|
|
274
|
+
**style_properties)
|
|
270
275
|
style = retrieve_style(style, style_properties)
|
|
271
276
|
box_style = (box_style ? retrieve_style(box_style) : style)
|
|
272
277
|
box_class_for_name(:text).new(items: [HexaPDF::Layout::TextFragment.create(text, style)],
|
|
273
|
-
width: width, height: height,
|
|
278
|
+
width: width, height: height, properties: properties,
|
|
279
|
+
style: box_style)
|
|
274
280
|
end
|
|
275
281
|
|
|
276
282
|
# Creates a HexaPDF::Layout::TextBox like #text_box but allows parts of the text to be
|
|
@@ -294,7 +300,8 @@ module HexaPDF
|
|
|
294
300
|
# If any style properties are set, the used style is duplicated and the additional
|
|
295
301
|
# properties applied.
|
|
296
302
|
#
|
|
297
|
-
# See #text_box for details on +width+, +height+, +style+, +style_properties
|
|
303
|
+
# See #text_box for details on +width+, +height+, +style+, +style_properties+, +properties+
|
|
304
|
+
# and +box_style+.
|
|
298
305
|
#
|
|
299
306
|
# Examples:
|
|
300
307
|
#
|
|
@@ -305,7 +312,8 @@ module HexaPDF
|
|
|
305
312
|
# layout.formatted_text_box(["Some ", {text: "string", style: {font_size: 20}}])
|
|
306
313
|
#
|
|
307
314
|
# See: #text_box, HexaPDF::Layout::TextBox, HexaPDF::Layout::TextFragment
|
|
308
|
-
def formatted_text_box(data, width: 0, height: 0, style: nil, box_style: nil,
|
|
315
|
+
def formatted_text_box(data, width: 0, height: 0, style: nil, properties: nil, box_style: nil,
|
|
316
|
+
**style_properties)
|
|
309
317
|
style = retrieve_style(style, style_properties)
|
|
310
318
|
box_style = (box_style ? retrieve_style(box_style) : style)
|
|
311
319
|
data.map! do |hash|
|
|
@@ -315,10 +323,15 @@ module HexaPDF
|
|
|
315
323
|
link = hash.delete(:link)
|
|
316
324
|
(hash[:overlays] ||= []) << [:link, {uri: link}] if link
|
|
317
325
|
text = hash.delete(:text) || link || ""
|
|
318
|
-
|
|
326
|
+
properties = hash.delete(:properties)
|
|
327
|
+
frag_style = retrieve_style(hash.delete(:style) || style, hash)
|
|
328
|
+
fragment = HexaPDF::Layout::TextFragment.create(text, frag_style)
|
|
329
|
+
fragment.properties.update(properties) if properties
|
|
330
|
+
fragment
|
|
319
331
|
end
|
|
320
332
|
end
|
|
321
|
-
box_class_for_name(:text).new(items: data, width: width, height: height,
|
|
333
|
+
box_class_for_name(:text).new(items: data, width: width, height: height,
|
|
334
|
+
properties: properties, style: box_style)
|
|
322
335
|
end
|
|
323
336
|
|
|
324
337
|
# Creates a HexaPDF::Layout::ImageBox for the given image.
|
|
@@ -326,7 +339,8 @@ module HexaPDF
|
|
|
326
339
|
# The +file+ argument can be anything that is accepted by HexaPDF::Document::Images#add or a
|
|
327
340
|
# HexaPDF::Type::Form object.
|
|
328
341
|
#
|
|
329
|
-
# See #text_box for details on +width+, +height+, +style+ and
|
|
342
|
+
# See #text_box for details on +width+, +height+, +style+, +style_properties+ and
|
|
343
|
+
# +properties+.
|
|
330
344
|
#
|
|
331
345
|
# Examples:
|
|
332
346
|
#
|
|
@@ -334,10 +348,11 @@ module HexaPDF
|
|
|
334
348
|
# layout.image_box(machu_picchu, height: 30)
|
|
335
349
|
#
|
|
336
350
|
# See: HexaPDF::Layout::ImageBox
|
|
337
|
-
def image_box(file, width: 0, height: 0, style: nil, **style_properties)
|
|
351
|
+
def image_box(file, width: 0, height: 0, properties: nil, style: nil, **style_properties)
|
|
338
352
|
style = retrieve_style(style, style_properties)
|
|
339
353
|
image = file.kind_of?(HexaPDF::Stream) ? file : @document.images.add(file)
|
|
340
|
-
box_class_for_name(:image).new(image: image, width: width, height: height,
|
|
354
|
+
box_class_for_name(:image).new(image: image, width: width, height: height,
|
|
355
|
+
properties: properties, style: style)
|
|
341
356
|
end
|
|
342
357
|
|
|
343
358
|
# :nodoc:
|
data/lib/hexapdf/layout/box.rb
CHANGED
|
@@ -116,8 +116,14 @@ module HexaPDF
|
|
|
116
116
|
# * Style#underlays
|
|
117
117
|
attr_reader :style
|
|
118
118
|
|
|
119
|
+
# Hash with custom properties. The keys should be strings and can be arbitrary.
|
|
120
|
+
#
|
|
121
|
+
# This can be used to store arbitrary information on boxes for later use. For example, a
|
|
122
|
+
# generic style layer could use one or more custom properties for its work.
|
|
123
|
+
attr_reader :properties
|
|
124
|
+
|
|
119
125
|
# :call-seq:
|
|
120
|
-
# Box.new(width: 0, height: 0, style: nil) {|canv, box| block} -> box
|
|
126
|
+
# Box.new(width: 0, height: 0, style: nil, properties: {}) {|canv, box| block} -> box
|
|
121
127
|
#
|
|
122
128
|
# Creates a new Box object with the given width and height that uses the provided block when
|
|
123
129
|
# it is asked to draw itself on a canvas (see #draw).
|
|
@@ -125,10 +131,11 @@ module HexaPDF
|
|
|
125
131
|
# Since the final location of the box is not known beforehand, the drawing operations inside
|
|
126
132
|
# the block should draw inside the rectangle (0, 0, content_width, content_height) - note that
|
|
127
133
|
# the width and height of the box may not be known beforehand.
|
|
128
|
-
def initialize(width: 0, height: 0, style: nil, &block)
|
|
134
|
+
def initialize(width: 0, height: 0, style: nil, properties: {}, &block)
|
|
129
135
|
@width = @initial_width = width
|
|
130
136
|
@height = @initial_height = height
|
|
131
137
|
@style = Style.create(style)
|
|
138
|
+
@properties = properties
|
|
132
139
|
@draw_block = block
|
|
133
140
|
@fit_successful = false
|
|
134
141
|
@split_box = false
|
data/lib/hexapdf/layout/line.rb
CHANGED
|
@@ -192,13 +192,14 @@ module HexaPDF
|
|
|
192
192
|
|
|
193
193
|
# Adds the given item at the end of the item list.
|
|
194
194
|
#
|
|
195
|
-
# If both the item and the last item in the item list are TextFragment objects
|
|
196
|
-
#
|
|
195
|
+
# If both the item and the last item in the item list are TextFragment objects with the same
|
|
196
|
+
# attributes, they are combined.
|
|
197
197
|
#
|
|
198
198
|
# Note: The cache is not cleared!
|
|
199
199
|
def add(item)
|
|
200
200
|
last = @items.last
|
|
201
|
-
if last.instance_of?(item.class) && item.kind_of?(TextFragment) &&
|
|
201
|
+
if last.instance_of?(item.class) && item.kind_of?(TextFragment) &&
|
|
202
|
+
last.attributes_hash == item.attributes_hash
|
|
202
203
|
if last.items.frozen?
|
|
203
204
|
@items[-1] = last = last.dup
|
|
204
205
|
last.items = last.items.dup
|
data/lib/hexapdf/layout/style.rb
CHANGED
|
@@ -382,8 +382,9 @@ module HexaPDF
|
|
|
382
382
|
class Layers
|
|
383
383
|
|
|
384
384
|
# Creates a new Layers object popuplated with the given +layers+.
|
|
385
|
-
def initialize(layers =
|
|
386
|
-
@layers =
|
|
385
|
+
def initialize(layers = nil)
|
|
386
|
+
@layers = []
|
|
387
|
+
layers&.each {|name, options| add(name, **(options || {})) }
|
|
387
388
|
end
|
|
388
389
|
|
|
389
390
|
# Duplicates the array holding the layers.
|
|
@@ -402,8 +403,8 @@ module HexaPDF
|
|
|
402
403
|
# object in 'style.layers_map'. In this case +name+ is used as the reference and the options
|
|
403
404
|
# are passed to layer object if it needs initialization.
|
|
404
405
|
def add(name = nil, **options, &block)
|
|
405
|
-
if block_given?
|
|
406
|
-
@layers << block
|
|
406
|
+
if block_given? || name.kind_of?(Proc)
|
|
407
|
+
@layers << (block || name)
|
|
407
408
|
elsif name
|
|
408
409
|
@layers << [name, options]
|
|
409
410
|
else
|
|
@@ -452,7 +453,9 @@ module HexaPDF
|
|
|
452
453
|
# be specified):
|
|
453
454
|
#
|
|
454
455
|
# +dest+::
|
|
455
|
-
# The destination array or a name of a named destination for in-document links.
|
|
456
|
+
# The destination array or a name of a named destination for in-document links. If neither
|
|
457
|
+
# +dest+ nor +uri+ nor +file+ is specified, it is assumed that the box has a custom
|
|
458
|
+
# property named 'link' which is used for the destination.
|
|
456
459
|
#
|
|
457
460
|
# +uri+::
|
|
458
461
|
# The URI to link to.
|
|
@@ -473,6 +476,7 @@ module HexaPDF
|
|
|
473
476
|
# Examples:
|
|
474
477
|
# LinkLayer.new(dest: [page, :XYZ, nil, nil, nil], border: true)
|
|
475
478
|
# LinkLayer.new(uri: "https://my.example.com/path", border: [5 5 2])
|
|
479
|
+
# LinkLayer.new # use 'link' custom box property for dest
|
|
476
480
|
def initialize(dest: nil, uri: nil, file: nil, border: false, border_color: nil)
|
|
477
481
|
if dest && (uri || file) || uri && file
|
|
478
482
|
raise ArgumentError, "Only one of dest, uri and file is allowed"
|
|
@@ -496,6 +500,8 @@ module HexaPDF
|
|
|
496
500
|
# page.
|
|
497
501
|
def call(canvas, box)
|
|
498
502
|
return unless canvas.context.type == :Page
|
|
503
|
+
@dest = box.properties['link'] unless @dest || @action
|
|
504
|
+
|
|
499
505
|
page = canvas.context
|
|
500
506
|
matrix = canvas.graphics_state.ctm
|
|
501
507
|
quad_points = [*matrix.evaluate(0, 0), *matrix.evaluate(box.width, 0),
|
|
@@ -554,7 +560,7 @@ module HexaPDF
|
|
|
554
560
|
# Duplicates the complex properties that can be modified, as well as the cache.
|
|
555
561
|
def initialize_copy(other)
|
|
556
562
|
super
|
|
557
|
-
@scaled_item_widths = {}
|
|
563
|
+
@scaled_item_widths = {}.compare_by_identity
|
|
558
564
|
clear_cache
|
|
559
565
|
|
|
560
566
|
@font_features = @font_features.dup if defined?(@font_features)
|
|
@@ -105,9 +105,29 @@ module HexaPDF
|
|
|
105
105
|
#
|
|
106
106
|
# The argument +style+ can either be a Style object or a hash of style properties, see
|
|
107
107
|
# Style::create for details.
|
|
108
|
-
def initialize(items, style)
|
|
108
|
+
def initialize(items, style, properties: nil)
|
|
109
109
|
@items = items
|
|
110
110
|
@style = Style.create(style)
|
|
111
|
+
@properties = properties
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Creates a new TextFragment with the same style and custom properties as this one but with
|
|
115
|
+
# the given +items+.
|
|
116
|
+
def dup_attributes(items)
|
|
117
|
+
self.class.new(items, @style, properties: @properties.dup)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Returns the custom properties hash for the text fragment.
|
|
121
|
+
#
|
|
122
|
+
# See Box#properties for usage details.
|
|
123
|
+
def properties
|
|
124
|
+
@properties ||= {}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Returns the value that should be used as hash key when only the fragment's attributes -
|
|
128
|
+
# without the items - should play a role.
|
|
129
|
+
def attributes_hash
|
|
130
|
+
@style.hash ^ @properties.hash
|
|
111
131
|
end
|
|
112
132
|
|
|
113
133
|
# The precision used to determine whether two floats represent the same value.
|
|
@@ -243,43 +243,38 @@ module HexaPDF
|
|
|
243
243
|
box_items << glyph if glyph && !glyph.kind_of?(Numeric) && glyph.str == '-'
|
|
244
244
|
|
|
245
245
|
unless box_items.empty?
|
|
246
|
-
result << Box.new(
|
|
246
|
+
result << Box.new(item.dup_attributes(box_items.freeze))
|
|
247
247
|
end
|
|
248
248
|
|
|
249
249
|
if glyph
|
|
250
250
|
case glyph.str
|
|
251
251
|
when ' '
|
|
252
|
-
glues[item.
|
|
253
|
-
|
|
254
|
-
result << glues[item.style]
|
|
252
|
+
result << (glues[item.attributes_hash] ||=
|
|
253
|
+
Glue.new(item.dup_attributes([glyph].freeze)))
|
|
255
254
|
when "\n", "\v", "\f", "\u{85}", "\u{2029}"
|
|
256
|
-
penalties[item.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
result << penalties[item.style]
|
|
255
|
+
result << (penalties[item.attributes_hash] ||=
|
|
256
|
+
Penalty.new(Penalty::PARAGRAPH_BREAK, 0,
|
|
257
|
+
item: item.dup_attributes([].freeze)))
|
|
260
258
|
when "\u{2028}"
|
|
261
259
|
result << Penalty.new(Penalty::LINE_BREAK, 0,
|
|
262
|
-
item:
|
|
260
|
+
item: item.dup_attributes([].freeze))
|
|
263
261
|
when "\r"
|
|
264
262
|
if !item.items[i + 1] || item.items[i + 1].kind_of?(Numeric) ||
|
|
265
263
|
item.items[i + 1].str != "\n"
|
|
266
|
-
penalties[item.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
result << penalties[item.style]
|
|
264
|
+
result << (penalties[item.attributes_hash] ||=
|
|
265
|
+
Penalty.new(Penalty::PARAGRAPH_BREAK, 0,
|
|
266
|
+
item: item.dup_attributes([].freeze)))
|
|
270
267
|
end
|
|
271
268
|
when '-'
|
|
272
269
|
result << Penalty::Standard
|
|
273
270
|
when "\t"
|
|
274
271
|
spaces = [item.style.font.decode_utf8(" ").first] * 8
|
|
275
|
-
result << Glue.new(
|
|
272
|
+
result << Glue.new(item.dup_attributes(spaces.freeze))
|
|
276
273
|
when "\u{00AD}"
|
|
277
|
-
|
|
278
|
-
frag = TextFragment.new([hyphen].freeze, item.style)
|
|
274
|
+
frag = item.dup_attributes([item.style.font.decode_utf8("-").first].freeze)
|
|
279
275
|
result << Penalty.new(Penalty::Standard.penalty, frag.width, item: frag)
|
|
280
276
|
when "\u{00A0}"
|
|
281
|
-
|
|
282
|
-
frag = TextFragment.new([space].freeze, item.style)
|
|
277
|
+
frag = item.dup_attributes([item.style.font.decode_utf8(" ").first].freeze)
|
|
283
278
|
result << Penalty.new(Penalty::ProhibitedBreak.penalty, frag.width, item: frag)
|
|
284
279
|
when "\u{200B}"
|
|
285
280
|
result << Penalty.new(0)
|
|
@@ -835,7 +830,7 @@ module HexaPDF
|
|
|
835
830
|
if too_wide_box && (too_wide_box.item.kind_of?(TextFragment) &&
|
|
836
831
|
too_wide_box.item.items.size > 1)
|
|
837
832
|
rest[0..rest.index(too_wide_box)] = too_wide_box.item.items.map do |item|
|
|
838
|
-
Box.new(
|
|
833
|
+
Box.new(too_wide_box.item.dup_attributes([item].freeze))
|
|
839
834
|
end
|
|
840
835
|
too_wide_box = nil
|
|
841
836
|
else
|
|
@@ -939,8 +934,7 @@ module HexaPDF
|
|
|
939
934
|
frag = line.items[indexes[i]]
|
|
940
935
|
value = -frag.items[indexes[i + 1]].width * adjustment
|
|
941
936
|
if frag.items.frozen?
|
|
942
|
-
|
|
943
|
-
line.items.insert(indexes[i], value)
|
|
937
|
+
line.items.insert(indexes[i], frag.dup_attributes([value]))
|
|
944
938
|
else
|
|
945
939
|
frag.items.insert(indexes[i + 1], value)
|
|
946
940
|
frag.clear_cache
|
data/lib/hexapdf/revision.rb
CHANGED
|
@@ -243,14 +243,21 @@ module HexaPDF
|
|
|
243
243
|
@objects.each do |oid, gen, obj|
|
|
244
244
|
if @xref_section.entry?(oid, gen)
|
|
245
245
|
stored_obj = @loader.call(@xref_section[oid, gen])
|
|
246
|
-
next if (stored_obj.type == :ObjStm || stored_obj.type == :XRef) && obj.null?
|
|
246
|
+
next if (stored_obj.type == :ObjStm || stored_obj.type == :XRef) && obj.null? ||
|
|
247
|
+
stored_obj.type == :Sig || stored_obj.type == :DocTimeStamp
|
|
247
248
|
|
|
248
249
|
streams_are_same = (obj.data.stream == stored_obj.data.stream)
|
|
249
250
|
next if obj.value == stored_obj.value && streams_are_same
|
|
250
251
|
|
|
251
252
|
if obj.value.kind_of?(Hash) && stored_obj.value.kind_of?(Hash)
|
|
252
253
|
keys = obj.value.keys | stored_obj.value.keys
|
|
253
|
-
|
|
254
|
+
values_unchanged = keys.all? do |key|
|
|
255
|
+
other = stored_obj[key]
|
|
256
|
+
# Force comparison of values if both are indirect objects
|
|
257
|
+
other = other.value if other.kind_of?(Object) && !other.indirect?
|
|
258
|
+
obj[key] == other
|
|
259
|
+
end
|
|
260
|
+
next if values_unchanged && streams_are_same
|
|
254
261
|
end
|
|
255
262
|
end
|
|
256
263
|
|
|
@@ -203,13 +203,6 @@ module HexaPDF
|
|
|
203
203
|
|
|
204
204
|
private
|
|
205
205
|
|
|
206
|
-
# Parses the stream data after the object is first initialized. Since the parsed stream data
|
|
207
|
-
# is cached, it is only parsed on initialization and not again if e.g. the stream is changed.
|
|
208
|
-
def after_data_change
|
|
209
|
-
super
|
|
210
|
-
parse_stream
|
|
211
|
-
end
|
|
212
|
-
|
|
213
206
|
# Parses the object numbers and their offsets from the start of the stream data.
|
|
214
207
|
def parse_oids_and_offsets(data)
|
|
215
208
|
oids = []
|
|
@@ -227,7 +220,12 @@ module HexaPDF
|
|
|
227
220
|
|
|
228
221
|
# Returns the container with the to-be-stored objects.
|
|
229
222
|
def objects
|
|
230
|
-
@objects ||=
|
|
223
|
+
@objects ||=
|
|
224
|
+
begin
|
|
225
|
+
@objects = {}
|
|
226
|
+
parse_stream
|
|
227
|
+
@objects
|
|
228
|
+
end
|
|
231
229
|
end
|
|
232
230
|
|
|
233
231
|
# Validates that the generation number of the object stream is zero.
|
data/lib/hexapdf/version.rb
CHANGED
|
@@ -90,12 +90,14 @@ describe HexaPDF::Document::Layout do
|
|
|
90
90
|
|
|
91
91
|
describe "box" do
|
|
92
92
|
it "creates the request box" do
|
|
93
|
-
box = @layout.box(:column, columns: 3, gaps: 20, width: 15, height: 30, style: {font_size: 10}
|
|
93
|
+
box = @layout.box(:column, columns: 3, gaps: 20, width: 15, height: 30, style: {font_size: 10},
|
|
94
|
+
properties: {key: :value})
|
|
94
95
|
assert_equal(15, box.width)
|
|
95
96
|
assert_equal(30, box.height)
|
|
96
97
|
assert_equal([-1, -1, -1], box.columns)
|
|
97
98
|
assert_equal([20], box.gaps)
|
|
98
99
|
assert_equal(10, box.style.font_size)
|
|
100
|
+
assert_equal({key: :value}, box.properties)
|
|
99
101
|
end
|
|
100
102
|
|
|
101
103
|
it "allows specifying the box's children via a provided block" do
|
|
@@ -113,13 +115,14 @@ describe HexaPDF::Document::Layout do
|
|
|
113
115
|
|
|
114
116
|
describe "text_box" do
|
|
115
117
|
it "creates a text box" do
|
|
116
|
-
box = @layout.text_box("Test", width: 10, height: 15)
|
|
118
|
+
box = @layout.text_box("Test", width: 10, height: 15, properties: {key: :value})
|
|
117
119
|
assert_equal(10, box.width)
|
|
118
120
|
assert_equal(15, box.height)
|
|
119
121
|
assert_same(@doc.fonts.add("Times"), box.style.font)
|
|
120
122
|
items = box.instance_variable_get(:@items)
|
|
121
123
|
assert_equal(1, items.length)
|
|
122
124
|
assert_same(box.style, items.first.style)
|
|
125
|
+
assert_equal({key: :value}, box.properties)
|
|
123
126
|
end
|
|
124
127
|
|
|
125
128
|
it "allows setting of a custom style" do
|
|
@@ -162,6 +165,11 @@ describe HexaPDF::Document::Layout do
|
|
|
162
165
|
assert_equal(1, box.instance_variable_get(:@items).length)
|
|
163
166
|
end
|
|
164
167
|
|
|
168
|
+
it "allows setting custom properties on the whole box" do
|
|
169
|
+
box = @layout.formatted_text_box(["Test"], properties: {key: :value})
|
|
170
|
+
assert_equal({key: :value}, box.properties)
|
|
171
|
+
end
|
|
172
|
+
|
|
165
173
|
it "allows using a hash with :text key instead of a simple string" do
|
|
166
174
|
box = @layout.formatted_text_box([{text: "Test"}])
|
|
167
175
|
items = box.instance_variable_get(:@items)
|
|
@@ -218,18 +226,26 @@ describe HexaPDF::Document::Layout do
|
|
|
218
226
|
assert_equal([:link, {uri: 'URI'}], items[0].style.overlays.instance_variable_get(:@layers)[0])
|
|
219
227
|
refute(items[2].style.overlays?)
|
|
220
228
|
end
|
|
229
|
+
|
|
230
|
+
it "allows setting custom properties" do
|
|
231
|
+
box = @layout.formatted_text_box([{text: 'test', properties: {named_dest: 'test'}}])
|
|
232
|
+
items = box.instance_variable_get(:@items)
|
|
233
|
+
assert_equal({named_dest: 'test'}, items[0].properties)
|
|
234
|
+
end
|
|
221
235
|
end
|
|
222
236
|
|
|
223
237
|
describe "image_box" do
|
|
224
238
|
it "creates an image box" do
|
|
225
239
|
image_path = File.join(TEST_DATA_DIR, 'images', 'gray.jpg')
|
|
226
240
|
|
|
227
|
-
box = @layout.image_box(image_path, width: 10, height: 15, style: {font_size: 20},
|
|
241
|
+
box = @layout.image_box(image_path, width: 10, height: 15, style: {font_size: 20},
|
|
242
|
+
properties: {key: :value}, subscript: true)
|
|
228
243
|
assert_equal(10, box.width)
|
|
229
244
|
assert_equal(15, box.height)
|
|
230
245
|
assert_equal(20, box.style.font_size)
|
|
231
246
|
assert(box.style.subscript)
|
|
232
247
|
assert_same(@doc.images.add(image_path), box.image)
|
|
248
|
+
assert_equal({key: :value}, box.properties)
|
|
233
249
|
end
|
|
234
250
|
|
|
235
251
|
it "allows using a form XObject" do
|
|
@@ -54,6 +54,11 @@ describe HexaPDF::Layout::Box do
|
|
|
54
54
|
box = create_box(style: HexaPDF::Layout::Style.new(padding: 20))
|
|
55
55
|
assert_equal(20, box.style.padding.top)
|
|
56
56
|
end
|
|
57
|
+
|
|
58
|
+
it "allows setting custom properties" do
|
|
59
|
+
box = create_box(properties: {'key' => :value})
|
|
60
|
+
assert_equal({'key' => :value}, box.properties)
|
|
61
|
+
end
|
|
57
62
|
end
|
|
58
63
|
|
|
59
64
|
it "returns false when asking whether it is a split box by default" do
|
|
@@ -50,10 +50,12 @@ describe HexaPDF::Layout::Line do
|
|
|
50
50
|
|
|
51
51
|
it "combines text fragments if possible" do
|
|
52
52
|
frag1 = setup_fragment("Home")
|
|
53
|
-
frag2 = HexaPDF::Layout::TextFragment.new(frag1.items
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
frag2 = HexaPDF::Layout::TextFragment.new(frag1.items[2, 2], frag1.style)
|
|
54
|
+
frag3 = HexaPDF::Layout::TextFragment.new(frag1.items[2, 2], frag1.style,
|
|
55
|
+
properties: {'key' => :value})
|
|
56
|
+
@line << setup_fragment("o") << :other << frag1 << frag2 << frag3
|
|
57
|
+
assert_equal(4, @line.items.length)
|
|
58
|
+
assert_equal(6, @line.items[-2].items.length)
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
it "duplicates the first of two combinable text fragments if its items are frozen" do
|
|
@@ -448,12 +448,16 @@ end
|
|
|
448
448
|
describe HexaPDF::Layout::Style::Layers do
|
|
449
449
|
before do
|
|
450
450
|
@layers = HexaPDF::Layout::Style::Layers.new
|
|
451
|
+
value = Object.new
|
|
452
|
+
value.define_singleton_method(:new) {|*| :new }
|
|
453
|
+
@config = Object.new
|
|
454
|
+
@config.define_singleton_method(:constantize) {|*| value }
|
|
451
455
|
end
|
|
452
456
|
|
|
453
457
|
it "can be initialized with an array of layers" do
|
|
454
|
-
data = [lambda {}]
|
|
458
|
+
data = [lambda {}, [:test]]
|
|
455
459
|
layers = HexaPDF::Layout::Style::Layers.new(data)
|
|
456
|
-
assert_equal(data, layers.enum_for(:each,
|
|
460
|
+
assert_equal([data[0], :new], layers.enum_for(:each, @config).to_a)
|
|
457
461
|
end
|
|
458
462
|
|
|
459
463
|
it "can be duplicated" do
|
|
@@ -469,13 +473,15 @@ describe HexaPDF::Layout::Style::Layers do
|
|
|
469
473
|
assert_equal([block], @layers.enum_for(:each, {}).to_a)
|
|
470
474
|
end
|
|
471
475
|
|
|
476
|
+
it "can use a given proc" do
|
|
477
|
+
block = proc { true }
|
|
478
|
+
@layers.add(block)
|
|
479
|
+
assert_equal([block], @layers.enum_for(:each, {}).to_a)
|
|
480
|
+
end
|
|
481
|
+
|
|
472
482
|
it "can store a reference" do
|
|
473
483
|
@layers.add(:link, option: :value)
|
|
474
|
-
|
|
475
|
-
value.define_singleton_method(:new) {|*| :new }
|
|
476
|
-
config = Object.new
|
|
477
|
-
config.define_singleton_method(:constantize) {|*| value }
|
|
478
|
-
assert_equal([:new], @layers.enum_for(:each, config).to_a)
|
|
484
|
+
assert_equal([:new], @layers.enum_for(:each, @config).to_a)
|
|
479
485
|
end
|
|
480
486
|
|
|
481
487
|
it "fails if neither a block nor a name is given when adding a layer" do
|
|
@@ -590,6 +596,13 @@ describe HexaPDF::Layout::Style::LinkLayer do
|
|
|
590
596
|
assert_equal({S: :Launch, F: "local-file.pdf", NewWindow: true}, annot[:A].value)
|
|
591
597
|
assert_nil(annot[:Dest])
|
|
592
598
|
end
|
|
599
|
+
|
|
600
|
+
it "works for destinations set via the 'link' custom box property" do
|
|
601
|
+
@box.properties['link'] = [@canvas.context, :FitH]
|
|
602
|
+
annot = call_link({})
|
|
603
|
+
assert_equal([@canvas.context, :FitH], annot[:Dest].value)
|
|
604
|
+
assert_nil(annot[:A])
|
|
605
|
+
end
|
|
593
606
|
end
|
|
594
607
|
end
|
|
595
608
|
|
|
@@ -46,11 +46,36 @@ describe HexaPDF::Layout::TextFragment do
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
it "can use style options" do
|
|
49
|
-
frag = HexaPDF::Layout::TextFragment.new(@items, font: @font, font_size: 20)
|
|
49
|
+
frag = HexaPDF::Layout::TextFragment.new(@items, {font: @font, font_size: 20})
|
|
50
50
|
assert_equal(20, frag.style.font_size)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
it "allows duplicating with only its attributes while also setting new items" do
|
|
55
|
+
setup_fragment([20])
|
|
56
|
+
@fragment.properties['key'] = :value
|
|
57
|
+
frag = @fragment.dup_attributes([21])
|
|
58
|
+
assert_equal([21], frag.items)
|
|
59
|
+
assert_same(frag.style, @fragment.style)
|
|
60
|
+
assert_equal(:value, frag.properties['key'])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "creates an attributes hash for storing the fragment based on the attributes without the items" do
|
|
64
|
+
setup_fragment([20])
|
|
65
|
+
hash = @fragment.attributes_hash
|
|
66
|
+
@fragment.properties['key'] = :value
|
|
67
|
+
new_hash = @fragment.attributes_hash
|
|
68
|
+
refute_equal(hash, new_hash)
|
|
69
|
+
@fragment.items << [21]
|
|
70
|
+
assert_equal(new_hash, @fragment.attributes_hash)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "allows setting custom properties" do
|
|
74
|
+
setup_fragment([])
|
|
75
|
+
@fragment.properties[:key] = :value
|
|
76
|
+
assert_equal({key: :value}, @fragment.properties)
|
|
77
|
+
end
|
|
78
|
+
|
|
54
79
|
it "returns :text for valign" do
|
|
55
80
|
assert_equal(:text, setup_fragment([]).valign)
|
|
56
81
|
end
|
|
@@ -31,12 +31,14 @@ module TestTextLayouterHelpers
|
|
|
31
31
|
else
|
|
32
32
|
assert_same(item.style, obj.item.style)
|
|
33
33
|
assert_equal(item.items, obj.item.items)
|
|
34
|
+
assert_equal(item.properties, obj.item.properties)
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def assert_glue(obj, fragment)
|
|
38
39
|
assert_kind_of(HexaPDF::Layout::TextLayouter::Glue, obj)
|
|
39
40
|
assert_same(fragment.style, obj.item.style)
|
|
41
|
+
assert_equal(fragment.properties, obj.item.properties)
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
def assert_penalty(obj, penalty, item = nil)
|
|
@@ -45,6 +47,7 @@ module TestTextLayouterHelpers
|
|
|
45
47
|
if item
|
|
46
48
|
assert_same(item.style, obj.item.style)
|
|
47
49
|
assert_equal(item.items, obj.item.items)
|
|
50
|
+
assert_equal(item.properties, obj.item.properties)
|
|
48
51
|
end
|
|
49
52
|
end
|
|
50
53
|
|
|
@@ -83,12 +86,10 @@ describe HexaPDF::Layout::TextLayouter::SimpleTextSegmentation do
|
|
|
83
86
|
@obj = HexaPDF::Layout::TextLayouter::SimpleTextSegmentation
|
|
84
87
|
end
|
|
85
88
|
|
|
86
|
-
def setup_fragment(text, style =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
HexaPDF::Layout::TextFragment.create(text, font: @font)
|
|
91
|
-
end
|
|
89
|
+
def setup_fragment(text, style = {font: @font})
|
|
90
|
+
fragment = HexaPDF::Layout::TextFragment.create(text, style)
|
|
91
|
+
fragment.properties['key'] = :value
|
|
92
|
+
fragment
|
|
92
93
|
end
|
|
93
94
|
|
|
94
95
|
it "handles InlineBox objects" do
|
|
@@ -138,11 +139,13 @@ describe HexaPDF::Layout::TextLayouter::SimpleTextSegmentation do
|
|
|
138
139
|
assert_equal([], result[index].item.items)
|
|
139
140
|
assert(result[index].item.items.frozen?)
|
|
140
141
|
assert_same(frag.style, result[index].item.style)
|
|
142
|
+
assert_equal(frag.properties, result[index].item.properties)
|
|
141
143
|
end
|
|
142
144
|
assert_penalty(result[15], HexaPDF::Layout::TextLayouter::Penalty::LINE_BREAK)
|
|
143
145
|
assert_equal([], result[15].item.items)
|
|
144
146
|
assert(result[15].item.items.frozen?)
|
|
145
147
|
assert_same(frag.style, result[15].item.style)
|
|
148
|
+
assert_equal(frag.properties, result[15].item.properties)
|
|
146
149
|
end
|
|
147
150
|
|
|
148
151
|
it "insert a standard penalty after a hyphen" do
|
|
@@ -86,24 +86,15 @@ describe HexaPDF::Composer do
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
describe "style" do
|
|
89
|
-
it "
|
|
90
|
-
@composer.style(:base, font_size: 20)
|
|
91
|
-
assert_equal(20, @composer.style(:
|
|
92
|
-
|
|
93
|
-
assert_equal(
|
|
94
|
-
assert(@composer.style(:yet_another_new, base: :newstyle).subscript)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
it "returns the named style" do
|
|
98
|
-
assert_kind_of(HexaPDF::Layout::Style, @composer.style(:base))
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
it "updates the style with the given properties" do
|
|
102
|
-
assert_equal(20, @composer.style(:base, font_size: 20).font_size)
|
|
89
|
+
it "delegates to layout.style" do
|
|
90
|
+
@composer.document.layout.style(:base, font_size: 20)
|
|
91
|
+
assert_equal(20, @composer.style(:base).font_size)
|
|
92
|
+
@composer.style(:base, font_size: 30)
|
|
93
|
+
assert_equal(30, @composer.document.layout.style(:base).font_size)
|
|
103
94
|
end
|
|
104
95
|
end
|
|
105
96
|
|
|
106
|
-
describe "text" do
|
|
97
|
+
describe "text/formatted_text/image/box" do
|
|
107
98
|
before do
|
|
108
99
|
test_self = self
|
|
109
100
|
@composer.define_singleton_method(:draw_box) do |arg|
|
|
@@ -111,151 +102,36 @@ describe HexaPDF::Composer do
|
|
|
111
102
|
end
|
|
112
103
|
end
|
|
113
104
|
|
|
114
|
-
it "
|
|
115
|
-
@composer.text("Test", width: 10, height: 15
|
|
105
|
+
it "delegates #text to layout.text" do
|
|
106
|
+
@composer.text("Test", width: 10, height: 15, style: {font_size: 20},
|
|
107
|
+
box_style: {font_size: 30}, line_spacing: 2)
|
|
116
108
|
assert_equal(10, @box.width)
|
|
117
109
|
assert_equal(15, @box.height)
|
|
118
|
-
|
|
110
|
+
assert_equal(30, @box.style.font_size)
|
|
119
111
|
items = @box.instance_variable_get(:@items)
|
|
120
112
|
assert_equal(1, items.length)
|
|
121
|
-
assert_same(
|
|
113
|
+
assert_same(20, items.first.style.font_size)
|
|
122
114
|
end
|
|
123
115
|
|
|
124
|
-
it "
|
|
125
|
-
style = HexaPDF::Layout::Style.new(font_size: 20, font: ['Times', {variant: :bold}])
|
|
126
|
-
@composer.text("Test", style: style)
|
|
127
|
-
assert_same(@box.style, style)
|
|
128
|
-
assert_same(@composer.document.fonts.add("Times", variant: :bold), @box.style.font)
|
|
129
|
-
assert_equal(20, @box.style.font_size)
|
|
130
|
-
|
|
131
|
-
@composer.text("Test", style: {font_size: 20})
|
|
132
|
-
assert_equal(20, @box.style.font_size)
|
|
133
|
-
|
|
134
|
-
@composer.style(:named, font_size: 20)
|
|
135
|
-
@composer.text("Test", style: :named)
|
|
136
|
-
assert_equal(20, @box.style.font_size)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
it "updates the used style with the provided options" do
|
|
140
|
-
@composer.text("Test", style: {subscript: true}, font_size: 20)
|
|
141
|
-
assert_equal(20, @box.style.font_size)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
it "allows using a box style different from the text style" do
|
|
145
|
-
style = HexaPDF::Layout::Style.new(font_size: 20)
|
|
146
|
-
@composer.text("Test", box_style: style)
|
|
147
|
-
refute_same(@box.instance_variable_get(:@items).first.style, style)
|
|
148
|
-
assert_same(@box.style, style)
|
|
149
|
-
|
|
150
|
-
@composer.style(:named, font_size: 20)
|
|
151
|
-
@composer.text("Test", box_style: :named)
|
|
152
|
-
assert_equal(20, @box.style.font_size)
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
describe "formatted_text" do
|
|
157
|
-
before do
|
|
158
|
-
test_self = self
|
|
159
|
-
@composer.define_singleton_method(:draw_box) do |arg|
|
|
160
|
-
test_self.instance_variable_set(:@box, arg)
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
it "creates a text box with the given text and draws it on the canvas" do
|
|
116
|
+
it "delegates #formatted_text to layout.formatted_text" do
|
|
165
117
|
@composer.formatted_text(["Test"], width: 10, height: 15)
|
|
166
118
|
assert_equal(10, @box.width)
|
|
167
119
|
assert_equal(15, @box.height)
|
|
168
120
|
assert_equal(1, @box.instance_variable_get(:@items).length)
|
|
169
121
|
end
|
|
170
122
|
|
|
171
|
-
it "
|
|
172
|
-
@composer.formatted_text([{text: "Test"}])
|
|
173
|
-
items = @box.instance_variable_get(:@items)
|
|
174
|
-
assert_equal(4, items[0].items.length)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
it "uses an empty string if the :text key for a hash is not specified" do
|
|
178
|
-
@composer.formatted_text([{font_size: "Test"}])
|
|
179
|
-
items = @box.instance_variable_get(:@items)
|
|
180
|
-
assert_equal(0, items[0].items.length)
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
it "allows setting a custom base style for all parts" do
|
|
184
|
-
@composer.formatted_text(["Test", "other"], font_size: 20)
|
|
185
|
-
items = @box.instance_variable_get(:@items)
|
|
186
|
-
assert_equal(20, @box.style.font_size)
|
|
187
|
-
assert_equal(20, items[0].style.font_size)
|
|
188
|
-
assert_equal(20, items[1].style.font_size)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
it "allows using custom style properties for a single part" do
|
|
192
|
-
@composer.formatted_text([{text: "Test", font_size: 20}, "test"], align: :center)
|
|
193
|
-
items = @box.instance_variable_get(:@items)
|
|
194
|
-
assert_equal(10, @box.style.font_size)
|
|
195
|
-
|
|
196
|
-
assert_equal(20, items[0].style.font_size)
|
|
197
|
-
assert_equal(:center, items[0].style.align)
|
|
198
|
-
|
|
199
|
-
assert_equal(10, items[1].style.font_size)
|
|
200
|
-
assert_equal(:center, items[1].style.align)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
it "allows using a custom style as basis for a single part" do
|
|
204
|
-
@composer.formatted_text([{text: "Test", style: {font_size: 20}, subscript: true}, "test"],
|
|
205
|
-
align: :center)
|
|
206
|
-
items = @box.instance_variable_get(:@items)
|
|
207
|
-
assert_equal(10, @box.style.font_size)
|
|
208
|
-
|
|
209
|
-
assert_equal(20, items[0].style.font_size)
|
|
210
|
-
assert_equal(:left, items[0].style.align)
|
|
211
|
-
assert(items[0].style.subscript)
|
|
212
|
-
|
|
213
|
-
assert_equal(10, items[1].style.font_size)
|
|
214
|
-
assert_equal(:center, items[1].style.align)
|
|
215
|
-
refute(items[1].style.subscript)
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
it "allows specifying a link to an URL via the :link key" do
|
|
219
|
-
@composer.formatted_text([{text: "Test", link: "URI"}, {link: "URI"}, "test"])
|
|
220
|
-
items = @box.instance_variable_get(:@items)
|
|
221
|
-
assert_equal(3, items.length)
|
|
222
|
-
assert_equal(4, items[0].items.length, "text should be Test")
|
|
223
|
-
assert_equal(3, items[1].items.length, "text should be URI")
|
|
224
|
-
assert_equal([:link, {uri: 'URI'}], items[0].style.overlays.instance_variable_get(:@layers)[0])
|
|
225
|
-
refute(items[2].style.overlays?)
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
describe "image" do
|
|
230
|
-
it "creates an image box and draws it on the canvas" do
|
|
231
|
-
box = nil
|
|
232
|
-
@composer.define_singleton_method(:draw_box) {|arg| box = arg }
|
|
233
|
-
image_path = File.join(TEST_DATA_DIR, 'images', 'gray.jpg')
|
|
234
|
-
|
|
235
|
-
@composer.image(image_path, width: 10, height: 15, style: {font_size: 20}, subscript: true)
|
|
236
|
-
assert_equal(10, box.width)
|
|
237
|
-
assert_equal(15, box.height)
|
|
238
|
-
assert_equal(20, box.style.font_size)
|
|
239
|
-
assert(box.style.subscript)
|
|
240
|
-
assert_same(@composer.document.images.add(image_path), box.image)
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
it "allows using a form XObject" do
|
|
123
|
+
it "delegates #image to layout.image" do
|
|
244
124
|
form = @composer.document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, 10, 10]})
|
|
245
125
|
@composer.image(form, width: 10)
|
|
246
|
-
assert_equal(
|
|
247
|
-
assert_equal(
|
|
126
|
+
assert_equal(10, @box.width)
|
|
127
|
+
assert_equal(0, @box.height)
|
|
248
128
|
end
|
|
249
|
-
end
|
|
250
129
|
|
|
251
|
-
|
|
252
|
-
it "creates the named box and draws it on the canvas" do
|
|
253
|
-
box = nil
|
|
254
|
-
@composer.define_singleton_method(:draw_box) {|arg| box = arg }
|
|
130
|
+
it "delegates #box to layout.box" do
|
|
255
131
|
image = @composer.document.images.add(File.join(TEST_DATA_DIR, 'images', 'gray.jpg'))
|
|
256
132
|
@composer.box(:list, width: 20) {|list| list.image(image) }
|
|
257
|
-
assert_equal(20, box.width)
|
|
258
|
-
assert_same(image, box.children[0].image)
|
|
133
|
+
assert_equal(20, @box.width)
|
|
134
|
+
assert_same(image, @box.children[0].image)
|
|
259
135
|
end
|
|
260
136
|
end
|
|
261
137
|
|
|
@@ -25,10 +25,13 @@ describe HexaPDF::Revision do
|
|
|
25
25
|
HexaPDF::Object.new(nil, oid: entry.oid, gen: entry.gen)
|
|
26
26
|
else
|
|
27
27
|
case entry.oid
|
|
28
|
+
when 2 then HexaPDF::Dictionary.new({Type: :Sig}, oid: entry.oid, gen: entry.gen)
|
|
28
29
|
when 4 then HexaPDF::Dictionary.new({Type: :XRef}, oid: entry.oid, gen: entry.gen)
|
|
29
30
|
when 5 then HexaPDF::Dictionary.new({Type: :ObjStm}, oid: entry.oid, gen: entry.gen)
|
|
30
31
|
when 7 then HexaPDF::Type::Catalog.new({Type: :Catalog}, oid: entry.oid, gen: entry.gen,
|
|
31
32
|
document: self)
|
|
33
|
+
when 6 then HexaPDF::Dictionary.new({Array: HexaPDF::PDFArray.new([1, 2])},
|
|
34
|
+
oid: entry.oid, gen: entry.gen)
|
|
32
35
|
else HexaPDF::Object.new(:Test, oid: entry.oid, gen: entry.gen)
|
|
33
36
|
end
|
|
34
37
|
end
|
|
@@ -43,7 +46,7 @@ describe HexaPDF::Revision do
|
|
|
43
46
|
|
|
44
47
|
it "takes an xref section and/or a parser on initialization" do
|
|
45
48
|
rev = HexaPDF::Revision.new({}, loader: @loader, xref_section: @xref_section)
|
|
46
|
-
assert_equal(:
|
|
49
|
+
assert_equal({Type: :Sig}, rev.object(2).value)
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
it "returns the next free object number" do
|
|
@@ -100,7 +103,7 @@ describe HexaPDF::Revision do
|
|
|
100
103
|
|
|
101
104
|
it "loads an object that is defined in the cross-reference section" do
|
|
102
105
|
obj = @rev.object(HexaPDF::Reference.new(2, 0))
|
|
103
|
-
assert_equal(:
|
|
106
|
+
assert_equal({Type: :Sig}, obj.value)
|
|
104
107
|
assert_equal(2, obj.oid)
|
|
105
108
|
assert_equal(0, obj.gen)
|
|
106
109
|
end
|
|
@@ -190,7 +193,7 @@ describe HexaPDF::Revision do
|
|
|
190
193
|
|
|
191
194
|
describe "each_modified_object" do
|
|
192
195
|
it "returns modified objects" do
|
|
193
|
-
obj = @rev.object(
|
|
196
|
+
obj = @rev.object(3)
|
|
194
197
|
obj.value = :Other
|
|
195
198
|
@rev.add(@obj)
|
|
196
199
|
deleted = @rev.object(6)
|
|
@@ -214,6 +217,18 @@ describe HexaPDF::Revision do
|
|
|
214
217
|
obj.delete(:Type)
|
|
215
218
|
assert_equal([], @rev.each_modified_object.to_a)
|
|
216
219
|
end
|
|
220
|
+
|
|
221
|
+
it "doesn't return dictionaries that have direct HexaPDF::Object child objects" do
|
|
222
|
+
obj = @rev.object(6)
|
|
223
|
+
obj[:Array] = HexaPDF::PDFArray.new([1, 2]) # same value but differen #data instance
|
|
224
|
+
assert_equal([], @rev.each_modified_object.to_a)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "doesn't return signature objects" do
|
|
228
|
+
obj = @rev.object(2)
|
|
229
|
+
obj[:x] = :y
|
|
230
|
+
assert_equal([], @rev.each_modified_object.to_a)
|
|
231
|
+
end
|
|
217
232
|
end
|
|
218
233
|
|
|
219
234
|
describe "reset_objects" do
|
data/test/hexapdf/test_writer.rb
CHANGED
|
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
|
|
|
40
40
|
219
|
|
41
41
|
%%EOF
|
|
42
42
|
3 0 obj
|
|
43
|
-
<</Producer(HexaPDF version 0.26.
|
|
43
|
+
<</Producer(HexaPDF version 0.26.2)>>
|
|
44
44
|
endobj
|
|
45
45
|
xref
|
|
46
46
|
3 1
|
|
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
|
|
|
72
72
|
141
|
|
73
73
|
%%EOF
|
|
74
74
|
6 0 obj
|
|
75
|
-
<</Producer(HexaPDF version 0.26.
|
|
75
|
+
<</Producer(HexaPDF version 0.26.2)>>
|
|
76
76
|
endobj
|
|
77
77
|
2 0 obj
|
|
78
78
|
<</Length 10>>stream
|
|
@@ -206,7 +206,7 @@ describe HexaPDF::Writer do
|
|
|
206
206
|
<</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
|
|
207
207
|
endobj
|
|
208
208
|
5 0 obj
|
|
209
|
-
<</Producer(HexaPDF version 0.26.
|
|
209
|
+
<</Producer(HexaPDF version 0.26.2)>>
|
|
210
210
|
endobj
|
|
211
211
|
4 0 obj
|
|
212
212
|
<</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
|
|
@@ -30,6 +30,12 @@ describe HexaPDF::Type::ObjectStream do
|
|
|
30
30
|
stream: "1 0 5 2 5 [1 2]")
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
it "parses an associated stream the first time the stored objects are accessed" do
|
|
34
|
+
assert_nil(@obj.instance_variable_get(:@objects))
|
|
35
|
+
assert_equal(0, @obj.object_index(HexaPDF::Reference.new(1, 0)))
|
|
36
|
+
assert_equal(1, @obj.object_index(HexaPDF::Reference.new(5, 0)))
|
|
37
|
+
end
|
|
38
|
+
|
|
33
39
|
it "correctly parses stream data" do
|
|
34
40
|
data = @obj.parse_stream
|
|
35
41
|
assert_equal([5, 1], data.object_by_index(0))
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hexapdf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.26.
|
|
4
|
+
version: 0.26.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Thomas Leitner
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-10-
|
|
11
|
+
date: 2022-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cmdparse
|
|
@@ -706,7 +706,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
706
706
|
- !ruby/object:Gem::Version
|
|
707
707
|
version: '0'
|
|
708
708
|
requirements: []
|
|
709
|
-
rubygems_version: 3.
|
|
709
|
+
rubygems_version: 3.2.32
|
|
710
710
|
signing_key:
|
|
711
711
|
specification_version: 4
|
|
712
712
|
summary: HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|