hexapdf 0.17.3 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/lib/hexapdf/content/color_space.rb +19 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +2 -2
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +4 -4
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +111 -10
- data/lib/hexapdf/dictionary.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +3 -1
- data/lib/hexapdf/object.rb +18 -0
- data/lib/hexapdf/serializer.rb +2 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +4 -4
- data/lib/hexapdf/type/acro_form/form.rb +39 -28
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +56 -18
- data/lib/hexapdf/type/annotations/widget.rb +3 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +16 -8
- data/test/hexapdf/content/test_color_space.rb +26 -0
- data/test/hexapdf/encryption/test_security_handler.rb +3 -1
- data/test/hexapdf/test_dictionary.rb +1 -0
- data/test/hexapdf/test_object.rb +28 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +8 -1
- data/test/hexapdf/type/acro_form/test_form.rb +15 -8
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +18 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d95ce1575c017f44b2c0f96e7e5a927b8c4f8c3adf6aa0f3a7dc983c5dfa77a8
|
4
|
+
data.tar.gz: e49a23655e5ce4f4ded50c5ac0c90d7892c41bd526dbdfe72ad85cca4891098b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37e3b09a059bb7875c797f50f60642b080f20a081fa2540f74837ac51a8cb9c1aa5b93e6502dd9f242d178f68ea654c0619b2e55d4cb934e36badca4a5057d1c
|
7
|
+
data.tar.gz: cd9fc830b8b4f5387478d7e6c32260672a9332bb2faf3aa7afcb4d71bf7c39481616f7f33d731229a6099e44dac84bbecd651c3618593f6c7ba872b9e958a3cb
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
## 0.18.0 - 2021-11-04
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* [HexaPDF::Content::ColorSpace::serialize_device_color] for serialization of
|
6
|
+
device colors in parts other than the canvas
|
7
|
+
* [HexaPDF::Type::AcroForm::VariableTextField::create_appearance_string] for
|
8
|
+
centralized creation of appearance strings
|
9
|
+
* [HexaPDF::Object.make_direct] for making objects and all parts of them direct
|
10
|
+
instead of indirect
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
* [HexaPDF::Type::AcroForm::VariableTextField::parse_appearance_string] to also
|
15
|
+
return the font color
|
16
|
+
* [HexaPDF::Type::AcroForm::VariableTextField#set_default_appearance_string] to
|
17
|
+
allow specifying the font color
|
18
|
+
* [HexaPDF::Type::AcroForm::Form] methods to support new variable text field
|
19
|
+
methods
|
20
|
+
* [HexaPDF::Type::AcroForm::AppearanceGenerator] to support the set font color
|
21
|
+
when creating text field appearances
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
|
25
|
+
* Writing of existing, encrypted PDF files where parts of the encryption
|
26
|
+
dictionary are indirect objects
|
27
|
+
* [HexaPDF::Content::GraphicObject::EndpointArc] to correctly determine the
|
28
|
+
start and end points
|
29
|
+
* [HexaPDF::Dictionary#perform_validation] to correctly handle objects that
|
30
|
+
should not be indirect objects
|
31
|
+
|
32
|
+
|
1
33
|
## 0.17.3 - 2021-10-31
|
2
34
|
|
3
35
|
### Fixed
|
@@ -35,7 +35,9 @@
|
|
35
35
|
#++
|
36
36
|
|
37
37
|
require 'hexapdf/error'
|
38
|
+
require 'hexapdf/content'
|
38
39
|
require 'hexapdf/configuration'
|
40
|
+
require 'hexapdf/serializer'
|
39
41
|
|
40
42
|
module HexaPDF
|
41
43
|
module Content
|
@@ -304,6 +306,23 @@ module HexaPDF
|
|
304
306
|
GlobalConfiguration.constantize('color_space.map', for_components(spec)).new.color(*spec)
|
305
307
|
end
|
306
308
|
|
309
|
+
# Serializes the given device color into the form expected by PDF content streams.
|
310
|
+
#
|
311
|
+
# The +type+ argument can either be :stroke to serialize as stroke color operator or :fill as
|
312
|
+
# fill color operator.
|
313
|
+
def self.serialize_device_color(color, type: :fill)
|
314
|
+
operator = case color.color_space.family
|
315
|
+
when :DeviceRGB then :rg
|
316
|
+
when :DeviceGray then :g
|
317
|
+
when :DeviceCMYK then :k
|
318
|
+
else
|
319
|
+
raise ArgumentError, "Device color object expected, got #{color.class}"
|
320
|
+
end
|
321
|
+
operator = operator.upcase if type == :stroke
|
322
|
+
Content::Operator::DEFAULT_OPERATORS[operator].
|
323
|
+
serialize(HexaPDF::Serializer.new, *color.components)
|
324
|
+
end
|
325
|
+
|
307
326
|
# Returns a device color object for the given components array without applying value
|
308
327
|
# normalization.
|
309
328
|
def self.prenormalized_device_color(components)
|
@@ -51,8 +51,8 @@ module HexaPDF
|
|
51
51
|
# Examples:
|
52
52
|
#
|
53
53
|
# #>pdf-center
|
54
|
-
# arc = canvas.graphic_object(:arc, a: 100, b: 50)
|
55
|
-
#
|
54
|
+
# arc = canvas.graphic_object(:arc, a: 100, b: 50, end_angle: 150)
|
55
|
+
# canvas.draw(arc).stroke
|
56
56
|
#
|
57
57
|
# See: ELL - https://spaceroots.org/documents/ellipse/elliptical-arc.pdf
|
58
58
|
class Arc
|
@@ -49,8 +49,8 @@ module HexaPDF
|
|
49
49
|
# Examples:
|
50
50
|
#
|
51
51
|
# #>pdf-center
|
52
|
-
# arc = canvas.graphic_object(:endpoint_arc, x: 50, y: 20, a: 30, b: 10)
|
53
|
-
#
|
52
|
+
# arc = canvas.graphic_object(:endpoint_arc, x: 50, y: 20, a: 30, b: 10)
|
53
|
+
# canvas.move_to(0, 0).draw(arc).stroke
|
54
54
|
#
|
55
55
|
# See: GraphicObject::Arc, ARC - https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
56
56
|
class EndpointArc
|
@@ -251,10 +251,10 @@ module HexaPDF
|
|
251
251
|
cy = sin_theta * cxp + cos_theta * cyp + (y1 + y2) / 2.0
|
252
252
|
|
253
253
|
# F.6.5.5
|
254
|
-
start_angle = compute_angle_to_x_axis((x1p - cxp)
|
254
|
+
start_angle = compute_angle_to_x_axis((x1p - cxp), (y1p - cyp)) % 360
|
255
255
|
|
256
256
|
# F.6.5.6 (modified bc we just need the end angle)
|
257
|
-
end_angle = compute_angle_to_x_axis((-x1p - cxp)
|
257
|
+
end_angle = compute_angle_to_x_axis((-x1p - cxp), (-y1p - cyp)) % 360
|
258
258
|
|
259
259
|
{cx: cx, cy: cy, a: rx, b: ry, start_angle: start_angle, end_angle: end_angle,
|
260
260
|
inclination: @inclination, clockwise: @clockwise}
|
@@ -41,20 +41,45 @@ module HexaPDF
|
|
41
41
|
# This graphic object represents a solid elliptical arc, i.e. an arc that has an inner and
|
42
42
|
# an outer set of a/b values.
|
43
43
|
#
|
44
|
+
# This graphic object is registered under the :solid_arc key for use with the
|
45
|
+
# HexaPDF::Content::Canvas class.
|
46
|
+
#
|
44
47
|
# Thus it can be used to create
|
45
48
|
#
|
46
|
-
# * an (elliptical) disk (when the inner a/b are zero and the difference between start and
|
49
|
+
# * an (*elliptical*) *disk* (when the inner a/b are zero and the difference between start and
|
47
50
|
# end angles is greater than or equal to 360),
|
48
51
|
#
|
49
|
-
#
|
52
|
+
# #>pdf-center
|
53
|
+
# canvas.fill_color("red").
|
54
|
+
# draw(:solid_arc, outer_a: 80, outer_b: 50, end_angle: 360).
|
55
|
+
# fill_stroke
|
56
|
+
#
|
57
|
+
# * an (*elliptical*) *sector* (when the inner a/b are zero and the difference between start
|
50
58
|
# and end angles is less than 360),
|
51
59
|
#
|
52
|
-
#
|
60
|
+
# #>pdf-center
|
61
|
+
# canvas.fill_color("red").
|
62
|
+
# draw(:solid_arc, outer_a: 80, outer_b: 50, start_angle: 20, end_angle: 230).
|
63
|
+
# fill_stroke
|
64
|
+
#
|
65
|
+
# * an (*elliptical*) *annulus* (when the inner a/b are nonzero and the difference between
|
53
66
|
# start and end angles is greater than or equal to 360), and
|
54
67
|
#
|
55
|
-
#
|
68
|
+
# #>pdf-center
|
69
|
+
# canvas.fill_color("red").
|
70
|
+
# draw(:solid_arc, outer_a: 80, outer_b: 50, inner_a: 70, inner_b: 30,
|
71
|
+
# end_angle: 360).
|
72
|
+
# fill_stroke
|
73
|
+
#
|
74
|
+
# * an (*elliptical*) *annular sector* (when the inner a/b are nonzero and the difference
|
56
75
|
# between start and end angles is less than 360)
|
57
76
|
#
|
77
|
+
# #>pdf-center
|
78
|
+
# canvas.fill_color("red").
|
79
|
+
# draw(:solid_arc, outer_a: 80, outer_b: 50, inner_a: 70, inner_b: 30,
|
80
|
+
# start_angle: 20, end_angle: 230).
|
81
|
+
# fill_stroke
|
82
|
+
#
|
58
83
|
# See: Arc
|
59
84
|
class SolidArc
|
60
85
|
|
@@ -66,30 +91,106 @@ module HexaPDF
|
|
66
91
|
end
|
67
92
|
|
68
93
|
# x-coordinate of center point
|
94
|
+
#
|
95
|
+
# Examples:
|
96
|
+
#
|
97
|
+
# #>pdf-center
|
98
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
99
|
+
# inner_a: 20, inner_b: 10)
|
100
|
+
# canvas.draw(solid_arc).stroke
|
101
|
+
# canvas.stroke_color("red").draw(solid_arc, cx: 50).stroke
|
69
102
|
attr_reader :cx
|
70
103
|
|
71
104
|
# y-coordinate of center point
|
105
|
+
#
|
106
|
+
# Examples:
|
107
|
+
#
|
108
|
+
# #>pdf-center
|
109
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
110
|
+
# inner_a: 20, inner_b: 10)
|
111
|
+
# canvas.draw(solid_arc).stroke
|
112
|
+
# canvas.stroke_color("red").draw(solid_arc, cy: 50).stroke
|
72
113
|
attr_reader :cy
|
73
114
|
|
74
|
-
# Length of inner semi-major axis
|
115
|
+
# Length of inner semi-major axis which (without altering the #inclination) is parallel to
|
116
|
+
# the x-axis
|
117
|
+
#
|
118
|
+
# Examples:
|
119
|
+
#
|
120
|
+
# #>pdf-center
|
121
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
122
|
+
# inner_a: 20, inner_b: 10)
|
123
|
+
# canvas.draw(solid_arc).stroke
|
124
|
+
# canvas.stroke_color("red").draw(solid_arc, inner_a: 5).stroke
|
75
125
|
attr_reader :inner_a
|
76
126
|
|
77
|
-
# Length of inner semi-minor axis
|
127
|
+
# Length of inner semi-minor axis which (without altering the #inclination) is parallel to the
|
128
|
+
# y-axis
|
129
|
+
#
|
130
|
+
# Examples:
|
131
|
+
#
|
132
|
+
# #>pdf-center
|
133
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
134
|
+
# inner_a: 20, inner_b: 10)
|
135
|
+
# canvas.draw(solid_arc).stroke
|
136
|
+
# canvas.stroke_color("red").draw(solid_arc, inner_b: 20).stroke
|
78
137
|
attr_reader :inner_b
|
79
138
|
|
80
|
-
# Length of outer semi-major axis
|
139
|
+
# Length of outer semi-major axis which (without altering the #inclination) is parallel to
|
140
|
+
# the x-axis
|
141
|
+
#
|
142
|
+
# Examples:
|
143
|
+
#
|
144
|
+
# #>pdf-center
|
145
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
146
|
+
# inner_a: 20, inner_b: 10)
|
147
|
+
# canvas.draw(solid_arc).stroke
|
148
|
+
# canvas.stroke_color("red").draw(solid_arc, outer_a: 45).stroke
|
81
149
|
attr_reader :outer_a
|
82
150
|
|
83
|
-
# Length of outer semi-minor axis
|
151
|
+
# Length of outer semi-minor axis which (without altering the #inclination) is parallel to the
|
152
|
+
# y-axis
|
153
|
+
#
|
154
|
+
# Examples:
|
155
|
+
#
|
156
|
+
# #>pdf-center
|
157
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
158
|
+
# inner_a: 20, inner_b: 10)
|
159
|
+
# canvas.draw(solid_arc).stroke
|
160
|
+
# canvas.stroke_color("red").draw(solid_arc, outer_b: 40).stroke
|
84
161
|
attr_reader :outer_b
|
85
162
|
|
86
|
-
# Start angle in degrees
|
163
|
+
# Start angle of the solid arc in degrees
|
164
|
+
#
|
165
|
+
# Examples:
|
166
|
+
#
|
167
|
+
# #>pdf-center
|
168
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
169
|
+
# inner_a: 20, inner_b: 10)
|
170
|
+
# canvas.draw(solid_arc).stroke
|
171
|
+
# canvas.stroke_color("red").draw(solid_arc, start_angle: 60).stroke
|
87
172
|
attr_reader :start_angle
|
88
173
|
|
89
|
-
# End angle in degrees
|
174
|
+
# End angle of the solid arc in degrees
|
175
|
+
#
|
176
|
+
# Examples:
|
177
|
+
#
|
178
|
+
# #>pdf-center
|
179
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
180
|
+
# inner_a: 20, inner_b: 10)
|
181
|
+
# canvas.draw(solid_arc).stroke
|
182
|
+
# canvas.stroke_color("red").draw(solid_arc, end_angle: 120).stroke
|
90
183
|
attr_reader :end_angle
|
91
184
|
|
92
185
|
# Inclination in degrees of semi-major axis in respect to x-axis
|
186
|
+
#
|
187
|
+
# Examples:
|
188
|
+
#
|
189
|
+
# #>pdf-center
|
190
|
+
# solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
|
191
|
+
# inner_a: 20, inner_b: 10)
|
192
|
+
# canvas.draw(solid_arc).stroke
|
193
|
+
# canvas.stroke_color("red").draw(solid_arc, inclination: 40).stroke
|
93
194
|
attr_reader :inclination
|
94
195
|
|
95
196
|
# Creates a solid arc with default values (a unit disk at the origin).
|
data/lib/hexapdf/dictionary.rb
CHANGED
@@ -318,8 +318,8 @@ module HexaPDF
|
|
318
318
|
value[name] = document.add(obj)
|
319
319
|
elsif !field.indirect && obj.kind_of?(HexaPDF::Object) && obj.indirect?
|
320
320
|
yield("Field #{name} needs to be a direct object", true)
|
321
|
-
document.delete(obj)
|
322
321
|
value[name] = obj.value
|
322
|
+
document.delete(obj)
|
323
323
|
end
|
324
324
|
end
|
325
325
|
end
|
@@ -213,7 +213,9 @@ module HexaPDF
|
|
213
213
|
end
|
214
214
|
|
215
215
|
handler = handler.new(document)
|
216
|
-
document.trailer[:Encrypt] = handler.set_up_decryption(dict, **options)
|
216
|
+
dict = document.trailer[:Encrypt] = handler.set_up_decryption(dict, **options)
|
217
|
+
HexaPDF::Object.make_direct(dict.value)
|
218
|
+
document.revisions.current.update(dict)
|
217
219
|
document.revisions.each do |r|
|
218
220
|
loader = r.loader
|
219
221
|
r.loader = lambda do |xref_entry|
|
data/lib/hexapdf/object.rb
CHANGED
@@ -141,6 +141,24 @@ module HexaPDF
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
+
# Makes sure that the object itself as well as all nested values are direct objects.
|
145
|
+
#
|
146
|
+
# If an indirect object is found, it is turned into a direct object and the indirect object is
|
147
|
+
# deleted from the document.
|
148
|
+
def self.make_direct(object)
|
149
|
+
if object.kind_of?(HexaPDF::Object) && object.indirect?
|
150
|
+
object_to_delete = object
|
151
|
+
object = object.value
|
152
|
+
object_to_delete.document.delete(object_to_delete)
|
153
|
+
end
|
154
|
+
if object.kind_of?(Hash)
|
155
|
+
object.transform_values! {|val| make_direct(val) }
|
156
|
+
elsif object.kind_of?(Array)
|
157
|
+
object.map! {|val| make_direct(val) }
|
158
|
+
end
|
159
|
+
object
|
160
|
+
end
|
161
|
+
|
144
162
|
# The wrapped HexaPDF::PDFData value.
|
145
163
|
#
|
146
164
|
# This attribute is not part of the public API!
|
data/lib/hexapdf/serializer.rb
CHANGED
@@ -227,8 +227,8 @@ module HexaPDF
|
|
227
227
|
# Note: Rich text fields are currently not supported!
|
228
228
|
def create_text_appearances
|
229
229
|
default_resources = @document.acro_form.default_resources
|
230
|
-
font, font_size = retrieve_font_information(default_resources)
|
231
|
-
style = HexaPDF::Layout::Style.new(font: font)
|
230
|
+
font, font_size, font_color = retrieve_font_information(default_resources)
|
231
|
+
style = HexaPDF::Layout::Style.new(font: font, fill_color: font_color)
|
232
232
|
border_style = @widget.border_style
|
233
233
|
padding = [1, border_style.width].max
|
234
234
|
|
@@ -482,7 +482,7 @@ module HexaPDF
|
|
482
482
|
|
483
483
|
# Returns the font wrapper and font size to be used for a variable text field.
|
484
484
|
def retrieve_font_information(resources)
|
485
|
-
font_name, font_size = @field.parse_default_appearance_string
|
485
|
+
font_name, font_size, font_color = @field.parse_default_appearance_string
|
486
486
|
font_object = resources.font(font_name) rescue nil
|
487
487
|
font = font_object&.font_wrapper
|
488
488
|
unless font
|
@@ -498,7 +498,7 @@ module HexaPDF
|
|
498
498
|
raise(HexaPDF::Error, "Font #{font_name} of the AcroForm's default resources not usable")
|
499
499
|
end
|
500
500
|
end
|
501
|
-
[font, font_size]
|
501
|
+
[font, font_size, font_color]
|
502
502
|
end
|
503
503
|
|
504
504
|
# Calculates the font size for text fields based on the font and font size of the default
|
@@ -157,8 +157,8 @@ module HexaPDF
|
|
157
157
|
# The optional keyword arguments allow setting often used properties of the field:
|
158
158
|
#
|
159
159
|
# +font+::
|
160
|
-
# The font that should be used for the text of the field. If +font_size+
|
161
|
-
# +
|
160
|
+
# The font that should be used for the text of the field. If +font_size+, +font_options+
|
161
|
+
# or +font_color+ is specified but +font+ isn't, the font Helvetica is used.
|
162
162
|
#
|
163
163
|
# If no font is set on the text field, the default font properties of the AcroForm form
|
164
164
|
# are used. Note that field specific or form specific font properties have to be set.
|
@@ -169,15 +169,20 @@ module HexaPDF
|
|
169
169
|
# A hash with font options like :variant that should be used.
|
170
170
|
#
|
171
171
|
# +font_size+::
|
172
|
-
# The font size that should be used. If +font+ or +
|
173
|
-
# +font_size+ isn't, font size defaults to 0 (= auto-sizing).
|
172
|
+
# The font size that should be used. If +font+, +font_options+ or +font_color+ is
|
173
|
+
# specified but +font_size+ isn't, font size defaults to 0 (= auto-sizing).
|
174
|
+
#
|
175
|
+
# +font_color+::
|
176
|
+
# The font color that should be used. If +font+, +font_options+ or +font_size+ is
|
177
|
+
# specified but +font_color+ isn't, font color defaults to 0 (i.e. black).
|
174
178
|
#
|
175
179
|
# +align+::
|
176
180
|
# The alignment of the text, either :left, :center or :right.
|
177
|
-
def create_text_field(name, font: nil, font_options: nil, font_size: nil,
|
181
|
+
def create_text_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil,
|
182
|
+
align: nil)
|
178
183
|
create_field(name, :Tx) do |field|
|
179
184
|
apply_variable_text_properties(field, font: font, font_options: font_options,
|
180
|
-
font_size: font_size, align: align)
|
185
|
+
font_size: font_size, font_color: font_color, align: align)
|
181
186
|
end
|
182
187
|
end
|
183
188
|
|
@@ -189,11 +194,11 @@ module HexaPDF
|
|
189
194
|
# The optional keyword arguments allow setting often used properties of the field, see
|
190
195
|
# #create_text_field for details.
|
191
196
|
def create_multiline_text_field(name, font: nil, font_options: nil, font_size: nil,
|
192
|
-
align: nil)
|
197
|
+
font_color: nil, align: nil)
|
193
198
|
create_field(name, :Tx) do |field|
|
194
199
|
field.initialize_as_multiline_text_field
|
195
200
|
apply_variable_text_properties(field, font: font, font_options: font_options,
|
196
|
-
font_size: font_size, align: align)
|
201
|
+
font_size: font_size, font_color: font_color, align: align)
|
197
202
|
end
|
198
203
|
end
|
199
204
|
|
@@ -208,11 +213,11 @@ module HexaPDF
|
|
208
213
|
# The optional keyword arguments allow setting often used properties of the field, see
|
209
214
|
# #create_text_field for details.
|
210
215
|
def create_comb_text_field(name, max_chars:, font: nil, font_options: nil, font_size: nil,
|
211
|
-
align: nil)
|
216
|
+
font_color: nil, align: nil)
|
212
217
|
create_field(name, :Tx) do |field|
|
213
218
|
field.initialize_as_comb_text_field
|
214
219
|
apply_variable_text_properties(field, font: font, font_options: font_options,
|
215
|
-
font_size: font_size, align: align)
|
220
|
+
font_size: font_size, font_color: font_color, align: align)
|
216
221
|
field[:MaxLen] = max_chars
|
217
222
|
end
|
218
223
|
end
|
@@ -224,11 +229,12 @@ module HexaPDF
|
|
224
229
|
#
|
225
230
|
# The optional keyword arguments allow setting often used properties of the field, see
|
226
231
|
# #create_text_field for details.
|
227
|
-
def create_file_select_field(name, font: nil, font_options: nil, font_size: nil,
|
232
|
+
def create_file_select_field(name, font: nil, font_options: nil, font_size: nil,
|
233
|
+
font_color: nil, align: nil)
|
228
234
|
create_field(name, :Tx) do |field|
|
229
235
|
field.initialize_as_file_select_field
|
230
236
|
apply_variable_text_properties(field, font: font, font_options: font_options,
|
231
|
-
font_size: font_size, align: align)
|
237
|
+
font_size: font_size, font_color: font_color, align: align)
|
232
238
|
end
|
233
239
|
end
|
234
240
|
|
@@ -239,11 +245,12 @@ module HexaPDF
|
|
239
245
|
#
|
240
246
|
# The optional keyword arguments allow setting often used properties of the field, see
|
241
247
|
# #create_text_field for details.
|
242
|
-
def create_password_field(name, font: nil, font_options: nil, font_size: nil,
|
248
|
+
def create_password_field(name, font: nil, font_options: nil, font_size: nil,
|
249
|
+
font_color: nil, align: nil)
|
243
250
|
create_field(name, :Tx) do |field|
|
244
251
|
field.initialize_as_password_field
|
245
252
|
apply_variable_text_properties(field, font: font, font_options: font_options,
|
246
|
-
font_size: font_size, align: align)
|
253
|
+
font_size: font_size, font_color: font_color, align: align)
|
247
254
|
end
|
248
255
|
end
|
249
256
|
|
@@ -280,13 +287,13 @@ module HexaPDF
|
|
280
287
|
# +font+, +font_options+, +font_size+ and +align+::
|
281
288
|
# See #create_text_field
|
282
289
|
def create_combo_box(name, option_items: nil, editable: nil, font: nil,
|
283
|
-
font_options: nil, font_size: nil, align: nil)
|
290
|
+
font_options: nil, font_size: nil, font_color: nil, align: nil)
|
284
291
|
create_field(name, :Ch) do |field|
|
285
292
|
field.initialize_as_combo_box
|
286
293
|
field.option_items = option_items if option_items
|
287
294
|
field.flag(:edit) if editable
|
288
295
|
apply_variable_text_properties(field, font: font, font_options: font_options,
|
289
|
-
font_size: font_size, align: align)
|
296
|
+
font_size: font_size, font_color: font_color, align: align)
|
290
297
|
end
|
291
298
|
end
|
292
299
|
|
@@ -306,13 +313,13 @@ module HexaPDF
|
|
306
313
|
# +font+, +font_options+, +font_size+ and +align+::
|
307
314
|
# See #create_text_field.
|
308
315
|
def create_list_box(name, option_items: nil, multi_select: nil, font: nil,
|
309
|
-
font_options: nil, font_size: nil, align: nil)
|
316
|
+
font_options: nil, font_size: nil, font_color: nil, align: nil)
|
310
317
|
create_field(name, :Ch) do |field|
|
311
318
|
field.initialize_as_list_box
|
312
319
|
field.option_items = option_items if option_items
|
313
320
|
field.flag(:multi_select) if multi_select
|
314
321
|
apply_variable_text_properties(field, font: font, font_options: font_options,
|
315
|
-
font_size: font_size, align: align)
|
322
|
+
font_size: font_size, font_color: font_color, align: align)
|
316
323
|
end
|
317
324
|
end
|
318
325
|
|
@@ -322,13 +329,16 @@ module HexaPDF
|
|
322
329
|
type: :XXResources)
|
323
330
|
end
|
324
331
|
|
325
|
-
# Sets the global default appearance string using the provided values
|
332
|
+
# Sets the global default appearance string using the provided values or the default values
|
333
|
+
# which provide a sane default.
|
326
334
|
#
|
327
|
-
#
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
335
|
+
# See VariableTextField::create_appearance_string for information on the arguments.
|
336
|
+
def set_default_appearance_string(font: 'Helvetica', font_options: {}, font_size: 0,
|
337
|
+
font_color: 0)
|
338
|
+
self[:DA] = VariableTextField.create_appearance_string(document, font: font,
|
339
|
+
font_options: font_options,
|
340
|
+
font_size: font_size,
|
341
|
+
font_color: font_color)
|
332
342
|
end
|
333
343
|
|
334
344
|
# Sets the /NeedAppearances field to +true+.
|
@@ -420,11 +430,12 @@ module HexaPDF
|
|
420
430
|
|
421
431
|
# Applies the given variable field properties to the field.
|
422
432
|
def apply_variable_text_properties(field, font: nil, font_options: nil, font_size: nil,
|
423
|
-
align: nil)
|
424
|
-
if font || font_options || font_size
|
433
|
+
font_color: nil, align: nil)
|
434
|
+
if font || font_options || font_size || font_color
|
425
435
|
field.set_default_appearance_string(font: font || 'Helvetica',
|
426
436
|
font_options: font_options || {},
|
427
|
-
font_size: font_size || 0
|
437
|
+
font_size: font_size || 0,
|
438
|
+
font_color: font_color || 0)
|
428
439
|
end
|
429
440
|
field.text_alignment(align) if align
|
430
441
|
end
|
@@ -437,7 +448,7 @@ module HexaPDF
|
|
437
448
|
yield("When the field /DA is present, the field /DR must also be present")
|
438
449
|
return
|
439
450
|
end
|
440
|
-
font_name,
|
451
|
+
font_name, = VariableTextField.parse_appearance_string(da)
|
441
452
|
if font_name && !(self[:DR][:Font] && self[:DR][:Font][font_name])
|
442
453
|
yield("The font specified in /DA is not in the /DR resource dictionary")
|
443
454
|
end
|
@@ -37,7 +37,7 @@
|
|
37
37
|
require 'hexapdf/dictionary'
|
38
38
|
require 'hexapdf/stream'
|
39
39
|
require 'hexapdf/error'
|
40
|
-
require 'hexapdf/content
|
40
|
+
require 'hexapdf/content'
|
41
41
|
|
42
42
|
module HexaPDF
|
43
43
|
module Type
|
@@ -61,14 +61,53 @@ module HexaPDF
|
|
61
61
|
|
62
62
|
UNSET_ARG = ::Object.new # :nodoc:
|
63
63
|
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
64
|
+
# Creates an AcroForm appearance string for the HexaPDF +document+ from the given arguments
|
65
|
+
# and returns it.
|
66
|
+
#
|
67
|
+
# +font+::
|
68
|
+
# The name of the font.
|
69
|
+
#
|
70
|
+
# +font_options+::
|
71
|
+
# Additional font options like :variant used when loading the font. See
|
72
|
+
# HexaPDF::Document::Fonts#add
|
73
|
+
#
|
74
|
+
# +font_size+::
|
75
|
+
# The font size. If this is set to 0, the font size is calculated using the height/width
|
76
|
+
# of the field.
|
77
|
+
#
|
78
|
+
# +font_color+::
|
79
|
+
# The font color. See HexaPDF::Content::ColorSpace.device_color_from_specification for
|
80
|
+
# allowed values.
|
81
|
+
def self.create_appearance_string(document, font: 'Helvetica', font_options: {},
|
82
|
+
font_size: 0, font_color: 0)
|
83
|
+
name = document.acro_form(create: true).default_resources.
|
84
|
+
add_font(document.fonts.add(font, **font_options).pdf_object)
|
85
|
+
font_color = HexaPDF::Content::ColorSpace.device_color_from_specification(font_color)
|
86
|
+
color_string = HexaPDF::Content::ColorSpace.serialize_device_color(font_color)
|
87
|
+
"#{color_string.chomp} /#{name} #{font_size} Tf"
|
88
|
+
end
|
89
|
+
|
90
|
+
# :call-seq:
|
91
|
+
# VariableTextField.parse_appearance_string(string) -> [font_name, font_size, font_color]
|
92
|
+
# VariableTextField.parse_appearance_string(string) {|obj, params| block } -> nil
|
93
|
+
#
|
94
|
+
# Parses the given appearance string.
|
95
|
+
#
|
96
|
+
# If no block is given, the appearance string is searched for font name, font size and font
|
97
|
+
# color all of which are returned. Otherwise the block is called with each found content
|
98
|
+
# stream operator and has to handle them itself.
|
67
99
|
def self.parse_appearance_string(appearance_string, &block) # :yield: obj, params
|
68
|
-
font_params = nil
|
69
|
-
block ||= lambda
|
100
|
+
font_params = [nil, nil, nil]
|
101
|
+
block ||= lambda do |obj, params|
|
102
|
+
case obj
|
103
|
+
when :Tf
|
104
|
+
font_params[0, 2] = params
|
105
|
+
when :rg, :g, :k
|
106
|
+
font_params[2] = HexaPDF::Content::ColorSpace.prenormalized_device_color(params)
|
107
|
+
end
|
108
|
+
end
|
70
109
|
HexaPDF::Content::Parser.parse(appearance_string.sub(/\/\//, '/'), &block)
|
71
|
-
font_params
|
110
|
+
block_given? ? nil : font_params
|
72
111
|
end
|
73
112
|
|
74
113
|
# :call-seq:
|
@@ -99,21 +138,20 @@ module HexaPDF
|
|
99
138
|
end
|
100
139
|
end
|
101
140
|
|
102
|
-
# Sets the default appearance string using the provided values
|
103
|
-
#
|
104
|
-
# The default argument values are a sane default. If +font_size+ is set to 0, the font size
|
105
|
-
# is calculated using the height/width of the field.
|
141
|
+
# Sets the default appearance string using the provided values or the default values which
|
142
|
+
# provide a sane default.
|
106
143
|
#
|
107
|
-
#
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
144
|
+
# See ::create_appearance_string for information on the arguments.
|
145
|
+
def set_default_appearance_string(font: 'Helvetica', font_options: {}, font_size: 0,
|
146
|
+
font_color: 0)
|
147
|
+
self[:DA] = self.class.create_appearance_string(document, font: font,
|
148
|
+
font_options: font_options,
|
149
|
+
font_size: font_size,
|
150
|
+
font_color: font_color)
|
113
151
|
end
|
114
152
|
|
115
153
|
# Parses the default appearance string and returns an array containing [font_name,
|
116
|
-
# font_size].
|
154
|
+
# font_size, font_color].
|
117
155
|
#
|
118
156
|
# The default appearance string is taken from the field or, if not set, the default
|
119
157
|
# appearance string of the form.
|
@@ -268,6 +268,7 @@ module HexaPDF
|
|
268
268
|
style ||= (field.check_box? ? :check : :cicrle)
|
269
269
|
size ||= 0
|
270
270
|
color = Content::ColorSpace.device_color_from_specification(color || 0)
|
271
|
+
serialized_color = Content::ColorSpace.serialize_device_color(color)
|
271
272
|
|
272
273
|
self[:MK] ||= {}
|
273
274
|
self[:MK][:CA] = case style
|
@@ -281,13 +282,6 @@ module HexaPDF
|
|
281
282
|
else
|
282
283
|
raise ArgumentError, "Unknown value #{style} for argument 'style'"
|
283
284
|
end
|
284
|
-
operator = case color.color_space.family
|
285
|
-
when :DeviceRGB then :rg
|
286
|
-
when :DeviceGray then :g
|
287
|
-
when :DeviceCMYK then :k
|
288
|
-
end
|
289
|
-
serialized_color = Content::Operator::DEFAULT_OPERATORS[operator].
|
290
|
-
serialize(HexaPDF::Serializer.new, *color.components)
|
291
285
|
self[:DA] = "/ZaDb #{size} Tf #{serialized_color}".strip
|
292
286
|
else
|
293
287
|
style = case self[:MK]&.[](:CA)
|
@@ -306,16 +300,10 @@ module HexaPDF
|
|
306
300
|
end
|
307
301
|
end
|
308
302
|
size = 0
|
309
|
-
color = [0]
|
303
|
+
color = HexaPDF::Content::ColorSpace.prenormalized_device_color([0])
|
310
304
|
if (da = self[:DA] || field[:DA])
|
311
|
-
HexaPDF::Type::AcroForm::VariableTextField.parse_appearance_string(da)
|
312
|
-
case obj
|
313
|
-
when :rg, :g, :k then color = params.dup
|
314
|
-
when :Tf then size = params[1]
|
315
|
-
end
|
316
|
-
end
|
305
|
+
_, size, color = HexaPDF::Type::AcroForm::VariableTextField.parse_appearance_string(da)
|
317
306
|
end
|
318
|
-
color = HexaPDF::Content::ColorSpace.prenormalized_device_color(color)
|
319
307
|
|
320
308
|
MarkerStyle.new(style, size, color)
|
321
309
|
end
|
data/lib/hexapdf/version.rb
CHANGED
@@ -55,21 +55,29 @@ describe HexaPDF::Content::GraphicObject::EndpointArc do
|
|
55
55
|
|
56
56
|
it "draws the arc onto the canvas" do
|
57
57
|
{
|
58
|
-
[false, false] => {cx: 100, cy: 50, start_angle: 180, end_angle: 270, clockwise: false},
|
59
|
-
[false, true] => {cx: 50, cy: 25, start_angle: 90, end_angle: 0, clockwise: true},
|
60
|
-
[true, false] => {cx: 50, cy: 25, start_angle: 90, end_angle: 360, clockwise: false},
|
61
|
-
[true, true] => {cx:
|
58
|
+
[false, false] => {cx: 100, cy: 50, a: 50, b: 25, start_angle: 180, end_angle: 270, clockwise: false},
|
59
|
+
[false, true] => {cx: 50, cy: 25, a: 50, b: 25, start_angle: 90, end_angle: 0, clockwise: true},
|
60
|
+
[true, false] => {cx: 50, cy: 25, a: 50, b: 25, start_angle: 90, end_angle: 360, clockwise: false},
|
61
|
+
[true, true] => {cx: 0, cy: 0, a: 40, b: 30, start_angle: 60, end_angle: 120},
|
62
62
|
}.each do |(large_arc, clockwise), data|
|
63
63
|
@page.delete(:Contents)
|
64
64
|
canvas = @page.canvas
|
65
|
-
canvas.
|
65
|
+
arc = canvas.graphic_object(:arc, **data)
|
66
|
+
canvas.draw(arc)
|
66
67
|
arc_data = @page.contents
|
67
68
|
|
68
69
|
canvas.contents.clear
|
69
70
|
assert(@page.contents.empty?)
|
70
|
-
canvas.move_to(
|
71
|
-
canvas.
|
72
|
-
|
71
|
+
canvas.move_to(*arc.start_point)
|
72
|
+
earc = canvas.graphic_object(:endpoint_arc, x: arc.end_point[0], y: arc.end_point[1],
|
73
|
+
a: data[:a], b: data[:b], inclination: data[:inclination] || 0,
|
74
|
+
large_arc: large_arc, clockwise: clockwise)
|
75
|
+
canvas.draw(earc)
|
76
|
+
narc = canvas.graphic_object(:arc, **earc.send(:compute_arc_values, *arc.start_point))
|
77
|
+
assert_in_delta(arc.start_point[0], narc.start_point[0], 0.0001)
|
78
|
+
assert_in_delta(arc.start_point[1], narc.start_point[1], 0.0001)
|
79
|
+
assert_in_delta(arc.end_point[0], narc.end_point[0], 0.0001)
|
80
|
+
assert_in_delta(arc.end_point[1], narc.end_point[1], 0.0001)
|
73
81
|
assert_equal(arc_data, @page.contents)
|
74
82
|
end
|
75
83
|
end
|
@@ -99,6 +99,32 @@ describe HexaPDF::Content::ColorSpace do
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
describe "self.serialize_device_color" do
|
103
|
+
it "works for device gray colors" do
|
104
|
+
color = @class.device_color_from_specification(0.5)
|
105
|
+
assert_equal("0.5 g\n", @class.serialize_device_color(color))
|
106
|
+
assert_equal("0.5 G\n", @class.serialize_device_color(color, type: :stroke))
|
107
|
+
end
|
108
|
+
|
109
|
+
it "works for device RGB colors" do
|
110
|
+
color = @class.device_color_from_specification("red")
|
111
|
+
assert_equal("1.0 0.0 0.0 rg\n", @class.serialize_device_color(color))
|
112
|
+
assert_equal("1.0 0.0 0.0 RG\n", @class.serialize_device_color(color, type: :stroke))
|
113
|
+
end
|
114
|
+
|
115
|
+
it "works for device CMYK colors" do
|
116
|
+
color = @class.device_color_from_specification([100, 100, 100, 0])
|
117
|
+
assert_equal("1.0 1.0 1.0 0.0 k\n", @class.serialize_device_color(color))
|
118
|
+
assert_equal("1.0 1.0 1.0 0.0 K\n", @class.serialize_device_color(color, type: :stroke))
|
119
|
+
end
|
120
|
+
|
121
|
+
it "fails if no device color is provided" do
|
122
|
+
assert_raises(ArgumentError) do
|
123
|
+
@class.serialize_device_color(@class::Universal.new([]).default_color)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
102
128
|
it "returns a device color object for prenormalized color values" do
|
103
129
|
assert_equal([5, 6, 7], @class.prenormalized_device_color([5, 6, 7]).components)
|
104
130
|
end
|
@@ -107,9 +107,11 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
107
107
|
end
|
108
108
|
|
109
109
|
it "updates the trailer's /Encrypt entry to be wrapped by an encryption dictionary" do
|
110
|
-
@document.trailer[:Encrypt] = {Filter: :Test,
|
110
|
+
@document.trailer[:Encrypt] = {Filter: :Test,
|
111
|
+
V: HexaPDF::Object.new(1, oid: 1, document: @document)}
|
111
112
|
HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
|
112
113
|
assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, @document.trailer[:Encrypt])
|
114
|
+
assert_equal({Filter: :Test, V: 1}, @document.trailer[:Encrypt].value)
|
113
115
|
end
|
114
116
|
|
115
117
|
it "returns the frozen security handler" do
|
data/test/hexapdf/test_object.rb
CHANGED
@@ -45,6 +45,34 @@ describe HexaPDF::Object do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
describe "class.make_direct" do
|
49
|
+
before do
|
50
|
+
@doc = HexaPDF::Document.new
|
51
|
+
end
|
52
|
+
|
53
|
+
it "doesn't touch wrapped direct objects" do
|
54
|
+
obj = HexaPDF::Object.new(5)
|
55
|
+
assert_same(obj, HexaPDF::Object.make_direct(obj))
|
56
|
+
end
|
57
|
+
|
58
|
+
it "works for simple values" do
|
59
|
+
obj = HexaPDF::Object.new(5, oid: 1, document: @doc)
|
60
|
+
assert_same(5, HexaPDF::Object.make_direct(obj))
|
61
|
+
end
|
62
|
+
|
63
|
+
it "works for hashes" do
|
64
|
+
obj = HexaPDF::Dictionary.new({a: 5, b: HexaPDF::Object.new(:a, oid: 3, document: @doc)},
|
65
|
+
oid: 1, document: @doc)
|
66
|
+
assert_equal({a: 5, b: :a}, HexaPDF::Object.make_direct(obj))
|
67
|
+
end
|
68
|
+
|
69
|
+
it "works for arrays" do
|
70
|
+
obj = HexaPDF::PDFArray.new([:b, HexaPDF::Object.new(:a, oid: 3, document: @doc)],
|
71
|
+
oid: 1, document: @doc)
|
72
|
+
assert_equal([:b, :a], HexaPDF::Object.make_direct(obj))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
48
76
|
describe "initialize" do
|
49
77
|
it "uses a simple value as is" do
|
50
78
|
obj = HexaPDF::Object.new(5)
|
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.
|
43
|
+
<</Producer(HexaPDF version 0.18.0)>>
|
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.
|
75
|
+
<</Producer(HexaPDF version 0.18.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -499,6 +499,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
499
499
|
|
500
500
|
it "creates the /N appearance stream according to the set string" do
|
501
501
|
@field.field_value = 'Text'
|
502
|
+
@field.set_default_appearance_string(font_color: "red")
|
502
503
|
@generator.create_appearances
|
503
504
|
assert_operators(@widget[:AP][:N].stream,
|
504
505
|
[[:begin_marked_content, [:Tx]],
|
@@ -507,6 +508,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
507
508
|
[:clip_path_non_zero],
|
508
509
|
[:end_path],
|
509
510
|
[:set_font_and_size, [:F1, 6.641436]],
|
511
|
+
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
510
512
|
[:begin_text],
|
511
513
|
[:set_text_matrix, [1, 0, 0, 1, 2, 3.240724]],
|
512
514
|
[:show_text, ["Text"]],
|
@@ -556,6 +558,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
556
558
|
|
557
559
|
it "creates the /N appearance stream according to the set string" do
|
558
560
|
@field.field_value = "Test\nValue"
|
561
|
+
@field.set_default_appearance_string(font_size: 10, font_color: "red")
|
559
562
|
@generator.create_appearances
|
560
563
|
assert_operators(@widget[:AP][:N].stream,
|
561
564
|
[[:begin_marked_content, [:Tx]],
|
@@ -566,6 +569,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
566
569
|
[:save_graphics_state],
|
567
570
|
[:set_leading, [11.5625]],
|
568
571
|
[:set_font_and_size, [:F1, 10]],
|
572
|
+
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
569
573
|
[:begin_text],
|
570
574
|
[:set_text_matrix, [1, 0, 0, 1, 2, 16.195]],
|
571
575
|
[:show_text, ['Test']],
|
@@ -653,6 +657,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
653
657
|
|
654
658
|
it "creates the /N appearance stream according to the set string" do
|
655
659
|
@field.field_value = 'Text'
|
660
|
+
@field.set_default_appearance_string(font_size: 10, font_color: "red")
|
656
661
|
@generator.create_appearances
|
657
662
|
assert_operators(@widget[:AP][:N].stream,
|
658
663
|
[[:begin_marked_content, [:Tx]],
|
@@ -661,6 +666,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
661
666
|
[:clip_path_non_zero],
|
662
667
|
[:end_path],
|
663
668
|
[:set_font_and_size, [:F1, 10]],
|
669
|
+
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
664
670
|
[:begin_text],
|
665
671
|
[:set_text_matrix, [1, 0, 0, 1, 2.945, 6.41]],
|
666
672
|
[:show_text_with_positioning, [['T', -416.5, 'e', -472, 'x', -611, 't']]],
|
@@ -721,6 +727,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
721
727
|
it "creates the /N appearance stream" do
|
722
728
|
@field[:I] = [1, 2]
|
723
729
|
@field[:V] = ['b', 'c']
|
730
|
+
@field.set_default_appearance_string(font_size: 12, font_color: "red")
|
724
731
|
@generator.create_appearances
|
725
732
|
assert_operators(@widget[:AP][:N].stream,
|
726
733
|
[[:begin_marked_content, [:Tx]],
|
@@ -734,7 +741,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
734
741
|
[:save_graphics_state],
|
735
742
|
[:set_leading, [13.875]],
|
736
743
|
[:set_font_and_size, [:F1, 12]],
|
737
|
-
[:
|
744
|
+
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
738
745
|
[:begin_text],
|
739
746
|
[:set_text_matrix, [1, 0, 0, 1, 2, 23.609]],
|
740
747
|
[:show_text, ["a"]],
|
@@ -8,6 +8,7 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
8
8
|
before do
|
9
9
|
@doc = HexaPDF::Document.new
|
10
10
|
@acro_form = @doc.add({Fields: []}, type: :XXAcroForm)
|
11
|
+
@doc.catalog[:AcroForm] = @acro_form
|
11
12
|
end
|
12
13
|
|
13
14
|
describe "signature flags" do
|
@@ -140,12 +141,13 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
140
141
|
|
141
142
|
def applies_variable_text_properties(method, **args)
|
142
143
|
field = @acro_form.send(method, "field", **args, font: 'Times')
|
143
|
-
font_name, font_size = field.parse_default_appearance_string
|
144
|
+
font_name, font_size, font_color = field.parse_default_appearance_string
|
144
145
|
assert_equal(:'Times-Roman', @acro_form.default_resources.font(font_name)[:BaseFont])
|
145
146
|
assert_equal(0, font_size)
|
147
|
+
assert_equal(HexaPDF::Content::ColorSpace::DeviceGray.new.color(0), font_color)
|
146
148
|
|
147
149
|
field = @acro_form.send(method, "field", **args, font_options: {variant: :bold})
|
148
|
-
font_name,
|
150
|
+
font_name, = field.parse_default_appearance_string
|
149
151
|
assert_equal(:'Helvetica-Bold', @acro_form.default_resources.font(font_name)[:BaseFont])
|
150
152
|
|
151
153
|
field = @acro_form.send(method, "field", **args, font_size: 10)
|
@@ -153,6 +155,10 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
153
155
|
assert_equal(:Helvetica, @acro_form.default_resources.font(font_name)[:BaseFont])
|
154
156
|
assert_equal(10, font_size)
|
155
157
|
|
158
|
+
field = @acro_form.send(method, "field", **args, font_color: "red")
|
159
|
+
_, _, font_color = field.parse_default_appearance_string
|
160
|
+
assert_equal(HexaPDF::Content::ColorSpace::DeviceRGB.new.color(255, 0, 0), font_color)
|
161
|
+
|
156
162
|
field = @acro_form.send(method, "field", **args, font: 'Courier', font_size: 10, align: :center)
|
157
163
|
font_name, font_size = field.parse_default_appearance_string
|
158
164
|
assert_equal(:Courier, @acro_form.default_resources.font(font_name)[:BaseFont])
|
@@ -229,16 +235,17 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
229
235
|
describe "set_default_appearance_string" do
|
230
236
|
it "uses sane default values if no arguments are provided" do
|
231
237
|
@acro_form.set_default_appearance_string
|
232
|
-
assert_equal("0 g /F1 0 Tf", @acro_form[:DA])
|
238
|
+
assert_equal("0.0 g /F1 0 Tf", @acro_form[:DA])
|
233
239
|
font = @acro_form.default_resources.font(:F1)
|
234
240
|
assert(font)
|
235
241
|
assert_equal(:Helvetica, font[:BaseFont])
|
236
242
|
end
|
237
243
|
|
238
|
-
it "allows specifying the used font and font
|
239
|
-
@acro_form.set_default_appearance_string(font: 'Times',
|
240
|
-
|
241
|
-
assert_equal(
|
244
|
+
it "allows specifying the used font, font size and font color" do
|
245
|
+
@acro_form.set_default_appearance_string(font: 'Times', font_options: {variant: :bold},
|
246
|
+
font_size: 10, font_color: "red")
|
247
|
+
assert_equal("1.0 0.0 0.0 rg /F1 10 Tf", @acro_form[:DA])
|
248
|
+
assert_equal(:'Times-Bold', @acro_form.default_resources.font(:F1)[:BaseFont])
|
242
249
|
end
|
243
250
|
end
|
244
251
|
|
@@ -333,7 +340,7 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
333
340
|
|
334
341
|
it "set the default appearance string, though optional, to a valid value to avoid problems" do
|
335
342
|
assert(@acro_form.validate)
|
336
|
-
assert_equal("0 g /F1 0 Tf", @acro_form[:DA])
|
343
|
+
assert_equal("0.0 g /F1 0 Tf", @acro_form[:DA])
|
337
344
|
end
|
338
345
|
|
339
346
|
describe "automatically creates the terminal fields; appearances" do
|
@@ -31,7 +31,7 @@ describe HexaPDF::Type::AcroForm::VariableTextField do
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
describe "set_default_appearance_string" do
|
34
|
+
describe "set_default_appearance_string / self.create_appearance_string" do
|
35
35
|
it "creates the AcroForm object if it doesn't exist" do
|
36
36
|
@doc.catalog.delete(:AcroForm)
|
37
37
|
@field.set_default_appearance_string
|
@@ -40,7 +40,7 @@ describe HexaPDF::Type::AcroForm::VariableTextField do
|
|
40
40
|
|
41
41
|
it "uses sane default values if no arguments are provided" do
|
42
42
|
@field.set_default_appearance_string
|
43
|
-
assert_equal("0 g /F1 0 Tf", @field[:DA])
|
43
|
+
assert_equal("0.0 g /F1 0 Tf", @field[:DA])
|
44
44
|
font = @doc.acro_form.default_resources.font(:F1)
|
45
45
|
assert(font)
|
46
46
|
assert_equal(:Helvetica, font[:BaseFont])
|
@@ -48,36 +48,46 @@ describe HexaPDF::Type::AcroForm::VariableTextField do
|
|
48
48
|
|
49
49
|
it "allows specifying the font" do
|
50
50
|
@field.set_default_appearance_string(font: 'Times')
|
51
|
-
assert_equal("0 g /F2 0 Tf", @field[:DA])
|
51
|
+
assert_equal("0.0 g /F2 0 Tf", @field[:DA])
|
52
52
|
assert_equal(:'Times-Roman', @doc.acro_form.default_resources.font(:F2)[:BaseFont])
|
53
53
|
end
|
54
54
|
|
55
55
|
it "allows specifying the font options" do
|
56
56
|
@field.set_default_appearance_string(font_options: {variant: :italic})
|
57
|
-
assert_equal("0 g /F2 0 Tf", @field[:DA])
|
57
|
+
assert_equal("0.0 g /F2 0 Tf", @field[:DA])
|
58
58
|
assert_equal(:'Helvetica-Oblique', @doc.acro_form.default_resources.font(:F2)[:BaseFont])
|
59
59
|
end
|
60
60
|
|
61
61
|
it "allows specifying the font size" do
|
62
62
|
@field.set_default_appearance_string(font_size: 10)
|
63
|
-
assert_equal("0 g /F1 10 Tf", @field[:DA])
|
63
|
+
assert_equal("0.0 g /F1 10 Tf", @field[:DA])
|
64
|
+
end
|
65
|
+
|
66
|
+
it "allows specifying the font color" do
|
67
|
+
@field.set_default_appearance_string(font_color: "red")
|
68
|
+
assert_equal("1.0 0.0 0.0 rg /F1 0 Tf", @field[:DA])
|
64
69
|
end
|
65
70
|
end
|
66
71
|
|
67
72
|
describe "parse_default_appearance_string" do
|
73
|
+
before do
|
74
|
+
@color = HexaPDF::Content::ColorSpace.prenormalized_device_color([1])
|
75
|
+
end
|
76
|
+
|
68
77
|
it "parses the default appearance string of the field" do
|
69
78
|
@field[:DA] = "1 g //F1 20 Tf 5 w /F2 10 Tf"
|
70
|
-
assert_equal([:F2, 10], @field.parse_default_appearance_string)
|
79
|
+
assert_equal([:F2, 10, @color], @field.parse_default_appearance_string)
|
71
80
|
end
|
72
81
|
|
73
82
|
it "uses the default appearance string of a parent field" do
|
74
83
|
parent = @doc.add({DA: "/F1 15 Tf"}, type: :XXAcroFormField)
|
75
84
|
@field[:Parent] = parent
|
76
|
-
assert_equal([:F1, 15], @field.parse_default_appearance_string)
|
85
|
+
assert_equal([:F1, 15, nil], @field.parse_default_appearance_string)
|
77
86
|
end
|
78
87
|
|
79
88
|
it "uses the global default appearance string" do
|
80
|
-
assert_equal([:F1, 0
|
89
|
+
assert_equal([:F1, 0, HexaPDF::Content::ColorSpace.prenormalized_device_color([0])],
|
90
|
+
@field.parse_default_appearance_string)
|
81
91
|
end
|
82
92
|
|
83
93
|
it "fails if no /DA value is set" do
|
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.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|