hexapdf 0.17.3 → 0.18.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 +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
|