hexapdf 1.2.0 → 1.3.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 +45 -0
- data/README.md +1 -1
- data/lib/hexapdf/cli/inspect.rb +13 -4
- data/lib/hexapdf/composer.rb +14 -0
- data/lib/hexapdf/configuration.rb +5 -0
- data/lib/hexapdf/document/annotations.rb +60 -2
- data/lib/hexapdf/document/layout.rb +45 -6
- data/lib/hexapdf/error.rb +11 -3
- data/lib/hexapdf/font/true_type/subsetter.rb +15 -2
- data/lib/hexapdf/layout/style.rb +101 -7
- data/lib/hexapdf/object.rb +2 -2
- data/lib/hexapdf/pdf_array.rb +25 -3
- data/lib/hexapdf/tokenizer.rb +4 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +57 -8
- data/lib/hexapdf/type/acro_form/field.rb +1 -0
- data/lib/hexapdf/type/acro_form/form.rb +7 -6
- data/lib/hexapdf/type/annotation.rb +12 -0
- data/lib/hexapdf/type/annotations/appearance_generator.rb +75 -0
- data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
- data/lib/hexapdf/type/annotations/circle.rb +65 -0
- data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
- data/lib/hexapdf/type/annotations/line.rb +4 -35
- data/lib/hexapdf/type/annotations/square.rb +65 -0
- data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
- data/lib/hexapdf/type/annotations/widget.rb +50 -20
- data/lib/hexapdf/type/annotations.rb +5 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_annotations.rb +22 -0
- data/test/hexapdf/document/test_layout.rb +24 -2
- data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
- data/test/hexapdf/layout/test_style.rb +27 -2
- data/test/hexapdf/test_composer.rb +7 -0
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_pdf_array.rb +36 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +78 -3
- data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
- data/test/hexapdf/type/acro_form/test_field.rb +5 -0
- data/test/hexapdf/type/acro_form/test_form.rb +17 -1
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +84 -0
- data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
- data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
- data/test/hexapdf/type/annotations/test_line.rb +0 -20
- data/test/hexapdf/type/annotations/test_widget.rb +35 -0
- metadata +9 -2
@@ -395,4 +395,88 @@ describe HexaPDF::Type::Annotations::AppearanceGenerator do
|
|
395
395
|
end
|
396
396
|
end
|
397
397
|
end
|
398
|
+
|
399
|
+
describe "square/circle" do
|
400
|
+
before do
|
401
|
+
@square = @doc.add({Type: :Annot, Subtype: :Square, Rect: [100, 100, 200, 150], C: [0],
|
402
|
+
BS: {W: 2}})
|
403
|
+
@generator = HexaPDF::Type::Annotations::AppearanceGenerator.new(@square)
|
404
|
+
end
|
405
|
+
|
406
|
+
it "sets the print flag and unsets the hidden flag" do
|
407
|
+
@square.flag(:hidden)
|
408
|
+
@generator.create_appearance
|
409
|
+
assert(@square.flagged?(:print))
|
410
|
+
refute(@square.flagged?(:hidden))
|
411
|
+
end
|
412
|
+
|
413
|
+
it "creates the /RD entry if it doesn't exist and adjusts the /Rect" do
|
414
|
+
@generator.create_appearance
|
415
|
+
assert_equal([99, 99, 201, 151], @square[:Rect])
|
416
|
+
assert_equal([0, 0, 102, 52], @square.appearance[:BBox])
|
417
|
+
assert_operators(@square.appearance.stream,
|
418
|
+
[[:set_line_width, [2]],
|
419
|
+
[:append_rectangle, [1, 1, 100, 50]],
|
420
|
+
[:stroke_path]])
|
421
|
+
end
|
422
|
+
|
423
|
+
it "uses an existing /RD entry" do
|
424
|
+
@square[:RD] = [2, 4, 6, 8]
|
425
|
+
@generator.create_appearance
|
426
|
+
assert_equal([100, 100, 200, 150], @square[:Rect])
|
427
|
+
assert_equal([0, 0, 100, 50], @square.appearance[:BBox])
|
428
|
+
assert_operators(@square.appearance.stream,
|
429
|
+
[[:set_line_width, [2]],
|
430
|
+
[:append_rectangle, [3, 9, 90, 36]],
|
431
|
+
[:stroke_path]])
|
432
|
+
end
|
433
|
+
|
434
|
+
it "can apply just a fill color without a stroke color" do
|
435
|
+
@square.delete(:C)
|
436
|
+
@square.interior_color(255, 0, 0)
|
437
|
+
@generator.create_appearance
|
438
|
+
assert_operators(@square.appearance.stream,
|
439
|
+
[[:set_device_rgb_non_stroking_color, [1, 0, 0]],
|
440
|
+
[:set_line_width, [2]],
|
441
|
+
[:append_rectangle, [1, 1, 100, 50]],
|
442
|
+
[:fill_path_non_zero]])
|
443
|
+
end
|
444
|
+
|
445
|
+
it "applies all set styling options" do
|
446
|
+
@square.border_style(color: [255, 0, 0], width: 10, style: [2, 1])
|
447
|
+
@square.interior_color(0, 255, 0)
|
448
|
+
@square.opacity(fill_alpha: 0.5, stroke_alpha: 0.5)
|
449
|
+
@generator.create_appearance
|
450
|
+
assert_operators(@square.appearance.stream,
|
451
|
+
[[:set_graphics_state_parameters, [:GS1]],
|
452
|
+
[:set_device_rgb_stroking_color, [1, 0, 0]],
|
453
|
+
[:set_device_rgb_non_stroking_color, [0, 1, 0]],
|
454
|
+
[:set_line_width, [10]],
|
455
|
+
[:set_line_dash_pattern, [[2, 1], 0]],
|
456
|
+
[:append_rectangle, [5, 5, 100, 50]],
|
457
|
+
[:fill_and_stroke_path_non_zero]])
|
458
|
+
end
|
459
|
+
|
460
|
+
it "doesn't draw anything if neither stroke nor fill color is set" do
|
461
|
+
@square.delete(:C)
|
462
|
+
@generator.create_appearance
|
463
|
+
assert_operators(@square.appearance.stream, [])
|
464
|
+
end
|
465
|
+
|
466
|
+
it "draws an ellipse" do
|
467
|
+
@square[:Subtype] = :Circle
|
468
|
+
@generator.create_appearance
|
469
|
+
assert_operators(@square.appearance.stream,
|
470
|
+
[[:set_line_width, [2]],
|
471
|
+
[:move_to, [101.0, 26.0]],
|
472
|
+
[:curve_to, [101.0, 34.920552, 91.45085, 43.190359, 76.0, 47.650635]],
|
473
|
+
[:curve_to, [60.54915, 52.110911, 41.45085, 52.110911, 26.0, 47.650635]],
|
474
|
+
[:curve_to, [10.54915, 43.190359, 1.0, 34.920552, 1.0, 26.0]],
|
475
|
+
[:curve_to, [1.0, 17.079448, 10.54915, 8.809641, 26.0, 4.349365]],
|
476
|
+
[:curve_to, [41.45085, -0.110911, 60.54915, -0.110911, 76.0, 4.349365]],
|
477
|
+
[:curve_to, [91.45085, 8.809641, 101.0, 17.079448, 101.0, 26.0]],
|
478
|
+
[:close_subpath],
|
479
|
+
[:stroke_path]])
|
480
|
+
end
|
481
|
+
end
|
398
482
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/annotations/border_effect'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::Annotations::BorderEffect do
|
8
|
+
class TestAnnot < HexaPDF::Type::Annotation
|
9
|
+
define_field :BE, type: :XXBorderEffect
|
10
|
+
include HexaPDF::Type::Annotations::BorderEffect
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
@doc = HexaPDF::Document.new
|
15
|
+
@annot = @doc.wrap({Type: :Annot}, type: TestAnnot)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "border_effect" do
|
19
|
+
it "returns :none if no border effect is set" do
|
20
|
+
assert_equal(:none, @annot.border_effect)
|
21
|
+
@annot[:BE] = {}
|
22
|
+
assert_equal(:none, @annot.border_effect)
|
23
|
+
@annot[:BE] = {S: :S}
|
24
|
+
assert_equal(:none, @annot.border_effect)
|
25
|
+
@annot[:BE] = {S: :K}
|
26
|
+
assert_equal(:none, @annot.border_effect)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns cloud(y|ier|iest) if /S is /C and depending on /I" do
|
30
|
+
@annot[:BE] = {S: :C}
|
31
|
+
assert_equal(:cloudy, @annot.border_effect)
|
32
|
+
@annot[:BE][:I] = 0
|
33
|
+
assert_equal(:cloudy, @annot.border_effect)
|
34
|
+
@annot[:BE][:I] = 1
|
35
|
+
assert_equal(:cloudier, @annot.border_effect)
|
36
|
+
@annot[:BE][:I] = 2
|
37
|
+
assert_equal(:cloudiest, @annot.border_effect)
|
38
|
+
@annot[:BE][:I] = 3
|
39
|
+
assert_equal(:cloudy, @annot.border_effect)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets the /BE entry appropriately" do
|
43
|
+
@annot.border_effect(:none)
|
44
|
+
refute(@annot.key?(:BE))
|
45
|
+
@annot.border_effect(nil)
|
46
|
+
refute(@annot.key?(:BE))
|
47
|
+
@annot.border_effect(:cloudy)
|
48
|
+
assert_equal({S: :C, I: 0}, @annot[:BE])
|
49
|
+
@annot.border_effect(:cloudier)
|
50
|
+
assert_equal({S: :C, I: 1}, @annot[:BE])
|
51
|
+
@annot.border_effect(:cloudiest)
|
52
|
+
assert_equal({S: :C, I: 2}, @annot[:BE])
|
53
|
+
end
|
54
|
+
|
55
|
+
it "raises an error if the given type is unknown" do
|
56
|
+
assert_raises(ArgumentError) { @annot.border_effect(:unknown) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/annotations/interior_color'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::Annotations::InteriorColor do
|
8
|
+
class TestAnnot < HexaPDF::Type::Annotation
|
9
|
+
define_field :IC, type: HexaPDF::PDFArray
|
10
|
+
include HexaPDF::Type::Annotations::InteriorColor
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
@doc = HexaPDF::Document.new
|
15
|
+
@annot = @doc.wrap({Type: :Annot}, type: TestAnnot)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "interior_color" do
|
19
|
+
it "returns the interior color" do
|
20
|
+
assert_nil(@annot.interior_color)
|
21
|
+
@annot[:IC] = []
|
22
|
+
assert_nil(@annot.interior_color)
|
23
|
+
@annot[:IC] = [0.5]
|
24
|
+
assert_equal(HexaPDF::Content::ColorSpace.device_color_from_specification(0.5),
|
25
|
+
@annot.interior_color)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets the interior color" do
|
29
|
+
@annot.interior_color(255)
|
30
|
+
assert_equal([1.0], @annot[:IC])
|
31
|
+
@annot.interior_color(255, 255, 0)
|
32
|
+
assert_equal([1.0, 1.0, 0], @annot[:IC])
|
33
|
+
@annot.interior_color(:transparent)
|
34
|
+
assert_equal([], @annot[:IC])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -51,26 +51,6 @@ describe HexaPDF::Type::Annotations::Line do
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
describe "interior_color" do
|
55
|
-
it "returns the interior color" do
|
56
|
-
assert_nil(@line.interior_color)
|
57
|
-
@line[:IC] = []
|
58
|
-
assert_nil(@line.interior_color)
|
59
|
-
@line[:IC] = [0.5]
|
60
|
-
assert_equal(HexaPDF::Content::ColorSpace.device_color_from_specification(0.5),
|
61
|
-
@line.interior_color)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "sets the interior color" do
|
65
|
-
@line.interior_color(255)
|
66
|
-
assert_equal([1.0], @line[:IC])
|
67
|
-
@line.interior_color(255, 255, 0)
|
68
|
-
assert_equal([1.0, 1.0, 0], @line[:IC])
|
69
|
-
@line.interior_color(:transparent)
|
70
|
-
assert_equal([], @line[:IC])
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
54
|
describe "leader_line_length" do
|
75
55
|
it "returns the leader line length" do
|
76
56
|
assert_equal(0, @line.leader_line_length)
|
@@ -98,6 +98,20 @@ describe HexaPDF::Type::Annotations::Widget do
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
+
it "uses the correct default style" do
|
102
|
+
@widget.form_field.initialize_as_check_box
|
103
|
+
@widget.marker_style(size: 10)
|
104
|
+
assert_equal('4', @widget[:MK][:CA])
|
105
|
+
|
106
|
+
@widget.form_field.initialize_as_radio_button
|
107
|
+
@widget.marker_style(size: 10)
|
108
|
+
assert_equal('l', @widget[:MK][:CA])
|
109
|
+
|
110
|
+
@widget.form_field.initialize_as_push_button
|
111
|
+
@widget.marker_style(size: 10)
|
112
|
+
assert_equal('', @widget[:MK][:CA])
|
113
|
+
end
|
114
|
+
|
101
115
|
it "fails if an invalid argument is provided" do
|
102
116
|
assert_raises(ArgumentError) { @widget.marker_style(style: 5) }
|
103
117
|
end
|
@@ -144,5 +158,26 @@ describe HexaPDF::Type::Annotations::Widget do
|
|
144
158
|
assert_equal([1, 0.2, 1, 1], @widget.marker_style.color.components)
|
145
159
|
end
|
146
160
|
end
|
161
|
+
|
162
|
+
describe "font_name" do
|
163
|
+
it "returns the font_name" do
|
164
|
+
@widget.form_field[:DA] = "/F1 15 Tf"
|
165
|
+
assert_equal(:F1, @widget.marker_style.font_name)
|
166
|
+
@widget[:DA] = "/F2 10 Tf"
|
167
|
+
assert_equal(:F2, @widget.marker_style.font_name)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "returns nil if none is set" do
|
171
|
+
assert_nil(@widget.marker_style.font_name)
|
172
|
+
@widget.form_field[:DA] = "0.0 g"
|
173
|
+
assert_nil(@widget.marker_style.font_name)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "sets the given font_name" do
|
177
|
+
@widget.form_field.initialize_as_push_button
|
178
|
+
@widget.marker_style(font_name: 'Helvetica', size: 10)
|
179
|
+
assert_equal('/F1 10 Tf 0.0 g', @widget[:DA])
|
180
|
+
end
|
181
|
+
end
|
147
182
|
end
|
148
183
|
end
|
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.3.0
|
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-04-23 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: cmdparse
|
@@ -509,10 +509,15 @@ files:
|
|
509
509
|
- lib/hexapdf/type/annotation.rb
|
510
510
|
- lib/hexapdf/type/annotations.rb
|
511
511
|
- lib/hexapdf/type/annotations/appearance_generator.rb
|
512
|
+
- lib/hexapdf/type/annotations/border_effect.rb
|
512
513
|
- lib/hexapdf/type/annotations/border_styling.rb
|
514
|
+
- lib/hexapdf/type/annotations/circle.rb
|
515
|
+
- lib/hexapdf/type/annotations/interior_color.rb
|
513
516
|
- lib/hexapdf/type/annotations/line.rb
|
514
517
|
- lib/hexapdf/type/annotations/link.rb
|
515
518
|
- lib/hexapdf/type/annotations/markup_annotation.rb
|
519
|
+
- lib/hexapdf/type/annotations/square.rb
|
520
|
+
- lib/hexapdf/type/annotations/square_circle.rb
|
516
521
|
- lib/hexapdf/type/annotations/text.rb
|
517
522
|
- lib/hexapdf/type/annotations/widget.rb
|
518
523
|
- lib/hexapdf/type/catalog.rb
|
@@ -791,7 +796,9 @@ files:
|
|
791
796
|
- test/hexapdf/type/actions/test_set_ocg_state.rb
|
792
797
|
- test/hexapdf/type/actions/test_uri.rb
|
793
798
|
- test/hexapdf/type/annotations/test_appearance_generator.rb
|
799
|
+
- test/hexapdf/type/annotations/test_border_effect.rb
|
794
800
|
- test/hexapdf/type/annotations/test_border_styling.rb
|
801
|
+
- test/hexapdf/type/annotations/test_interior_color.rb
|
795
802
|
- test/hexapdf/type/annotations/test_line.rb
|
796
803
|
- test/hexapdf/type/annotations/test_markup_annotation.rb
|
797
804
|
- test/hexapdf/type/annotations/test_text.rb
|