hexapdf 1.3.0 → 1.4.1
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 +68 -0
- data/lib/hexapdf/cli/form.rb +10 -5
- data/lib/hexapdf/cli/images.rb +5 -1
- data/lib/hexapdf/cli.rb +3 -0
- data/lib/hexapdf/configuration.rb +10 -0
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +1 -2
- data/lib/hexapdf/document/annotations.rb +47 -0
- data/lib/hexapdf/document/layout.rb +73 -33
- data/lib/hexapdf/document/metadata.rb +10 -3
- data/lib/hexapdf/document.rb +10 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +7 -2
- data/lib/hexapdf/font/encoding/base.rb +27 -0
- data/lib/hexapdf/font/type1_wrapper.rb +1 -3
- data/lib/hexapdf/layout/box.rb +5 -0
- data/lib/hexapdf/layout/container_box.rb +63 -28
- data/lib/hexapdf/layout/style.rb +28 -13
- data/lib/hexapdf/layout/table_box.rb +20 -2
- data/lib/hexapdf/serializer.rb +7 -7
- data/lib/hexapdf/type/annotations/appearance_generator.rb +94 -16
- data/lib/hexapdf/type/annotations/interior_color.rb +1 -1
- data/lib/hexapdf/type/annotations/line.rb +1 -157
- data/lib/hexapdf/type/annotations/line_ending_styling.rb +208 -0
- data/lib/hexapdf/type/annotations/markup_annotation.rb +0 -1
- data/lib/hexapdf/type/annotations/polygon.rb +64 -0
- data/lib/hexapdf/type/annotations/polygon_polyline.rb +109 -0
- data/lib/hexapdf/type/annotations/polyline.rb +64 -0
- data/lib/hexapdf/type/annotations.rb +4 -0
- data/lib/hexapdf/type/font_type1.rb +12 -1
- data/lib/hexapdf/type/measure.rb +57 -0
- data/lib/hexapdf/type.rb +1 -0
- data/lib/hexapdf/utils/sorted_tree_node.rb +4 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +0 -1
- data/test/hexapdf/document/test_annotations.rb +20 -0
- data/test/hexapdf/document/test_layout.rb +16 -10
- data/test/hexapdf/document/test_metadata.rb +13 -1
- data/test/hexapdf/encryption/test_standard_security_handler.rb +2 -1
- data/test/hexapdf/font/encoding/test_base.rb +20 -0
- data/test/hexapdf/layout/test_box.rb +8 -0
- data/test/hexapdf/layout/test_container_box.rb +34 -6
- data/test/hexapdf/layout/test_page_style.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +20 -1
- data/test/hexapdf/layout/test_table_box.rb +14 -1
- data/test/hexapdf/test_dictionary_fields.rb +1 -0
- data/test/hexapdf/test_document.rb +1 -0
- data/test/hexapdf/test_serializer.rb +2 -1
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +126 -0
- data/test/hexapdf/type/annotations/test_line.rb +0 -25
- data/test/hexapdf/type/annotations/test_line_ending_styling.rb +42 -0
- data/test/hexapdf/type/annotations/test_polygon_polyline.rb +29 -0
- data/test/hexapdf/type/annotations/test_widget.rb +8 -0
- data/test/hexapdf/type/test_font_type1.rb +14 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +11 -1
- metadata +9 -2
@@ -13,6 +13,17 @@ describe HexaPDF::Document::Metadata do
|
|
13
13
|
|
14
14
|
it "parses the info dictionary on creation" do
|
15
15
|
assert_equal('Title', @metadata.title)
|
16
|
+
|
17
|
+
time = Time.now
|
18
|
+
@doc.trailer.info[:ModDate] = ''
|
19
|
+
assert_nil(HexaPDF::Document::Metadata.new(@doc).modification_date)
|
20
|
+
@doc.trailer.info[:ModDate] = time
|
21
|
+
assert_equal(time, HexaPDF::Document::Metadata.new(@doc).modification_date)
|
22
|
+
@doc.trailer.info[:CreationDate] = ''
|
23
|
+
assert_nil(HexaPDF::Document::Metadata.new(@doc).creation_date)
|
24
|
+
@doc.trailer.info[:CreationDate] = time
|
25
|
+
assert_equal(time, HexaPDF::Document::Metadata.new(@doc).creation_date)
|
26
|
+
|
16
27
|
@doc.trailer.info[:Trapped] = :Unknown
|
17
28
|
assert_nil(HexaPDF::Document::Metadata.new(@doc).trapped)
|
18
29
|
@doc.trailer.info[:Trapped] = :True
|
@@ -213,6 +224,7 @@ describe HexaPDF::Document::Metadata do
|
|
213
224
|
title.language = 'de'
|
214
225
|
@metadata.title(['Title', title])
|
215
226
|
@metadata.author(['Author 1', 'Author 2'])
|
227
|
+
@metadata.creation_date('')
|
216
228
|
@metadata.register_property_type('dc', 'other', 'URI')
|
217
229
|
@metadata.property('dc', 'other', 'https://test.org/example')
|
218
230
|
@metadata.property('pdfaid', 'part', 3)
|
@@ -243,7 +255,7 @@ describe HexaPDF::Document::Metadata do
|
|
243
255
|
</rdf:Description>
|
244
256
|
<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
|
245
257
|
<xmp:CreatorTool>Creator</xmp:CreatorTool>
|
246
|
-
<xmp:CreateDate
|
258
|
+
<xmp:CreateDate></xmp:CreateDate>
|
247
259
|
<xmp:ModifyDate>#{@metadata.send(:xmp_date, @time)}</xmp:ModifyDate>
|
248
260
|
</rdf:Description>
|
249
261
|
<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">
|
@@ -188,10 +188,11 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
188
188
|
refute(dict.value.key?(:Perms))
|
189
189
|
crypt_filter.call(dict, 4, :AESV2, 16)
|
190
190
|
|
191
|
-
dict = @handler.set_up_encryption(key_length: 256, algorithm: :aes)
|
191
|
+
dict = @handler.set_up_encryption(key_length: 256, algorithm: :aes, owner_password: 'hexapdf')
|
192
192
|
assert_equal(32, dict[:UE].length)
|
193
193
|
assert_equal(32, dict[:OE].length)
|
194
194
|
assert_equal(16, dict[:Perms].length)
|
195
|
+
assert(@handler.send(:owner_password_valid?, 'hexapdf'))
|
195
196
|
crypt_filter.call(dict, 6, :AESV3, 32)
|
196
197
|
end
|
197
198
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
|
+
require 'hexapdf/font/encoding'
|
4
5
|
require 'hexapdf/font/encoding/base'
|
5
6
|
|
6
7
|
describe HexaPDF::Font::Encoding::Base do
|
@@ -42,4 +43,23 @@ describe HexaPDF::Font::Encoding::Base do
|
|
42
43
|
assert_nil(@base.code(:Unknown))
|
43
44
|
end
|
44
45
|
end
|
46
|
+
|
47
|
+
describe "to_compact_array" do
|
48
|
+
before do
|
49
|
+
@base.code_to_name[66] = :B
|
50
|
+
@base.code_to_name[67] = :C
|
51
|
+
@base.code_to_name[20] = :space
|
52
|
+
@base.code_to_name[28] = :D
|
53
|
+
@base.code_to_name[29] = :E
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns the difference array" do
|
57
|
+
assert_equal([20, :space, 28, :D, :E, 65, :A, :B, :C], @base.to_compact_array)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "ignores the codes that are the same in the base encoding" do
|
61
|
+
std_encoding = HexaPDF::Font::Encoding.for_name(:StandardEncoding)
|
62
|
+
assert_equal([20, :space, 28, :D, :E, ], @base.to_compact_array(base_encoding: std_encoding))
|
63
|
+
end
|
64
|
+
end
|
45
65
|
end
|
@@ -5,6 +5,14 @@ require 'hexapdf/document'
|
|
5
5
|
require 'hexapdf/layout/box'
|
6
6
|
|
7
7
|
describe HexaPDF::Layout::Box::FitResult do
|
8
|
+
it "allows setting the status to failure" do
|
9
|
+
result = HexaPDF::Layout::Box::FitResult.new(nil)
|
10
|
+
result.overflow!
|
11
|
+
refute(result.failure?)
|
12
|
+
result.failure!
|
13
|
+
assert(result.failure?)
|
14
|
+
end
|
15
|
+
|
8
16
|
it "shows the box's mask area on #draw when using debug output" do
|
9
17
|
doc = HexaPDF::Document.new(config: {'debug' => true})
|
10
18
|
canvas = doc.pages.add.canvas
|
@@ -10,6 +10,10 @@ describe HexaPDF::Layout::ContainerBox do
|
|
10
10
|
@frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
11
11
|
end
|
12
12
|
|
13
|
+
def child_box(height: 0, width: 0, **style_properties)
|
14
|
+
@doc.layout.box(height: height, width: width, style: style_properties)
|
15
|
+
end
|
16
|
+
|
13
17
|
def create_box(children, **kwargs)
|
14
18
|
HexaPDF::Layout::ContainerBox.new(children: Array(children), **kwargs)
|
15
19
|
end
|
@@ -48,17 +52,41 @@ describe HexaPDF::Layout::ContainerBox do
|
|
48
52
|
|
49
53
|
describe "fit_content" do
|
50
54
|
it "fits the children according to their mask_mode, valign, and align style properties" do
|
51
|
-
box = create_box([
|
52
|
-
|
53
|
-
|
55
|
+
box = create_box([child_box(height: 20),
|
56
|
+
child_box(height: 20, valign: :bottom, mask_mode: :fill_horizontal),
|
57
|
+
child_box(width: 20, align: :right, mask_mode: :fill_vertical)])
|
54
58
|
check_box(box, 100, 100, [[0, 80], [0, 0], [80, 20]])
|
55
59
|
end
|
56
60
|
|
57
61
|
it "respects the initially set width/height as well as border/padding" do
|
58
|
-
box = create_box(
|
62
|
+
box = create_box(child_box(height: 20), height: 50, width: 40,
|
59
63
|
style: {padding: 2, border: {width: 3}})
|
60
64
|
check_box(box, 40, 50, [[5, 75]])
|
61
65
|
end
|
66
|
+
|
67
|
+
it "fails if splitting is not allowed and the content is too big" do
|
68
|
+
box = create_box([child_box(height: 80), child_box(height: 30)])
|
69
|
+
box.fit(@frame.available_width, @frame.available_height, @frame)
|
70
|
+
assert(box.fit_result.failure?)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "splits the box if splitting is allowed and the content is too big" do
|
74
|
+
box = create_box([child_box(height: 80), child_box(height: 30)], splitable: true)
|
75
|
+
box.fit(@frame.available_width, @frame.available_height, @frame)
|
76
|
+
assert(box.fit_result.overflow?)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "split_content" do
|
81
|
+
it "assigns the overflown boxes to the split box" do
|
82
|
+
box = create_box([child_box(height: 80), child_box(height: 30)], splitable: true)
|
83
|
+
box.fit(@frame.available_width, @frame.available_height, @frame)
|
84
|
+
assert(box.fit_result.overflow?)
|
85
|
+
box_a, box_b = box.split
|
86
|
+
assert_same(box, box_a)
|
87
|
+
assert(box_b.split_box?)
|
88
|
+
assert_equal(1, box_b.children.size)
|
89
|
+
end
|
62
90
|
end
|
63
91
|
|
64
92
|
describe "draw_content" do
|
@@ -67,10 +95,10 @@ describe HexaPDF::Layout::ContainerBox do
|
|
67
95
|
end
|
68
96
|
|
69
97
|
it "draws the result onto the canvas" do
|
70
|
-
|
98
|
+
cbox = @doc.layout.box(height: 20) do |canvas, b|
|
71
99
|
canvas.line(0, 0, b.content_width, b.content_height).end_path
|
72
100
|
end
|
73
|
-
box = create_box(
|
101
|
+
box = create_box(cbox)
|
74
102
|
box.fit(100, 100, @frame)
|
75
103
|
box.draw(@canvas, 0, 50)
|
76
104
|
assert_operators(@canvas.contents, [[:save_graphics_state],
|
@@ -136,6 +136,19 @@ describe HexaPDF::Layout::Style::Quad do
|
|
136
136
|
assert_equal(new_quad.bottom, quad.bottom)
|
137
137
|
assert_equal(new_quad.left, quad.left)
|
138
138
|
end
|
139
|
+
|
140
|
+
it "works with a Hash as value" do
|
141
|
+
quad = create_quad(top: 5, left: 10)
|
142
|
+
assert_equal(5, quad.top)
|
143
|
+
assert_equal(0, quad.bottom)
|
144
|
+
assert_equal(10, quad.left)
|
145
|
+
assert_equal(0, quad.right)
|
146
|
+
quad.set(right: 7)
|
147
|
+
assert_equal(5, quad.top)
|
148
|
+
assert_equal(0, quad.bottom)
|
149
|
+
assert_equal(10, quad.left)
|
150
|
+
assert_equal(7, quad.right)
|
151
|
+
end
|
139
152
|
end
|
140
153
|
|
141
154
|
it "can be asked if it contains only a single value" do
|
@@ -744,7 +757,7 @@ describe HexaPDF::Layout::Style do
|
|
744
757
|
@style.line_spacing = 1.2
|
745
758
|
assert_equal(0.005, @style.scaled_font_size)
|
746
759
|
assert_equal([[:font, @style.font], [:font_size, 5], [:horizontal_scaling, 100],
|
747
|
-
[:line_spacing, @style.line_spacing]
|
760
|
+
[:line_spacing, @style.line_spacing]],
|
748
761
|
@style.each_property.to_a.sort)
|
749
762
|
end
|
750
763
|
end
|
@@ -887,6 +900,9 @@ describe HexaPDF::Layout::Style do
|
|
887
900
|
end
|
888
901
|
|
889
902
|
it "handles subscript" do
|
903
|
+
@style.subscript = false
|
904
|
+
assert_equal(10, @style.calculated_font_size)
|
905
|
+
assert_equal(0, @style.calculated_text_rise)
|
890
906
|
@style.subscript = true
|
891
907
|
assert_in_delta(5.83, @style.calculated_font_size)
|
892
908
|
assert_in_delta(0.00583, @style.scaled_font_size, 0.000001)
|
@@ -894,6 +910,9 @@ describe HexaPDF::Layout::Style do
|
|
894
910
|
end
|
895
911
|
|
896
912
|
it "handles superscript" do
|
913
|
+
@style.superscript = false
|
914
|
+
assert_equal(10, @style.calculated_font_size)
|
915
|
+
assert_equal(0, @style.calculated_text_rise)
|
897
916
|
@style.superscript = true
|
898
917
|
assert_in_delta(5.83, @style.calculated_font_size)
|
899
918
|
assert_in_delta(3.30, @style.calculated_text_rise)
|
@@ -116,6 +116,18 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
116
116
|
assert_equal(12, cell.preferred_height)
|
117
117
|
end
|
118
118
|
|
119
|
+
it "respects the set minimum height of the cell" do
|
120
|
+
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 10), min_height: 30)
|
121
|
+
assert(cell.fit(100, 25, @frame).failure?)
|
122
|
+
|
123
|
+
assert(cell.fit(100, 100, @frame).success?)
|
124
|
+
assert_equal(30, cell.height)
|
125
|
+
|
126
|
+
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 20), min_height: 2)
|
127
|
+
assert(cell.fit(100, 100, @frame).success?)
|
128
|
+
assert_equal(32, cell.height)
|
129
|
+
end
|
130
|
+
|
119
131
|
it "doesn't fit children that are too big" do
|
120
132
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 300, height: 20))
|
121
133
|
assert(cell.fit(100, 100, @frame).failure?)
|
@@ -262,7 +274,7 @@ describe HexaPDF::Layout::TableBox::Cells do
|
|
262
274
|
end
|
263
275
|
|
264
276
|
it "sets the correct information on the created cells" do
|
265
|
-
cells = create_cells([[:a, {col_span: 2, content: :b}],
|
277
|
+
cells = create_cells([[:a, {col_span: 2, content: :b, min_height: 30}],
|
266
278
|
[{col_span: 2, row_span: 2, content: :c}, {row_span: 2, content: :d}]])
|
267
279
|
assert_equal(0, cells[0, 0].row)
|
268
280
|
assert_equal(0, cells[0, 0].column)
|
@@ -272,6 +284,7 @@ describe HexaPDF::Layout::TableBox::Cells do
|
|
272
284
|
assert_equal(1, cells[0, 1].column)
|
273
285
|
assert_equal(1, cells[0, 1].row_span)
|
274
286
|
assert_equal(2, cells[0, 1].col_span)
|
287
|
+
assert_equal(30, cells[0, 1].instance_variable_get(:@min_height))
|
275
288
|
assert_equal(1, cells[1, 0].row)
|
276
289
|
assert_equal(0, cells[1, 0].column)
|
277
290
|
assert_equal(2, cells[1, 0].row_span)
|
@@ -193,6 +193,7 @@ describe HexaPDF::DictionaryFields do
|
|
193
193
|
["D:19981223195210Z00'00", [1998, 12, 23, 19, 52, 10, "+00:00"]],
|
194
194
|
["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
|
195
195
|
["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], # no trailing ', as per PDF 2.0
|
196
|
+
["D:19981223195210-08'00''", [1998, 12, 23, 19, 52, 10, "-08:00"]], # two trailing '
|
196
197
|
["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], # TZ hour too large
|
197
198
|
["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], # TZ min too large
|
198
199
|
["D:19982423195210-08'00'", [1998, 12, 23, 19, 52, 10, "-08:00"]], # months too large
|
@@ -181,7 +181,8 @@ describe HexaPDF::Serializer do
|
|
181
181
|
|
182
182
|
it "encrypts strings in indirect PDF objects" do
|
183
183
|
assert_serialized("(enc:1:test)", HexaPDF::Object.new("test", oid: 1))
|
184
|
-
assert_serialized("<</x[(enc:1
|
184
|
+
assert_serialized("<</x[(enc:1:\xFE\xFF\x00t\x00e\x00s\x00t\x00\xF6)]>>".b,
|
185
|
+
HexaPDF::Object.new({x: ["testö"]}, oid: 1))
|
185
186
|
end
|
186
187
|
|
187
188
|
it "doesn't encrypt strings in direct PDF objects" do
|
@@ -479,4 +479,130 @@ describe HexaPDF::Type::Annotations::AppearanceGenerator do
|
|
479
479
|
[:stroke_path]])
|
480
480
|
end
|
481
481
|
end
|
482
|
+
|
483
|
+
describe "polygon/polyline" do
|
484
|
+
before do
|
485
|
+
@polyline = @doc.add({Type: :Annot, Subtype: :PolyLine, C: [0],
|
486
|
+
Vertices: [100, 100, 200, 150, 210, 80]})
|
487
|
+
@generator = HexaPDF::Type::Annotations::AppearanceGenerator.new(@polyline)
|
488
|
+
end
|
489
|
+
|
490
|
+
it "sets the print flag and unsets the hidden flag" do
|
491
|
+
@polyline.flag(:hidden)
|
492
|
+
@generator.create_appearance
|
493
|
+
assert(@polyline.flagged?(:print))
|
494
|
+
refute(@polyline.flagged?(:hidden))
|
495
|
+
end
|
496
|
+
|
497
|
+
it "creates a simple polyline" do
|
498
|
+
@generator.create_appearance
|
499
|
+
assert_equal([96, 76, 214, 154], @polyline[:Rect])
|
500
|
+
assert_equal([96, 76, 214, 154], @polyline.appearance[:BBox])
|
501
|
+
assert_operators(@polyline.appearance.stream,
|
502
|
+
[[:move_to, [100, 100]],
|
503
|
+
[:line_to, [200, 150]],
|
504
|
+
[:line_to, [210, 80]],
|
505
|
+
[:stroke_path]])
|
506
|
+
end
|
507
|
+
|
508
|
+
it "creates a simple polygon" do
|
509
|
+
@polyline[:Subtype] = :Polygon
|
510
|
+
@generator.create_appearance
|
511
|
+
assert_operators(@polyline.appearance.stream,
|
512
|
+
[[:move_to, [100, 100]],
|
513
|
+
[:line_to, [200, 150]],
|
514
|
+
[:line_to, [210, 80]],
|
515
|
+
[:close_subpath],
|
516
|
+
[:stroke_path]])
|
517
|
+
end
|
518
|
+
|
519
|
+
describe "stroke color" do
|
520
|
+
it "uses the specified border color for stroking operations" do
|
521
|
+
@polyline.border_style(color: "red")
|
522
|
+
@generator.create_appearance
|
523
|
+
assert_operators(@polyline.appearance.stream,
|
524
|
+
[:set_device_rgb_stroking_color, [1, 0, 0]], range: 0)
|
525
|
+
assert_operators(@polyline.appearance.stream,
|
526
|
+
[:stroke_path], range: 4)
|
527
|
+
end
|
528
|
+
|
529
|
+
it "works with a transparent border" do
|
530
|
+
@polyline.border_style(color: :transparent)
|
531
|
+
@generator.create_appearance
|
532
|
+
assert_operators(@polyline.appearance.stream, [:end_path], range: 3)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
describe "interior color" do
|
537
|
+
it "uses the specified interior color for non-stroking operations" do
|
538
|
+
@polyline[:Subtype] = :Polygon
|
539
|
+
@polyline.border_style(color: :transparent)
|
540
|
+
@polyline.interior_color("red")
|
541
|
+
@generator.create_appearance
|
542
|
+
assert_operators(@polyline.appearance.stream,
|
543
|
+
[:set_device_rgb_non_stroking_color, [1, 0, 0]], range: 0)
|
544
|
+
assert_operators(@polyline.appearance.stream,
|
545
|
+
[:fill_path_non_zero], range: 5)
|
546
|
+
end
|
547
|
+
|
548
|
+
it "works together with the stroke color" do
|
549
|
+
@polyline[:Subtype] = :Polygon
|
550
|
+
@polyline.interior_color("red")
|
551
|
+
@generator.create_appearance
|
552
|
+
assert_operators(@polyline.appearance.stream,
|
553
|
+
[:set_device_rgb_non_stroking_color, [1, 0, 0]], range: 0)
|
554
|
+
assert_operators(@polyline.appearance.stream,
|
555
|
+
[:fill_and_stroke_path_non_zero], range: 5)
|
556
|
+
end
|
557
|
+
|
558
|
+
it "works if neither interior nor border color is used" do
|
559
|
+
@polyline[:Subtype] = :Polygon
|
560
|
+
@polyline.interior_color(:transparent)
|
561
|
+
@polyline.border_style(color: :transparent)
|
562
|
+
@generator.create_appearance
|
563
|
+
assert_operators(@polyline.appearance.stream,
|
564
|
+
[:end_path], range: 4)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
it "sets the specified border line width" do
|
569
|
+
@polyline.border_style(width: 4)
|
570
|
+
@generator.create_appearance
|
571
|
+
assert_operators(@polyline.appearance.stream,
|
572
|
+
[:set_line_width, [4]], range: 0)
|
573
|
+
end
|
574
|
+
|
575
|
+
it "sets the specified line dash pattern if it is an array" do
|
576
|
+
@polyline.border_style(style: [5, 2])
|
577
|
+
@generator.create_appearance
|
578
|
+
assert_operators(@polyline.appearance.stream,
|
579
|
+
[:set_line_dash_pattern, [[5, 2], 0]], range: 0)
|
580
|
+
end
|
581
|
+
|
582
|
+
it "sets the specified opacity" do
|
583
|
+
@polyline.opacity(fill_alpha: 0.5, stroke_alpha: 0.5)
|
584
|
+
@generator.create_appearance
|
585
|
+
assert_operators(@polyline.appearance.stream,
|
586
|
+
[:set_graphics_state_parameters, [:GS1]], range: 0)
|
587
|
+
end
|
588
|
+
|
589
|
+
it "draws the specified line ending style" do
|
590
|
+
@polyline.line_ending_style(start_style: :open_arrow, end_style: :rclosed_arrow)
|
591
|
+
@polyline.border_style(width: 2)
|
592
|
+
@polyline.interior_color("red")
|
593
|
+
@generator.create_appearance
|
594
|
+
assert_equal([86, 52, 238, 158], @polyline[:Rect])
|
595
|
+
assert_equal([86, 52, 238, 158], @polyline.appearance[:BBox])
|
596
|
+
assert_operators(@polyline.appearance.stream,
|
597
|
+
[[:move_to, [109.917818, 115.021215]],
|
598
|
+
[:line_to, [100, 100]],
|
599
|
+
[:line_to, [117.967662, 98.921525]],
|
600
|
+
[:stroke_path],
|
601
|
+
[:move_to, [221.114086, 94.158993]],
|
602
|
+
[:line_to, [210, 80]],
|
603
|
+
[:line_to, [203.294995, 96.704578]],
|
604
|
+
[:close_subpath],
|
605
|
+
[:fill_and_stroke_path_non_zero]], range: 6..-1)
|
606
|
+
end
|
607
|
+
end
|
482
608
|
end
|
@@ -26,31 +26,6 @@ describe HexaPDF::Type::Annotations::Line do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
describe "line_ending_style" do
|
30
|
-
it "returns the current style" do
|
31
|
-
assert_kind_of(HexaPDF::Type::Annotations::Line::LineEndingStyle, @line.line_ending_style)
|
32
|
-
assert_equal([:none, :none], @line.line_ending_style.to_a)
|
33
|
-
@line[:LE] = [:Diamond, :OpenArrow]
|
34
|
-
assert_equal([:diamond, :open_arrow], @line.line_ending_style.to_a)
|
35
|
-
@line[:LE] = [:Diamond, :Unknown]
|
36
|
-
assert_equal([:diamond, :none], @line.line_ending_style.to_a)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "sets the style" do
|
40
|
-
assert_same(@line, @line.line_ending_style(start_style: :OpenArrow))
|
41
|
-
assert_equal([:OpenArrow, :None], @line[:LE])
|
42
|
-
assert_same(@line, @line.line_ending_style(end_style: :open_arrow))
|
43
|
-
assert_equal([:OpenArrow, :OpenArrow], @line[:LE])
|
44
|
-
assert_same(@line, @line.line_ending_style(start_style: :circle, end_style: :ClosedArrow))
|
45
|
-
assert_equal([:Circle, :ClosedArrow], @line[:LE])
|
46
|
-
end
|
47
|
-
|
48
|
-
it "raises an error for unknown styles" do
|
49
|
-
assert_raises(ArgumentError) { @line.line_ending_style(start_style: :unknown) }
|
50
|
-
assert_raises(ArgumentError) { @line.line_ending_style(end_style: :unknown) }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
29
|
describe "leader_line_length" do
|
55
30
|
it "returns the leader line length" do
|
56
31
|
assert_equal(0, @line.leader_line_length)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/annotations/line_ending_styling'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::Annotations::LineEndingStyling do
|
8
|
+
class TestAnnotLineEndingStyling < HexaPDF::Type::Annotation
|
9
|
+
define_field :LE, type: HexaPDF::PDFArray, default: [:None, :None]
|
10
|
+
include HexaPDF::Type::Annotations::LineEndingStyling
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
@doc = HexaPDF::Document.new
|
15
|
+
@annot = @doc.wrap({Type: :Annot}, type: TestAnnotLineEndingStyling)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "line_ending_style" do
|
19
|
+
it "returns the current style" do
|
20
|
+
assert_kind_of(HexaPDF::Type::Annotations::Line::LineEndingStyle, @annot.line_ending_style)
|
21
|
+
assert_equal([:none, :none], @annot.line_ending_style.to_a)
|
22
|
+
@annot[:LE] = [:Diamond, :OpenArrow]
|
23
|
+
assert_equal([:diamond, :open_arrow], @annot.line_ending_style.to_a)
|
24
|
+
@annot[:LE] = [:Diamond, :Unknown]
|
25
|
+
assert_equal([:diamond, :none], @annot.line_ending_style.to_a)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets the style" do
|
29
|
+
assert_same(@annot, @annot.line_ending_style(start_style: :OpenArrow))
|
30
|
+
assert_equal([:OpenArrow, :None], @annot[:LE])
|
31
|
+
assert_same(@annot, @annot.line_ending_style(end_style: :open_arrow))
|
32
|
+
assert_equal([:OpenArrow, :OpenArrow], @annot[:LE])
|
33
|
+
assert_same(@annot, @annot.line_ending_style(start_style: :circle, end_style: :ClosedArrow))
|
34
|
+
assert_equal([:Circle, :ClosedArrow], @annot[:LE])
|
35
|
+
end
|
36
|
+
|
37
|
+
it "raises an error for unknown styles" do
|
38
|
+
assert_raises(ArgumentError) { @annot.line_ending_style(start_style: :unknown) }
|
39
|
+
assert_raises(ArgumentError) { @annot.line_ending_style(end_style: :unknown) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/annotations/polygon_polyline'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::Annotations::PolygonPolyline do
|
8
|
+
before do
|
9
|
+
@doc = HexaPDF::Document.new
|
10
|
+
@annot = @doc.add({Type: :Annot, Subtype: :Polyline, Rect: [0, 0, 0, 0]},
|
11
|
+
type: HexaPDF::Type::Annotations::PolygonPolyline)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "vertices" do
|
15
|
+
it "returns the coordinates of the vertices" do
|
16
|
+
@annot[:Vertices] = [10, 20, 30, 40, 50, 60]
|
17
|
+
assert_equal([10, 20, 30, 40, 50, 60], @annot.vertices)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets the vertices" do
|
21
|
+
assert_same(@annot, @annot.vertices(1, 2, 3, 4, 5, 6))
|
22
|
+
assert_equal([1, 2, 3, 4, 5, 6], @annot[:Vertices])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises an ArgumentError if an uneven number of arguments is provided" do
|
26
|
+
assert_raises(ArgumentError) { @annot.vertices(1, 2, 3) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -52,6 +52,14 @@ describe HexaPDF::Type::Annotations::Widget do
|
|
52
52
|
assert_kind_of(HexaPDF::Type::AcroForm::TextField, result)
|
53
53
|
refute_same(@widget.data, result.data)
|
54
54
|
end
|
55
|
+
|
56
|
+
it "works when the type of the field is defined higher up in the field hierarchy" do
|
57
|
+
@widget[:Parent] = {T: 'parent', Kids: [@widget]}
|
58
|
+
@widget[:Parent][:Parent] = {FT: :Tx, Kids: [@widget[:Parent]]}
|
59
|
+
result = @widget.form_field
|
60
|
+
assert_kind_of(HexaPDF::Type::AcroForm::TextField, result)
|
61
|
+
refute_same(@widget.data, result.data)
|
62
|
+
end
|
55
63
|
end
|
56
64
|
|
57
65
|
describe "background_color" do
|
@@ -143,5 +143,19 @@ describe HexaPDF::Type::FontType1 do
|
|
143
143
|
@font[:Encoding] = :Other
|
144
144
|
refute(@font.validate)
|
145
145
|
end
|
146
|
+
|
147
|
+
it "works around certain invalid PDFs with a /SymbolEncoding value for /Encoding" do
|
148
|
+
@font[:Encoding] = :SymbolEncoding
|
149
|
+
@font[:BaseFont] = :Symbol
|
150
|
+
assert(@font.validate)
|
151
|
+
refute(@font.key?(:Encoding))
|
152
|
+
end
|
153
|
+
|
154
|
+
it "works around certain invalid PDFs with a /StandardEncoding value for /Encoding" do
|
155
|
+
@font[:Encoding] = :StandardEncoding
|
156
|
+
assert(@font.validate)
|
157
|
+
assert(:WinAnsiEncoding, @font[:Encoding][:BaseEncoding])
|
158
|
+
assert_equal([39, :quoteright, 96, :quoteleft], @font[:Encoding][:Differences][0, 4])
|
159
|
+
end
|
146
160
|
end
|
147
161
|
end
|
@@ -219,11 +219,21 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
219
219
|
it "checks that leaf node containers have an even number of entries" do
|
220
220
|
@kid11[:Names].delete_at(0)
|
221
221
|
refute(@kid11.validate do |message, c|
|
222
|
-
assert_match(/odd number/, message)
|
222
|
+
assert_match(/leaf.*odd number/, message)
|
223
223
|
refute(c)
|
224
224
|
end)
|
225
225
|
end
|
226
226
|
|
227
|
+
it "corrects a root node container with an odd number of entries" do
|
228
|
+
@root.value.clear
|
229
|
+
@root[:Names] = ['Test']
|
230
|
+
assert(@root.validate do |message, c|
|
231
|
+
assert_match(/root.*odd number/, message)
|
232
|
+
assert(c)
|
233
|
+
end)
|
234
|
+
assert(@root[:Names].empty?)
|
235
|
+
end
|
236
|
+
|
227
237
|
it "checks that the keys are of the correct type" do
|
228
238
|
@kid11[:Names][2] = 5
|
229
239
|
refute(@kid11.validate do |message, c|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-09-23 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: cmdparse
|
@@ -514,8 +514,12 @@ files:
|
|
514
514
|
- lib/hexapdf/type/annotations/circle.rb
|
515
515
|
- lib/hexapdf/type/annotations/interior_color.rb
|
516
516
|
- lib/hexapdf/type/annotations/line.rb
|
517
|
+
- lib/hexapdf/type/annotations/line_ending_styling.rb
|
517
518
|
- lib/hexapdf/type/annotations/link.rb
|
518
519
|
- lib/hexapdf/type/annotations/markup_annotation.rb
|
520
|
+
- lib/hexapdf/type/annotations/polygon.rb
|
521
|
+
- lib/hexapdf/type/annotations/polygon_polyline.rb
|
522
|
+
- lib/hexapdf/type/annotations/polyline.rb
|
519
523
|
- lib/hexapdf/type/annotations/square.rb
|
520
524
|
- lib/hexapdf/type/annotations/square_circle.rb
|
521
525
|
- lib/hexapdf/type/annotations/text.rb
|
@@ -539,6 +543,7 @@ files:
|
|
539
543
|
- lib/hexapdf/type/info.rb
|
540
544
|
- lib/hexapdf/type/mark_information.rb
|
541
545
|
- lib/hexapdf/type/marked_content_reference.rb
|
546
|
+
- lib/hexapdf/type/measure.rb
|
542
547
|
- lib/hexapdf/type/metadata.rb
|
543
548
|
- lib/hexapdf/type/names.rb
|
544
549
|
- lib/hexapdf/type/namespace.rb
|
@@ -800,7 +805,9 @@ files:
|
|
800
805
|
- test/hexapdf/type/annotations/test_border_styling.rb
|
801
806
|
- test/hexapdf/type/annotations/test_interior_color.rb
|
802
807
|
- test/hexapdf/type/annotations/test_line.rb
|
808
|
+
- test/hexapdf/type/annotations/test_line_ending_styling.rb
|
803
809
|
- test/hexapdf/type/annotations/test_markup_annotation.rb
|
810
|
+
- test/hexapdf/type/annotations/test_polygon_polyline.rb
|
804
811
|
- test/hexapdf/type/annotations/test_text.rb
|
805
812
|
- test/hexapdf/type/annotations/test_widget.rb
|
806
813
|
- test/hexapdf/type/test_annotation.rb
|