hexapdf 1.1.0 → 1.2.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 +41 -0
- data/lib/hexapdf/cli/command.rb +63 -63
- data/lib/hexapdf/cli/inspect.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +0 -1
- data/lib/hexapdf/cli/optimize.rb +5 -5
- data/lib/hexapdf/configuration.rb +21 -0
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +1 -1
- data/lib/hexapdf/document/annotations.rb +115 -0
- data/lib/hexapdf/document.rb +30 -7
- data/lib/hexapdf/font/true_type_wrapper.rb +1 -0
- data/lib/hexapdf/font/type1_wrapper.rb +1 -0
- data/lib/hexapdf/type/acro_form/java_script_actions.rb +9 -2
- data/lib/hexapdf/type/acro_form/text_field.rb +9 -2
- data/lib/hexapdf/type/annotation.rb +59 -1
- data/lib/hexapdf/type/annotations/appearance_generator.rb +273 -0
- data/lib/hexapdf/type/annotations/border_styling.rb +160 -0
- data/lib/hexapdf/type/annotations/line.rb +521 -0
- data/lib/hexapdf/type/annotations/widget.rb +2 -96
- data/lib/hexapdf/type/annotations.rb +3 -0
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +0 -1
- data/lib/hexapdf/xref_section.rb +7 -4
- data/test/hexapdf/content/test_graphics_state.rb +2 -3
- data/test/hexapdf/content/test_operator.rb +4 -5
- data/test/hexapdf/digital_signature/test_cms_handler.rb +7 -8
- data/test/hexapdf/digital_signature/test_handler.rb +2 -3
- data/test/hexapdf/digital_signature/test_pkcs1_handler.rb +1 -2
- data/test/hexapdf/document/test_annotations.rb +33 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +7 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +7 -0
- data/test/hexapdf/task/test_optimize.rb +1 -1
- data/test/hexapdf/test_document.rb +11 -3
- data/test/hexapdf/test_stream.rb +1 -2
- data/test/hexapdf/test_xref_section.rb +1 -1
- data/test/hexapdf/type/acro_form/test_java_script_actions.rb +21 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +7 -1
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +398 -0
- data/test/hexapdf/type/annotations/test_border_styling.rb +114 -0
- data/test/hexapdf/type/annotations/test_line.rb +189 -0
- data/test/hexapdf/type/annotations/test_widget.rb +0 -81
- data/test/hexapdf/type/test_annotation.rb +55 -0
- data/test/hexapdf/type/test_form.rb +6 -0
- metadata +10 -2
@@ -0,0 +1,398 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
|
6
|
+
describe HexaPDF::Type::Annotations::AppearanceGenerator do
|
7
|
+
before do
|
8
|
+
@doc = HexaPDF::Document.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "create" do
|
12
|
+
it "fails for unsupported annotation types" do
|
13
|
+
annot = @doc.add({Type: :Annot, Subtype: :Unknown})
|
14
|
+
error = assert_raises(HexaPDF::Error) do
|
15
|
+
HexaPDF::Type::Annotations::AppearanceGenerator.new(annot).create_appearance
|
16
|
+
end
|
17
|
+
assert_match(/Unknown.*not yet supported/, error.message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "line" do
|
22
|
+
before do
|
23
|
+
@line = @doc.add({Type: :Annot, Subtype: :Line, L: [100, 100, 200, 100], C: [0]})
|
24
|
+
@generator = HexaPDF::Type::Annotations::AppearanceGenerator.new(@line)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "sets the print flag and unsets the hidden flag" do
|
28
|
+
@line.flag(:hidden)
|
29
|
+
@generator.create_appearance
|
30
|
+
assert(@line.flagged?(:print))
|
31
|
+
refute(@line.flagged?(:hidden))
|
32
|
+
end
|
33
|
+
|
34
|
+
it "creates a simple line" do
|
35
|
+
@generator.create_appearance
|
36
|
+
assert_equal([96, 96, 204, 104], @line[:Rect])
|
37
|
+
assert_equal([96, 96, 204, 104], @line.appearance[:BBox])
|
38
|
+
assert_operators(@line.appearance.stream,
|
39
|
+
[[:concatenate_matrix, [1.0, 0.0, -0.0, 1.0, 100, 100]],
|
40
|
+
[:move_to, [0, 0]],
|
41
|
+
[:line_to, [100.0, 0]],
|
42
|
+
[:stroke_path]])
|
43
|
+
end
|
44
|
+
|
45
|
+
it "creates a rotated line" do
|
46
|
+
@line.line(100, 100, 50, 150)
|
47
|
+
@generator.create_appearance
|
48
|
+
assert_equal([46, 96, 104, 154], @line[:Rect])
|
49
|
+
assert_operators(@line.appearance.stream,
|
50
|
+
[[:concatenate_matrix, [-0.707107, 0.707107, -0.707107, -0.707107, 100, 100]],
|
51
|
+
[:move_to, [0, 0]],
|
52
|
+
[:line_to, [70.710678, 0]],
|
53
|
+
[:stroke_path]])
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "stroke color" do
|
57
|
+
it "uses the specified border color for stroking operations" do
|
58
|
+
@line.border_style(color: "red")
|
59
|
+
@generator.create_appearance
|
60
|
+
assert_operators(@line.appearance.stream,
|
61
|
+
[:set_device_rgb_stroking_color, [1, 0, 0]], range: 0)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "works with a transparent border" do
|
65
|
+
@line.border_style(color: :transparent, width: 1)
|
66
|
+
@generator.create_appearance
|
67
|
+
assert_operators(@line.appearance.stream, [:end_path], range: 3)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "uses the specified interior color for non-stroking operations" do
|
72
|
+
@line.interior_color("red")
|
73
|
+
@generator.create_appearance
|
74
|
+
assert_operators(@line.appearance.stream,
|
75
|
+
[:set_device_rgb_non_stroking_color, [1, 0, 0]], range: 0)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "sets the specified border line width" do
|
79
|
+
@line.border_style(width: 2)
|
80
|
+
@generator.create_appearance
|
81
|
+
assert_operators(@line.appearance.stream,
|
82
|
+
[:set_line_width, [2]], range: 0)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "sets the specified line dash pattern if it is an array" do
|
86
|
+
@line.border_style(style: [5, 2])
|
87
|
+
@generator.create_appearance
|
88
|
+
assert_operators(@line.appearance.stream,
|
89
|
+
[:set_line_dash_pattern, [[5, 2], 0]], range: 0)
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "leader lines" do
|
93
|
+
it "works for positive leader line length values" do
|
94
|
+
@line.leader_line_length(10)
|
95
|
+
@generator.create_appearance
|
96
|
+
assert_operators(@line.appearance.stream,
|
97
|
+
[[:concatenate_matrix, [1.0, 0.0, -0.0, 1.0, 100, 100]],
|
98
|
+
[:move_to, [0, 0]],
|
99
|
+
[:line_to, [0, 10]],
|
100
|
+
[:move_to, [100, 0]],
|
101
|
+
[:line_to, [100, 10]],
|
102
|
+
[:move_to, [0, 10]],
|
103
|
+
[:line_to, [100.0, 10]],
|
104
|
+
[:stroke_path]])
|
105
|
+
end
|
106
|
+
|
107
|
+
it "works for negative leader line length values" do
|
108
|
+
@line.leader_line_length(-10)
|
109
|
+
@generator.create_appearance
|
110
|
+
assert_operators(@line.appearance.stream,
|
111
|
+
[[:concatenate_matrix, [1.0, 0.0, -0.0, 1.0, 100, 100]],
|
112
|
+
[:move_to, [0, 0]],
|
113
|
+
[:line_to, [0, -10]],
|
114
|
+
[:move_to, [100, 0]],
|
115
|
+
[:line_to, [100, -10]],
|
116
|
+
[:move_to, [0, -10]],
|
117
|
+
[:line_to, [100.0, -10]],
|
118
|
+
[:stroke_path]])
|
119
|
+
end
|
120
|
+
|
121
|
+
it "works when using an offset and a positive leader line length" do
|
122
|
+
@line.leader_line_length(10)
|
123
|
+
@line.leader_line_offset(5)
|
124
|
+
@generator.create_appearance
|
125
|
+
assert_operators(@line.appearance.stream,
|
126
|
+
[[:concatenate_matrix, [1.0, 0.0, -0.0, 1.0, 100, 100]],
|
127
|
+
[:move_to, [0, 5]],
|
128
|
+
[:line_to, [0, 15]],
|
129
|
+
[:move_to, [100, 5]],
|
130
|
+
[:line_to, [100, 15]],
|
131
|
+
[:move_to, [0, 15]],
|
132
|
+
[:line_to, [100.0, 15]],
|
133
|
+
[:stroke_path]])
|
134
|
+
end
|
135
|
+
|
136
|
+
it "works when using an offset and a negative leader line length" do
|
137
|
+
@line.leader_line_length(-10)
|
138
|
+
@line.leader_line_offset(5)
|
139
|
+
@generator.create_appearance
|
140
|
+
assert_operators(@line.appearance.stream,
|
141
|
+
[[:concatenate_matrix, [1.0, 0.0, -0.0, 1.0, 100, 100]],
|
142
|
+
[:move_to, [0, -5]],
|
143
|
+
[:line_to, [0, -15]],
|
144
|
+
[:move_to, [100, -5]],
|
145
|
+
[:line_to, [100, -15]],
|
146
|
+
[:move_to, [0, -15]],
|
147
|
+
[:line_to, [100.0, -15]],
|
148
|
+
[:stroke_path]])
|
149
|
+
end
|
150
|
+
|
151
|
+
it "works when using leader line extensions" do
|
152
|
+
@line.leader_line_length(10)
|
153
|
+
@line.leader_line_extension_length(5)
|
154
|
+
@generator.create_appearance
|
155
|
+
assert_operators(@line.appearance.stream,
|
156
|
+
[[:concatenate_matrix, [1.0, 0.0, -0.0, 1.0, 100, 100]],
|
157
|
+
[:move_to, [0, 0]],
|
158
|
+
[:line_to, [0, 15]],
|
159
|
+
[:move_to, [100, 0]],
|
160
|
+
[:line_to, [100, 15]],
|
161
|
+
[:move_to, [0, 10]],
|
162
|
+
[:line_to, [100.0, 10]],
|
163
|
+
[:stroke_path]])
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "line ending styles" do
|
168
|
+
before do
|
169
|
+
@line.border_style(width: 2)
|
170
|
+
@line.interior_color("red")
|
171
|
+
end
|
172
|
+
|
173
|
+
it "works correctly for a transparent border" do
|
174
|
+
@line.line_ending_style(start_style: :square, end_style: :square)
|
175
|
+
@line.border_style(color: :transparent)
|
176
|
+
@generator.create_appearance
|
177
|
+
assert_operators(@line.appearance.stream,
|
178
|
+
[[:append_rectangle, [-3, -3, 6, 6]],
|
179
|
+
[:fill_path_non_zero],
|
180
|
+
[:append_rectangle, [97, -3, 6, 6]],
|
181
|
+
[:fill_path_non_zero]], range: 5..-1)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "works for a square" do
|
185
|
+
@line.line_ending_style(start_style: :square, end_style: :square)
|
186
|
+
@generator.create_appearance
|
187
|
+
assert_operators(@line.appearance.stream,
|
188
|
+
[[:append_rectangle, [-6, -6, 12, 12]],
|
189
|
+
[:fill_and_stroke_path_non_zero],
|
190
|
+
[:append_rectangle, [94, -6, 12, 12]],
|
191
|
+
[:fill_and_stroke_path_non_zero]], range: 6..-1)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "works for a circle" do
|
195
|
+
@line.line_ending_style(start_style: :circle, end_style: :circle)
|
196
|
+
@generator.create_appearance
|
197
|
+
assert_operators(@line.appearance.stream,
|
198
|
+
[[:move_to, [6.0, 0.0]],
|
199
|
+
[:curve_to, [6.0, 2.140933, 4.854102, 4.125686, 3.0, 5.196152]],
|
200
|
+
[:curve_to, [1.145898, 6.266619, -1.145898, 6.266619, -3.0, 5.196152]],
|
201
|
+
[:curve_to, [-4.854102, 4.125686, -6.0, 2.140933, -6.0, 0.0]],
|
202
|
+
[:curve_to, [-6.0, -2.140933, -4.854102, -4.125686, -3.0, -5.196152]],
|
203
|
+
[:curve_to, [-1.145898, -6.266619, 1.145898, -6.266619, 3.0, -5.196152]],
|
204
|
+
[:curve_to, [4.854102, -4.125686, 6.0, -2.140933, 6.0, -0.0]],
|
205
|
+
[:close_subpath],
|
206
|
+
[:fill_and_stroke_path_non_zero],
|
207
|
+
[:move_to, [106.0, 0.0]],
|
208
|
+
[:curve_to, [106.0, 2.140933, 104.854102, 4.125686, 103.0, 5.196152]],
|
209
|
+
[:curve_to, [101.145898, 6.266619, 98.854102, 6.266619, 97.0, 5.196152]],
|
210
|
+
[:curve_to, [95.145898, 4.125686, 94.0, 2.140933, 94.0, 0.0]],
|
211
|
+
[:curve_to, [94.0, -2.140933, 95.145898, -4.125686, 97.0, -5.196152]],
|
212
|
+
[:curve_to, [98.854102, -6.266619, 101.145898, -6.266619, 103.0, -5.196152]],
|
213
|
+
[:curve_to, [104.854102, -4.125686, 106.0, -2.140933, 106.0, -0.0]],
|
214
|
+
[:close_subpath],
|
215
|
+
[:fill_and_stroke_path_non_zero]], range: 6..-1)
|
216
|
+
end
|
217
|
+
|
218
|
+
it "works for a diamond" do
|
219
|
+
@line.line_ending_style(start_style: :diamond, end_style: :diamond)
|
220
|
+
@generator.create_appearance
|
221
|
+
assert_operators(@line.appearance.stream,
|
222
|
+
[[:move_to, [6, 0]],
|
223
|
+
[:line_to, [0, 6]],
|
224
|
+
[:line_to, [-6, 0]],
|
225
|
+
[:line_to, [0, -6]],
|
226
|
+
[:close_subpath],
|
227
|
+
[:fill_and_stroke_path_non_zero],
|
228
|
+
[:move_to, [106.0, 0]],
|
229
|
+
[:line_to, [100.0, 6]],
|
230
|
+
[:line_to, [94.0, 0]],
|
231
|
+
[:line_to, [100.0, -6]],
|
232
|
+
[:close_subpath],
|
233
|
+
[:fill_and_stroke_path_non_zero]], range: 6..-1)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "works for open and closed as well as reversed open and closed arrows" do
|
237
|
+
dx = 15.588457
|
238
|
+
[:open_arrow, :closed_arrow, :ropen_arrow, :rclosed_arrow].each do |style|
|
239
|
+
@line.line_ending_style(start_style: style, end_style: style)
|
240
|
+
@generator.create_appearance
|
241
|
+
used_dx = (style == :ropen_arrow || style == :rclosed_arrow ? -dx : dx)
|
242
|
+
ops = [[:move_to, [used_dx, 9.0]],
|
243
|
+
[:line_to, [0, 0]],
|
244
|
+
[:line_to, [used_dx, -9.0]],
|
245
|
+
[:move_to, [100 - used_dx, -9.0]],
|
246
|
+
[:line_to, [100.0, 0]],
|
247
|
+
[:line_to, [100 - used_dx, 9.0]]]
|
248
|
+
if style == :closed_arrow || style == :rclosed_arrow
|
249
|
+
ops.insert(3, [:close_subpath], [:fill_and_stroke_path_non_zero])
|
250
|
+
ops.insert(-1, [:close_subpath], [:fill_and_stroke_path_non_zero])
|
251
|
+
else
|
252
|
+
ops.insert(3, [:stroke_path])
|
253
|
+
ops.insert(-1, [:stroke_path])
|
254
|
+
end
|
255
|
+
assert_operators(@line.appearance.stream, ops, range: 6..-1)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
it "works for butt" do
|
260
|
+
@line.line_ending_style(start_style: :butt, end_style: :butt)
|
261
|
+
@generator.create_appearance
|
262
|
+
assert_operators(@line.appearance.stream,
|
263
|
+
[[:move_to, [0, 6]],
|
264
|
+
[:line_to, [0, -6]],
|
265
|
+
[:stroke_path],
|
266
|
+
[:move_to, [100.0, 6]],
|
267
|
+
[:line_to, [100.0, -6]],
|
268
|
+
[:stroke_path]], range: 6..-1)
|
269
|
+
end
|
270
|
+
|
271
|
+
it "works for slash" do
|
272
|
+
@line.line_ending_style(start_style: :slash, end_style: :slash)
|
273
|
+
@generator.create_appearance
|
274
|
+
assert_operators(@line.appearance.stream,
|
275
|
+
[[:move_to, [3, 5.196152]],
|
276
|
+
[:line_to, [-3, -5.196152]],
|
277
|
+
[:stroke_path],
|
278
|
+
[:move_to, [103.0, 5.196152]],
|
279
|
+
[:line_to, [97.0, -5.196152]],
|
280
|
+
[:stroke_path]], range: 6..-1)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe "caption" do
|
285
|
+
before do
|
286
|
+
@line.captioned(true)
|
287
|
+
@line.contents("Test")
|
288
|
+
end
|
289
|
+
|
290
|
+
it "adjusts the annotation's /Rect entry" do
|
291
|
+
@line.contents("This is some eeeeextra long text")
|
292
|
+
@generator.create_appearance
|
293
|
+
assert_equal([80.2225, 91.83749999999999, 219.7775, 108.1625], @line[:Rect])
|
294
|
+
end
|
295
|
+
|
296
|
+
it "puts the caption inline" do
|
297
|
+
@generator.create_appearance
|
298
|
+
assert_operators(@line.appearance.stream,
|
299
|
+
[[:move_to, [0, 0]],
|
300
|
+
[:line_to, [40.2475, 0]],
|
301
|
+
[:move_to, [59.7525, 0]],
|
302
|
+
[:line_to, [100, 0]],
|
303
|
+
[:stroke_path],
|
304
|
+
[:save_graphics_state],
|
305
|
+
[:set_font_and_size, [:F1, 9]],
|
306
|
+
[:begin_text],
|
307
|
+
[:move_text, [41.2475, -2.2995]],
|
308
|
+
[:show_text, ["Test"]],
|
309
|
+
[:end_text]], range: 1..-2)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "puts the caption inline with an offset" do
|
313
|
+
@line.caption_offset(20, 5)
|
314
|
+
@generator.create_appearance
|
315
|
+
assert_operators(@line.appearance.stream,
|
316
|
+
[[:move_to, [0, 0]],
|
317
|
+
[:line_to, [60.2475, 0]],
|
318
|
+
[:move_to, [79.7525, 0]],
|
319
|
+
[:line_to, [100, 0]],
|
320
|
+
[:stroke_path],
|
321
|
+
[:save_graphics_state],
|
322
|
+
[:set_font_and_size, [:F1, 9]],
|
323
|
+
[:begin_text],
|
324
|
+
[:move_text, [61.2475, 2.7005]],
|
325
|
+
[:show_text, ["Test"]],
|
326
|
+
[:end_text]], range: 1..-2)
|
327
|
+
end
|
328
|
+
|
329
|
+
it "handles too long inline captions" do
|
330
|
+
@line.contents('This inline text is so long that no line is shown')
|
331
|
+
@generator.create_appearance
|
332
|
+
assert_operators(@line.appearance.stream,
|
333
|
+
[[:move_to, [0, 0]],
|
334
|
+
[:line_to, [0, 0]],
|
335
|
+
[:move_to, [100, 0]],
|
336
|
+
[:line_to, [100, 0]],
|
337
|
+
[:stroke_path],
|
338
|
+
[:save_graphics_state],
|
339
|
+
[:set_font_and_size, [:F1, 9]],
|
340
|
+
[:begin_text],
|
341
|
+
[:move_text, [-41.0395, -2.2995]],
|
342
|
+
[:show_text, ["This inline text is so long that no line is shown"]],
|
343
|
+
[:end_text]], range: 1..-2)
|
344
|
+
end
|
345
|
+
|
346
|
+
it "puts the caption on top of the line" do
|
347
|
+
@line.caption_position(:top)
|
348
|
+
@generator.create_appearance
|
349
|
+
assert_operators(@line.appearance.stream,
|
350
|
+
[[:move_to, [0, 0]],
|
351
|
+
[:line_to, [100, 0]],
|
352
|
+
[:stroke_path],
|
353
|
+
[:save_graphics_state],
|
354
|
+
[:set_font_and_size, [:F1, 9]],
|
355
|
+
[:begin_text],
|
356
|
+
[:move_text, [41.2475, 3.863]],
|
357
|
+
[:show_text, ["Test"]],
|
358
|
+
[:end_text]], range: 1..-2)
|
359
|
+
end
|
360
|
+
|
361
|
+
it "puts the caption on top of the line" do
|
362
|
+
@line.caption_position(:top)
|
363
|
+
@line.caption_offset(-20, -5)
|
364
|
+
@generator.create_appearance
|
365
|
+
assert_operators(@line.appearance.stream,
|
366
|
+
[[:move_to, [0, 0]],
|
367
|
+
[:line_to, [100, 0]],
|
368
|
+
[:stroke_path],
|
369
|
+
[:save_graphics_state],
|
370
|
+
[:set_font_and_size, [:F1, 9]],
|
371
|
+
[:begin_text],
|
372
|
+
[:move_text, [21.2475, -1.137]],
|
373
|
+
[:show_text, ["Test"]],
|
374
|
+
[:end_text]], range: 1..-2)
|
375
|
+
end
|
376
|
+
|
377
|
+
it "handles text with line breaks" do
|
378
|
+
@line.contents("This inline text\ris long")
|
379
|
+
@generator.create_appearance
|
380
|
+
assert_operators(@line.appearance.stream,
|
381
|
+
[[:move_to, [0, 0]],
|
382
|
+
[:line_to, [20.2405, 0]],
|
383
|
+
[:move_to, [79.7595, 0]],
|
384
|
+
[:line_to, [100, 0]],
|
385
|
+
[:stroke_path],
|
386
|
+
[:save_graphics_state],
|
387
|
+
[:set_leading, [10.40625]],
|
388
|
+
[:set_font_and_size, [:F1, 9]],
|
389
|
+
[:begin_text],
|
390
|
+
[:move_text, [21.2405, 2.903625]],
|
391
|
+
[:show_text, ["This inline text"]],
|
392
|
+
[:move_text_next_line],
|
393
|
+
[:show_text, ["is long"]],
|
394
|
+
[:end_text]], range: 1..-2)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/annotations/border_styling'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::Annotations::BorderStyling do
|
8
|
+
class TestAnnot < HexaPDF::Type::Annotation
|
9
|
+
define_field :BS, type: :Border
|
10
|
+
define_field :MK, type: :XXAppearanceCharacteristics
|
11
|
+
include HexaPDF::Type::Annotations::BorderStyling
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
@doc = HexaPDF::Document.new
|
16
|
+
@annot = @doc.wrap({Type: :Annot}, type: TestAnnot)
|
17
|
+
@color = HexaPDF::Content::ColorSpace.prenormalized_device_color([1, 0, 1])
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "border_style" do
|
21
|
+
describe "getter" do
|
22
|
+
it "no /Border, /BS or /C|/MK set" do
|
23
|
+
@annot.delete(:MK)
|
24
|
+
assert_equal([1, nil, :solid, 0, 0], @annot.border_style.to_a)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "no /Border, /BS but with /MK empty" do
|
28
|
+
@annot[:Subtype] = :Widget
|
29
|
+
@annot[:MK] = {}
|
30
|
+
assert_equal([1, nil, :solid, 0, 0], @annot.border_style.to_a)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "uses the color from /C" do
|
34
|
+
@annot[:C] = [1, 0, 1]
|
35
|
+
assert_equal([1, @color, :solid, 0, 0], @annot.border_style.to_a)
|
36
|
+
@annot[:C] = []
|
37
|
+
assert_equal([1, nil, :solid, 0, 0], @annot.border_style.to_a)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "uses the color from /MK" do
|
41
|
+
@annot[:Subtype] = :Widget
|
42
|
+
@annot[:MK] = {BC: [1, 0, 1]}
|
43
|
+
assert_equal([1, @color, :solid, 0, 0], @annot.border_style.to_a)
|
44
|
+
@annot[:MK][:BC] = []
|
45
|
+
assert_equal([1, nil, :solid, 0, 0], @annot.border_style.to_a)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "uses the data from /Border" do
|
49
|
+
@annot[:Border] = [1, 2, 3, [1, 2]]
|
50
|
+
assert_equal([3, nil, [1, 2], 1, 2], @annot.border_style.to_a)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "uses the data from /BS, overriding /Border values" do
|
54
|
+
@annot[:Border] = [1, 2, 3, [1, 2]]
|
55
|
+
@annot[:BS] = {W: 5, S: :D, D: [5, 6]}
|
56
|
+
assert_equal([5, nil, [5, 6], 0, 0], @annot.border_style.to_a)
|
57
|
+
|
58
|
+
[[:S, :solid], [:D, [5, 6]], [:B, :beveled], [:I, :inset],
|
59
|
+
[:U, :underlined], [:Unknown, :solid]].each do |val, result|
|
60
|
+
@annot[:BS] = {S: val, D: [5, 6]}
|
61
|
+
assert_equal([1, nil, result, 0, 0], @annot.border_style.to_a)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "setter" do
|
67
|
+
it "returns self" do
|
68
|
+
assert_equal(@annot, @annot.border_style(width: 1))
|
69
|
+
end
|
70
|
+
|
71
|
+
it "sets the color" do
|
72
|
+
@annot.border_style(color: [1.0, 51, 1.0])
|
73
|
+
assert_equal([1, 0.2, 1], @annot[:C])
|
74
|
+
|
75
|
+
@annot.border_style(color: :transparent)
|
76
|
+
assert_equal([], @annot[:C])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "sets the color on a widget using /MK" do
|
80
|
+
@annot[:Subtype] = :Widget
|
81
|
+
@annot.border_style(color: [1.0, 51, 1.0])
|
82
|
+
assert_equal([1, 0.2, 1], @annot[:MK][:BC])
|
83
|
+
|
84
|
+
@annot.border_style(color: :transparent)
|
85
|
+
assert_equal([], @annot[:MK][:BC])
|
86
|
+
end
|
87
|
+
|
88
|
+
it "sets the width" do
|
89
|
+
@annot.border_style(width: 2)
|
90
|
+
assert_equal(2, @annot[:BS][:W])
|
91
|
+
end
|
92
|
+
|
93
|
+
it "sets the style" do
|
94
|
+
[[:solid, :S], [[5, 6], :D], [:beveled, :B], [:inset, :I], [:underlined, :U]].each do |val, r|
|
95
|
+
@annot.border_style(style: val)
|
96
|
+
assert_equal(r, @annot[:BS][:S])
|
97
|
+
assert_equal(val, @annot[:BS][:D]) if r == :D
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "overrides all priorly set values" do
|
102
|
+
@annot.border_style(width: 3, style: :inset, color: [1])
|
103
|
+
@annot.border_style(width: 5)
|
104
|
+
border_style = @annot.border_style
|
105
|
+
assert_equal(:solid, border_style.style)
|
106
|
+
assert_equal([0], border_style.color.components)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "raises an error for an unknown style" do
|
110
|
+
assert_raises(ArgumentError) { @annot.border_style(style: :unknown) }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/annotations/line'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::Annotations::Line do
|
8
|
+
before do
|
9
|
+
@doc = HexaPDF::Document.new
|
10
|
+
@line = @doc.add({Type: :Annot, Subtype: :Line, Rect: [0, 0, 0, 0], L: [0, 0, 1, 1]})
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "line" do
|
14
|
+
it "returns the coordinates of the start and end points" do
|
15
|
+
@line[:L] = [10, 20, 30, 40]
|
16
|
+
assert_equal([10, 20, 30, 40], @line.line)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sets the line points" do
|
20
|
+
assert_equal(@line, @line.line(1, 2, 3, 4))
|
21
|
+
assert_equal([1, 2, 3, 4], @line[:L])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises an ArgumentError if not all arguments are provided" do
|
25
|
+
assert_raises(ArgumentError) { @line.line(1, 2, 3) }
|
26
|
+
end
|
27
|
+
end
|
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
|
+
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
|
+
describe "leader_line_length" do
|
75
|
+
it "returns the leader line length" do
|
76
|
+
assert_equal(0, @line.leader_line_length)
|
77
|
+
@line[:LL] = 10
|
78
|
+
assert_equal(10, @line.leader_line_length)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "sets the leader line length" do
|
82
|
+
assert_equal(@line, @line.leader_line_length(10))
|
83
|
+
assert_equal(10, @line[:LL])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "leader_line_extension_length" do
|
88
|
+
it "returns the leader line extension length" do
|
89
|
+
assert_equal(0, @line.leader_line_extension_length)
|
90
|
+
@line[:LLE] = 10
|
91
|
+
assert_equal(10, @line.leader_line_extension_length)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "sets the leader line extension length" do
|
95
|
+
assert_equal(@line, @line.leader_line_extension_length(10))
|
96
|
+
assert_equal(10, @line[:LLE])
|
97
|
+
end
|
98
|
+
|
99
|
+
it "raises an error for negative numbers" do
|
100
|
+
assert_raises(ArgumentError) { @line.leader_line_extension_length(-10) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "leader_line_offset" do
|
105
|
+
it "returns the leader line offset" do
|
106
|
+
assert_equal(0, @line.leader_line_offset)
|
107
|
+
@line[:LLO] = 10
|
108
|
+
assert_equal(10, @line.leader_line_offset)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "sets the leader line offset" do
|
112
|
+
assert_equal(@line, @line.leader_line_offset(10))
|
113
|
+
assert_equal(10, @line[:LLO])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "captioned" do
|
118
|
+
it "returns whether a caption is shown" do
|
119
|
+
refute(@line.captioned)
|
120
|
+
@line[:Cap] = true
|
121
|
+
assert(@line.captioned)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "sets whether a caption should be shown" do
|
125
|
+
assert_equal(@line, @line.captioned(true))
|
126
|
+
assert_equal(true, @line[:Cap])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "caption_position" do
|
131
|
+
it "returns the caption position" do
|
132
|
+
assert_equal(:inline, @line.caption_position)
|
133
|
+
@line[:CP] = :Top
|
134
|
+
assert_equal(:top, @line.caption_position)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "sets the caption position" do
|
138
|
+
assert_equal(@line, @line.caption_position(:top))
|
139
|
+
assert_equal(:Top, @line[:CP])
|
140
|
+
end
|
141
|
+
|
142
|
+
it "raises an error on invalid caption positions" do
|
143
|
+
assert_raises(ArgumentError) { @line.caption_position(:unknown) }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "caption_offset" do
|
148
|
+
it "returns the caption offset" do
|
149
|
+
assert_equal([0, 0], @line.caption_offset)
|
150
|
+
@line[:CO] = [10, 5]
|
151
|
+
assert_equal([10, 5], @line.caption_offset)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "sets the caption offset" do
|
155
|
+
assert_equal(@line, @line.caption_offset(5, 10))
|
156
|
+
assert_equal([5, 10], @line[:CO])
|
157
|
+
assert_equal(@line, @line.caption_offset(5))
|
158
|
+
assert_equal([5, 0], @line[:CO])
|
159
|
+
assert_equal(@line, @line.caption_offset(nil, 5))
|
160
|
+
assert_equal([0, 5], @line[:CO])
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "perform_validation" do
|
165
|
+
it "validates that leader line length is set if extension length is set" do
|
166
|
+
@line[:LLE] = 10
|
167
|
+
m = nil
|
168
|
+
refute(@line.validate {|msg| m = msg })
|
169
|
+
assert_match(/\/LL required to be non-zero/, m)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "ensures leader line extension length is non-negative" do
|
173
|
+
@line[:LL] = 10
|
174
|
+
@line[:LLE] = -10
|
175
|
+
m = nil
|
176
|
+
assert(@line.validate {|msg| m = msg })
|
177
|
+
assert_equal(10, @line[:LLE])
|
178
|
+
assert_match(/non-negative/, m)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "ensures leader line offset is non-negative" do
|
182
|
+
@line[:LLO] = -10
|
183
|
+
m = nil
|
184
|
+
assert(@line.validate {|msg| m = msg })
|
185
|
+
assert_equal(10, @line[:LLO])
|
186
|
+
assert_match(/\/LLO must be a non-negative/, m)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|