hexapdf 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -0
- data/README.md +1 -1
- data/lib/hexapdf/cli/command.rb +63 -63
- data/lib/hexapdf/cli/inspect.rb +14 -5
- data/lib/hexapdf/cli/modify.rb +0 -1
- data/lib/hexapdf/cli/optimize.rb +5 -5
- data/lib/hexapdf/composer.rb +14 -0
- data/lib/hexapdf/configuration.rb +26 -0
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +1 -1
- data/lib/hexapdf/document/annotations.rb +173 -0
- data/lib/hexapdf/document/layout.rb +45 -6
- data/lib/hexapdf/document.rb +28 -7
- data/lib/hexapdf/error.rb +11 -3
- data/lib/hexapdf/font/true_type/subsetter.rb +15 -2
- data/lib/hexapdf/font/true_type_wrapper.rb +1 -0
- data/lib/hexapdf/font/type1_wrapper.rb +1 -0
- data/lib/hexapdf/layout/style.rb +101 -7
- data/lib/hexapdf/object.rb +2 -2
- data/lib/hexapdf/pdf_array.rb +25 -3
- data/lib/hexapdf/tokenizer.rb +4 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +57 -8
- data/lib/hexapdf/type/acro_form/field.rb +1 -0
- data/lib/hexapdf/type/acro_form/form.rb +7 -6
- data/lib/hexapdf/type/acro_form/java_script_actions.rb +9 -2
- data/lib/hexapdf/type/acro_form/text_field.rb +9 -2
- data/lib/hexapdf/type/annotation.rb +71 -1
- data/lib/hexapdf/type/annotations/appearance_generator.rb +348 -0
- data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
- data/lib/hexapdf/type/annotations/border_styling.rb +160 -0
- data/lib/hexapdf/type/annotations/circle.rb +65 -0
- data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
- data/lib/hexapdf/type/annotations/line.rb +490 -0
- data/lib/hexapdf/type/annotations/square.rb +65 -0
- data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
- data/lib/hexapdf/type/annotations/widget.rb +52 -116
- data/lib/hexapdf/type/annotations.rb +8 -0
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +0 -1
- data/lib/hexapdf/xref_section.rb +7 -4
- data/test/hexapdf/content/test_graphics_state.rb +2 -3
- data/test/hexapdf/content/test_operator.rb +4 -5
- data/test/hexapdf/digital_signature/test_cms_handler.rb +7 -8
- data/test/hexapdf/digital_signature/test_handler.rb +2 -3
- data/test/hexapdf/digital_signature/test_pkcs1_handler.rb +1 -2
- data/test/hexapdf/document/test_annotations.rb +55 -0
- data/test/hexapdf/document/test_layout.rb +24 -2
- data/test/hexapdf/font/test_true_type_wrapper.rb +7 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +7 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
- data/test/hexapdf/layout/test_style.rb +27 -2
- data/test/hexapdf/task/test_optimize.rb +1 -1
- data/test/hexapdf/test_composer.rb +7 -0
- data/test/hexapdf/test_document.rb +11 -3
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_pdf_array.rb +36 -3
- data/test/hexapdf/test_stream.rb +1 -2
- data/test/hexapdf/test_xref_section.rb +1 -1
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +78 -3
- data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
- data/test/hexapdf/type/acro_form/test_field.rb +5 -0
- data/test/hexapdf/type/acro_form/test_form.rb +17 -1
- data/test/hexapdf/type/acro_form/test_java_script_actions.rb +21 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +7 -1
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +482 -0
- data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
- data/test/hexapdf/type/annotations/test_border_styling.rb +114 -0
- data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
- data/test/hexapdf/type/annotations/test_line.rb +169 -0
- data/test/hexapdf/type/annotations/test_widget.rb +35 -81
- data/test/hexapdf/type/test_annotation.rb +55 -0
- data/test/hexapdf/type/test_form.rb +6 -0
- metadata +17 -2
@@ -48,6 +48,8 @@ module HexaPDF
|
|
48
48
|
# See: PDF2.0 s12.5.6.19, HexaPDF::Type::Annotation
|
49
49
|
class Widget < Annotation
|
50
50
|
|
51
|
+
include BorderStyling
|
52
|
+
|
51
53
|
# The dictionary used by the /MK key of the widget annotation.
|
52
54
|
class AppearanceCharacteristics < Dictionary
|
53
55
|
|
@@ -122,111 +124,21 @@ module HexaPDF
|
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
125
|
-
# Describes the
|
126
|
-
#
|
127
|
-
# The +color+ property is either +nil+ if the border is transparent or else a device color
|
128
|
-
# object - see HexaPDF::Content::ColorSpace.
|
129
|
-
#
|
130
|
-
# The +style+ property can be one of the following:
|
131
|
-
#
|
132
|
-
# :solid:: Solid line.
|
133
|
-
# :beveled:: Embossed rectangle seemingly raised above the surface of the page.
|
134
|
-
# :inset:: Engraved rectangle receeding into the page.
|
135
|
-
# :underlined:: Underlined, i.e. only the bottom border is draw.
|
136
|
-
# Array: Dash array describing how to dash the line.
|
137
|
-
BorderStyle = Struct.new(:width, :color, :style, :horizontal_corner_radius,
|
138
|
-
:vertical_corner_radius)
|
139
|
-
|
140
|
-
# :call-seq:
|
141
|
-
# widget.border_style => border_style
|
142
|
-
# widget.border_style(color: 0, width: 1, style: :solid) => widget
|
143
|
-
#
|
144
|
-
# Returns a BorderStyle instance representing the border style of the widget when no
|
145
|
-
# argument is given. Otherwise sets the border style of the widget and returns self.
|
146
|
-
#
|
147
|
-
# When setting a border style, arguments that are not provided will use the default: a
|
148
|
-
# border with a solid, black, 1pt wide line. This also means that multiple invocations will
|
149
|
-
# reset *all* prior values.
|
150
|
-
#
|
151
|
-
# +color+:: The color of the border. See
|
152
|
-
# HexaPDF::Content::ColorSpace.device_color_from_specification for information on
|
153
|
-
# the allowed arguments.
|
154
|
-
#
|
155
|
-
# If the special value +:transparent+ is used when setting the color, a
|
156
|
-
# transparent is used. A transparent border will return a +nil+ value when getting
|
157
|
-
# the border color.
|
158
|
-
#
|
159
|
-
# +width+:: The width of the border. If set to 0, no border is shown.
|
160
|
-
#
|
161
|
-
# +style+:: Defines how the border is drawn. can be one of the following:
|
162
|
-
#
|
163
|
-
# +:solid+:: Draws a solid border.
|
164
|
-
# +:beveled+:: Draws a beveled border.
|
165
|
-
# +:inset+:: Draws an inset border.
|
166
|
-
# +:underlined+:: Draws only the bottom border.
|
167
|
-
# Array:: An array specifying a line dash pattern (see
|
168
|
-
# HexaPDF::Content::LineDashPattern)
|
169
|
-
def border_style(color: nil, width: nil, style: nil)
|
170
|
-
if color || width || style
|
171
|
-
color = if color == :transparent
|
172
|
-
[]
|
173
|
-
else
|
174
|
-
Content::ColorSpace.device_color_from_specification(color || 0).components
|
175
|
-
end
|
176
|
-
width ||= 1
|
177
|
-
style ||= :solid
|
178
|
-
|
179
|
-
(self[:MK] ||= {})[:BC] = color
|
180
|
-
bs = self[:BS] = {W: width}
|
181
|
-
case style
|
182
|
-
when :solid then bs[:S] = :S
|
183
|
-
when :beveled then bs[:S] = :B
|
184
|
-
when :inset then bs[:S] = :I
|
185
|
-
when :underlined then bs[:S] = :U
|
186
|
-
when Array
|
187
|
-
bs[:S] = :D
|
188
|
-
bs[:D] = style
|
189
|
-
else
|
190
|
-
raise ArgumentError, "Unknown value #{style} for style argument"
|
191
|
-
end
|
192
|
-
self
|
193
|
-
else
|
194
|
-
result = BorderStyle.new(1, nil, :solid, 0, 0)
|
195
|
-
if (ac = self[:MK]) && (bc = ac[:BC]) && !bc.empty?
|
196
|
-
result.color = Content::ColorSpace.prenormalized_device_color(bc.value)
|
197
|
-
end
|
198
|
-
|
199
|
-
if (bs = self[:BS])
|
200
|
-
result.width = bs[:W] if bs.key?(:W)
|
201
|
-
result.style = case bs[:S]
|
202
|
-
when :S then :solid
|
203
|
-
when :B then :beveled
|
204
|
-
when :I then :inset
|
205
|
-
when :U then :underlined
|
206
|
-
when :D then bs[:D].value
|
207
|
-
else :solid
|
208
|
-
end
|
209
|
-
elsif key?(:Border)
|
210
|
-
border = self[:Border]
|
211
|
-
result.horizontal_corner_radius = border[0]
|
212
|
-
result.vertical_corner_radius = border[1]
|
213
|
-
result.width = border[2]
|
214
|
-
result.style = border[3] if border[3]
|
215
|
-
end
|
216
|
-
|
217
|
-
result
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
# Describes the marker style of a check box or radio button widget.
|
127
|
+
# Describes the marker style of a check box, radio button or push button widget.
|
222
128
|
class MarkerStyle
|
223
129
|
|
224
|
-
# The kind of marker that is shown inside the widget.
|
225
|
-
# +:check+, +:circle+, +:cross+, +:diamond+, +:square+ or +:star+, or a one character
|
226
|
-
# string. The latter is interpreted using the ZapfDingbats font.
|
130
|
+
# The kind of marker that is shown inside the widget.
|
227
131
|
#
|
228
|
-
#
|
229
|
-
#
|
132
|
+
# Radion buttons and check boxes::
|
133
|
+
# Can either be one of the symbols +:check+, +:circle+, +:cross+, +:diamond+,
|
134
|
+
# +:square+ or +:star+, or a one character string. The latter is interpreted using the
|
135
|
+
# ZapfDingbats font.
|
136
|
+
#
|
137
|
+
# If an empty string is set, it is treated as if +nil+ was set, i.e. it shows the
|
138
|
+
# default marker for the field type.
|
139
|
+
#
|
140
|
+
# Push buttons:
|
141
|
+
# The caption string.
|
230
142
|
attr_reader :style
|
231
143
|
|
232
144
|
# The size of the marker in PDF points that is shown inside the widget. The special value
|
@@ -237,27 +149,40 @@ module HexaPDF
|
|
237
149
|
# HexaPDF::Content::ColorSpace.
|
238
150
|
attr_reader :color
|
239
151
|
|
152
|
+
# The resource name of the font that should be used for the caption.
|
153
|
+
#
|
154
|
+
# This is only used for push button widgets.
|
155
|
+
attr_reader :font_name
|
156
|
+
|
240
157
|
# Creates a new instance with the given values.
|
241
|
-
def initialize(style, size, color)
|
158
|
+
def initialize(style, size, color, font_name)
|
242
159
|
@style = style
|
243
160
|
@size = size
|
244
161
|
@color = color
|
162
|
+
@font_name = font_name
|
245
163
|
end
|
246
164
|
|
247
165
|
end
|
248
166
|
|
249
167
|
# :call-seq:
|
250
|
-
# widget.marker_style
|
251
|
-
# widget.marker_style(style: nil, size: nil, color: nil) => widget
|
168
|
+
# widget.marker_style => marker_style
|
169
|
+
# widget.marker_style(style: nil, size: nil, color: nil, font_name: nil) => widget
|
252
170
|
#
|
253
171
|
# Returns a MarkerStyle instance representing the marker style of the widget when no
|
254
172
|
# argument is given. Otherwise sets the button marker style of the widget and returns self.
|
255
173
|
#
|
256
|
-
# This method returns valid information only for check boxes and
|
174
|
+
# This method returns valid information only for check boxes, radio buttons and push buttons!
|
257
175
|
#
|
258
|
-
# When setting a marker style, arguments that are not provided will use the default:
|
259
|
-
#
|
260
|
-
#
|
176
|
+
# When setting a marker style, arguments that are not provided will use the default:
|
177
|
+
#
|
178
|
+
# * For check boxes a black auto-sized checkmark (i.e. :check)
|
179
|
+
# * For radio buttons a black auto-sized circle (i.e. :circle)
|
180
|
+
# * For push buttons a black 9pt empty text using Helvetica
|
181
|
+
#
|
182
|
+
# This also means that multiple invocations will reset *all* prior values.
|
183
|
+
#
|
184
|
+
# Note that the +font_name+ argument must be a valid HexaPDF font name (this is in contrast
|
185
|
+
# to MarkerStyle#font_name which returns the resource name of the font).
|
261
186
|
#
|
262
187
|
# Note: The marker is called "normal caption" in the PDF 2.0 spec and the /CA entry of the
|
263
188
|
# associated appearance characteristics dictionary. The marker size and color are set using
|
@@ -265,13 +190,18 @@ module HexaPDF
|
|
265
190
|
# does it).
|
266
191
|
#
|
267
192
|
# See: PDF2.0 s12.5.6.19 and s12.7.4.3
|
268
|
-
def marker_style(style: nil, size: nil, color: nil)
|
193
|
+
def marker_style(style: nil, size: nil, color: nil, font_name: nil)
|
269
194
|
field = form_field
|
270
|
-
if style || size || color
|
271
|
-
style ||=
|
272
|
-
|
195
|
+
if style || size || color || font_name
|
196
|
+
style ||= case field.concrete_field_type
|
197
|
+
when :check_box then :check
|
198
|
+
when :radio_button then :circle
|
199
|
+
when :push_button then ''
|
200
|
+
end
|
201
|
+
size ||= (field.push_button? ? 9 : 0)
|
273
202
|
color = Content::ColorSpace.device_color_from_specification(color || 0)
|
274
203
|
serialized_color = Content::ColorSpace.serialize_device_color(color)
|
204
|
+
font_name ||= 'Helvetica'
|
275
205
|
|
276
206
|
self[:MK] ||= {}
|
277
207
|
self[:MK][:CA] = case style
|
@@ -285,7 +215,13 @@ module HexaPDF
|
|
285
215
|
else
|
286
216
|
raise ArgumentError, "Unknown value #{style} for argument 'style'"
|
287
217
|
end
|
288
|
-
self[:DA] =
|
218
|
+
self[:DA] = if field.push_button?
|
219
|
+
name = document.acro_form(create: true).default_resources.
|
220
|
+
add_font(document.fonts.add(font_name).pdf_object)
|
221
|
+
"/#{name} #{size} Tf #{serialized_color}".strip
|
222
|
+
else
|
223
|
+
"/ZaDb #{size} Tf #{serialized_color}".strip
|
224
|
+
end
|
289
225
|
else
|
290
226
|
style = case self[:MK]&.[](:CA)
|
291
227
|
when '4' then :check
|
@@ -305,12 +241,12 @@ module HexaPDF
|
|
305
241
|
size = 0
|
306
242
|
color = HexaPDF::Content::ColorSpace.prenormalized_device_color([0])
|
307
243
|
if (da = self[:DA] || field[:DA])
|
308
|
-
|
244
|
+
font_name, da_size, da_color = AcroForm::VariableTextField.parse_appearance_string(da)
|
309
245
|
size = da_size || size
|
310
246
|
color = da_color || color
|
311
247
|
end
|
312
248
|
|
313
|
-
MarkerStyle.new(style, size, color)
|
249
|
+
MarkerStyle.new(style, size, color, font_name)
|
314
250
|
end
|
315
251
|
end
|
316
252
|
|
@@ -48,6 +48,14 @@ module HexaPDF
|
|
48
48
|
autoload(:Text, 'hexapdf/type/annotations/text')
|
49
49
|
autoload(:Link, 'hexapdf/type/annotations/link')
|
50
50
|
autoload(:Widget, 'hexapdf/type/annotations/widget')
|
51
|
+
autoload(:BorderStyling, 'hexapdf/type/annotations/border_styling')
|
52
|
+
autoload(:Line, 'hexapdf/type/annotations/line')
|
53
|
+
autoload(:AppearanceGenerator, 'hexapdf/type/annotations/appearance_generator')
|
54
|
+
autoload(:BorderEffect, 'hexapdf/type/annotations/border_effect')
|
55
|
+
autoload(:InteriorColor, 'hexapdf/type/annotations/interior_color')
|
56
|
+
autoload(:SquareCircle, 'hexapdf/type/annotations/square_circle')
|
57
|
+
autoload(:Square, 'hexapdf/type/annotations/square')
|
58
|
+
autoload(:Circle, 'hexapdf/type/annotations/circle')
|
51
59
|
|
52
60
|
end
|
53
61
|
|
data/lib/hexapdf/type/form.rb
CHANGED
@@ -167,14 +167,14 @@ module HexaPDF
|
|
167
167
|
# bounding box.
|
168
168
|
#
|
169
169
|
# *Note* that a canvas can only be retrieved for initially empty form XObjects!
|
170
|
-
def canvas
|
170
|
+
def canvas(translate: true)
|
171
171
|
cache(:canvas) do
|
172
172
|
unless stream.empty?
|
173
173
|
raise HexaPDF::Error, "Cannot create a canvas for a form XObjects with contents"
|
174
174
|
end
|
175
175
|
|
176
176
|
canvas = Content::Canvas.new(self)
|
177
|
-
if box.left != 0 || box.bottom != 0
|
177
|
+
if translate && (box.left != 0 || box.bottom != 0)
|
178
178
|
canvas.save_graphics_state.translate(box.left, box.bottom)
|
179
179
|
end
|
180
180
|
self.stream = canvas.stream_data
|
data/lib/hexapdf/version.rb
CHANGED
data/lib/hexapdf/writer.rb
CHANGED
@@ -150,7 +150,6 @@ module HexaPDF
|
|
150
150
|
|
151
151
|
xref_section = XRefSection.new
|
152
152
|
xref_section.mark_as_initial_section! unless previous_xref_pos
|
153
|
-
xref_section.add_free_entry(0, 65535) if previous_xref_pos.nil?
|
154
153
|
rev.each do |obj|
|
155
154
|
if obj.null?
|
156
155
|
xref_section.add_free_entry(obj.oid, obj.gen)
|
data/lib/hexapdf/xref_section.rb
CHANGED
@@ -113,9 +113,10 @@ module HexaPDF
|
|
113
113
|
|
114
114
|
# Marks this XRefSection object as being the first cross-reference section in a PDF file.
|
115
115
|
#
|
116
|
-
# This has the consequence that only a single sub-section is created.
|
116
|
+
# This has the consequence that only a single sub-section starting a zero is created.
|
117
117
|
def mark_as_initial_section!
|
118
118
|
@initial_section = true
|
119
|
+
add_free_entry(0, 65535)
|
119
120
|
end
|
120
121
|
|
121
122
|
# Adds an in-use entry to the cross-reference section.
|
@@ -161,9 +162,10 @@ module HexaPDF
|
|
161
162
|
return to_enum(__method__) unless block_given?
|
162
163
|
|
163
164
|
temp = []
|
164
|
-
oids.sort
|
165
|
-
|
166
|
-
|
165
|
+
sorted_oids = oids.sort
|
166
|
+
expected_next_oid = sorted_oids[0]
|
167
|
+
sorted_oids.each do |oid|
|
168
|
+
if expected_next_oid != oid
|
167
169
|
if @initial_section
|
168
170
|
expected_next_oid.upto(oid - 1) do |free_oid|
|
169
171
|
temp << self.class.free_entry(free_oid, 0)
|
@@ -174,6 +176,7 @@ module HexaPDF
|
|
174
176
|
end
|
175
177
|
end
|
176
178
|
temp << self[oid]
|
179
|
+
expected_next_oid = oid + 1
|
177
180
|
end
|
178
181
|
yield(temp)
|
179
182
|
self
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
4
|
require 'hexapdf/content/graphics_state'
|
5
|
-
require 'ostruct'
|
6
5
|
|
7
6
|
# Dummy class used as wrapper so that constant lookup works correctly
|
8
7
|
class GraphicsStateWrapper < Minitest::Spec
|
@@ -149,8 +148,8 @@ class GraphicsStateWrapper < Minitest::Spec
|
|
149
148
|
end
|
150
149
|
|
151
150
|
it "uses the correct glyph to text space scaling" do
|
152
|
-
font =
|
153
|
-
font.glyph_scaling_factor
|
151
|
+
font = Object.new
|
152
|
+
font.define_singleton_method(:glyph_scaling_factor) { 0.002 }
|
154
153
|
@gs.font = font
|
155
154
|
@gs.font_size = 10
|
156
155
|
assert_equal(0.02, @gs.scaled_font_size)
|
@@ -4,7 +4,6 @@ require 'test_helper'
|
|
4
4
|
require 'hexapdf/content/operator'
|
5
5
|
require 'hexapdf/content/processor'
|
6
6
|
require 'hexapdf/serializer'
|
7
|
-
require 'ostruct'
|
8
7
|
|
9
8
|
describe HexaPDF::Content::Operator::BaseOperator do
|
10
9
|
before do
|
@@ -191,8 +190,8 @@ end
|
|
191
190
|
|
192
191
|
describe_operator :SetGraphicsStateParameters, :gs do
|
193
192
|
it "applies parameters from an ExtGState dictionary" do
|
194
|
-
font =
|
195
|
-
font.glyph_scaling_factor
|
193
|
+
font = Object.new
|
194
|
+
font.define_singleton_method(:glyph_scaling_factor) { 0.01 }
|
196
195
|
@processor.resources[:ExtGState] = {Name: {LW: 10, LC: 2, LJ: 2, ML: 2, D: [[3, 5], 2],
|
197
196
|
RI: 2, SA: true, BM: :Multiply, CA: 0.5, ca: 0.5,
|
198
197
|
AIS: true, TK: false, Font: [font, 10],
|
@@ -453,8 +452,8 @@ describe_operator :SetFontAndSize, :Tf do
|
|
453
452
|
self[:Font] && self[:Font][name]
|
454
453
|
end
|
455
454
|
|
456
|
-
font =
|
457
|
-
font.glyph_scaling_factor
|
455
|
+
font = Object.new
|
456
|
+
font.define_singleton_method(:glyph_scaling_factor) { 0.01 }
|
458
457
|
@processor.resources[:Font] = {F1: font}
|
459
458
|
invoke(:F1, 10)
|
460
459
|
assert_equal(@processor.resources.font(:F1), @processor.graphics_state.font)
|
@@ -4,17 +4,16 @@ require 'digest'
|
|
4
4
|
require 'test_helper'
|
5
5
|
require_relative 'common'
|
6
6
|
require 'hexapdf/digital_signature'
|
7
|
-
require 'ostruct'
|
8
7
|
|
9
8
|
describe HexaPDF::DigitalSignature::CMSHandler do
|
10
9
|
before do
|
11
|
-
@data = 'Some data'
|
12
|
-
@dict =
|
13
|
-
@pkcs7 = OpenSSL::PKCS7.sign(CERTIFICATES.signer_certificate, CERTIFICATES.signer_key,
|
14
|
-
|
15
|
-
|
16
|
-
@dict.contents =
|
17
|
-
@dict.signed_data =
|
10
|
+
@data = data = 'Some data'
|
11
|
+
@dict = Struct.new(:contents, :signed_data, :signature_type, :Reference, :M).new
|
12
|
+
@pkcs7 = pkcs7 = OpenSSL::PKCS7.sign(CERTIFICATES.signer_certificate, CERTIFICATES.signer_key,
|
13
|
+
@data, [CERTIFICATES.ca_certificate],
|
14
|
+
OpenSSL::PKCS7::DETACHED)
|
15
|
+
@dict.contents = pkcs7.to_der
|
16
|
+
@dict.signed_data = data
|
18
17
|
@handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
|
19
18
|
end
|
20
19
|
|
@@ -4,7 +4,6 @@ require 'test_helper'
|
|
4
4
|
require 'hexapdf/digital_signature'
|
5
5
|
require 'hexapdf/document'
|
6
6
|
require 'time'
|
7
|
-
require 'ostruct'
|
8
7
|
require 'openssl'
|
9
8
|
|
10
9
|
describe HexaPDF::DigitalSignature::Handler do
|
@@ -33,7 +32,7 @@ describe HexaPDF::DigitalSignature::Handler do
|
|
33
32
|
|
34
33
|
describe "store_verification_callback" do
|
35
34
|
before do
|
36
|
-
@context =
|
35
|
+
@context = Struct.new(:error).new
|
37
36
|
end
|
38
37
|
|
39
38
|
it "can allow self-signed certificates" do
|
@@ -60,7 +59,7 @@ describe HexaPDF::DigitalSignature::Handler do
|
|
60
59
|
].each do |success, not_before, not_after|
|
61
60
|
@result.messages.clear
|
62
61
|
@handler.define_singleton_method(:signer_certificate) do
|
63
|
-
|
62
|
+
Struct.new(:not_before, :not_after).new.tap do |struct|
|
64
63
|
struct.not_before = Time.parse("2021-11-14 #{not_before}")
|
65
64
|
struct.not_after = Time.parse("2021-11-14 #{not_after}")
|
66
65
|
end
|
@@ -3,12 +3,11 @@
|
|
3
3
|
require 'test_helper'
|
4
4
|
require_relative 'common'
|
5
5
|
require 'hexapdf/digital_signature'
|
6
|
-
require 'ostruct'
|
7
6
|
|
8
7
|
describe HexaPDF::DigitalSignature::PKCS1Handler do
|
9
8
|
before do
|
10
9
|
@data = 'Some data'
|
11
|
-
@dict =
|
10
|
+
@dict = Struct.new(:signed_data, :contents, :Cert, :Reference, :M).new
|
12
11
|
@dict.signed_data = @data
|
13
12
|
encoded_data = CERTIFICATES.signer_key.sign(OpenSSL::Digest.new('SHA1'), @data)
|
14
13
|
@dict.contents = OpenSSL::ASN1::OctetString.new(encoded_data).to_der
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
|
6
|
+
describe HexaPDF::Document::Annotations do
|
7
|
+
before do
|
8
|
+
@doc = HexaPDF::Document.new
|
9
|
+
@page = @doc.pages.add
|
10
|
+
@annots = @doc.annotations
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "create" do
|
14
|
+
it "fails if the type argument doesn't refer to an implemented method" do
|
15
|
+
assert_raises(ArgumentError) { @annots.create(:unknown, @page) }
|
16
|
+
end
|
17
|
+
|
18
|
+
it "delegates to the actual create_TYPE implementation" do
|
19
|
+
annot = @annots.create(:line, @page, start_point: [0, 0], end_point: [10, 10])
|
20
|
+
assert_equal(:Line, annot[:Subtype])
|
21
|
+
annot = @annots.create(:rectangle, @page, 10, 20, 30, 40)
|
22
|
+
assert_equal(:Square, annot[:Subtype])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "create_line" do
|
27
|
+
it "creates an appropriate line annotation object" do
|
28
|
+
annot = @annots.create(:line, @page, start_point: [0, 5], end_point: [10, 15])
|
29
|
+
assert_equal(:Annot, annot[:Type])
|
30
|
+
assert_equal(:Line, annot[:Subtype])
|
31
|
+
assert_equal([0, 5, 10, 15], annot.line)
|
32
|
+
assert_equal(annot, @page[:Annots].first)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "create_rectangle" do
|
37
|
+
it "creates an appropriate square annotation object" do
|
38
|
+
annot = @annots.create(:rectangle, @page, 10, 20, 30, 40)
|
39
|
+
assert_equal(:Annot, annot[:Type])
|
40
|
+
assert_equal(:Square, annot[:Subtype])
|
41
|
+
assert_equal([10, 20, 40, 60], annot[:Rect])
|
42
|
+
assert_equal(annot, @page[:Annots].first)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "create_ellipse" do
|
47
|
+
it "creates an appropriate circle annotation object" do
|
48
|
+
annot = @annots.create(:ellipse, @page, 100, 100, a: 30, b: 40)
|
49
|
+
assert_equal(:Annot, annot[:Type])
|
50
|
+
assert_equal(:Circle, annot[:Subtype])
|
51
|
+
assert_equal([70, 60, 130, 140], annot[:Rect])
|
52
|
+
assert_equal(annot, @page[:Annots].first)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -146,6 +146,16 @@ describe HexaPDF::Document::Layout do
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
+
describe "style?" do
|
150
|
+
it "returns true if a given style is defined" do
|
151
|
+
assert(@layout.style?(:base))
|
152
|
+
end
|
153
|
+
|
154
|
+
it "returns false if a given style is not defined" do
|
155
|
+
refute(@layout.style?(:unknown))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
149
159
|
describe "styles" do
|
150
160
|
it "returns the existing styles" do
|
151
161
|
@layout.style(:test, font_size: 20)
|
@@ -168,6 +178,16 @@ describe HexaPDF::Document::Layout do
|
|
168
178
|
assert_kind_of(HexaPDF::Font::Type1Wrapper, style.font)
|
169
179
|
end
|
170
180
|
|
181
|
+
it "uses the font_bold property when resolving a font name to a font wrapper" do
|
182
|
+
style = @layout.send(:retrieve_style, {font: 'Helvetica', font_bold: true})
|
183
|
+
assert_equal('Helvetica-Bold', style.font.wrapped_font.font_name)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "uses the font_italic property when resolving a font name to a font wrapper" do
|
187
|
+
style = @layout.send(:retrieve_style, {font: 'Helvetica', font_italic: true})
|
188
|
+
assert_equal('Helvetica-Oblique', style.font.wrapped_font.font_name)
|
189
|
+
end
|
190
|
+
|
171
191
|
it "sets the :base style's font if no font is set" do
|
172
192
|
@layout.style(:base, font: 'Helvetica')
|
173
193
|
style = @layout.send(:retrieve_style, {})
|
@@ -203,7 +223,8 @@ describe HexaPDF::Document::Layout do
|
|
203
223
|
|
204
224
|
describe "box" do
|
205
225
|
it "creates the request box" do
|
206
|
-
box = @layout.box(:column, columns: 3,
|
226
|
+
box = @layout.box(:column, columns: 3, width: 15, height: 30,
|
227
|
+
style: {font_size: 10, box_options: {gaps: 20}},
|
207
228
|
properties: {key: :value})
|
208
229
|
assert_equal(15, box.width)
|
209
230
|
assert_equal(30, box.height)
|
@@ -431,9 +452,10 @@ describe HexaPDF::Document::Layout do
|
|
431
452
|
|
432
453
|
describe "table_box" do
|
433
454
|
it "creates a table box" do
|
434
|
-
box = @layout.table_box([['m']],
|
455
|
+
box = @layout.table_box([['m']], header: proc { [['a']] },
|
435
456
|
footer: proc { [['b']] }, cell_style: {background_color: "red"},
|
436
457
|
width: 100, height: 300, style: {background_color: "blue"},
|
458
|
+
box_options: {column_widths: [100]},
|
437
459
|
properties: {key: :value}, border: {width: 1})
|
438
460
|
assert_equal(100, box.width)
|
439
461
|
assert_equal(300, box.height)
|
@@ -209,6 +209,13 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
209
209
|
dict[:Encoding].stream)
|
210
210
|
assert_equal([glyph.id, [glyph.width]], dict[:DescendantFonts][0][:W].value)
|
211
211
|
end
|
212
|
+
|
213
|
+
it "handles the case where the font is added but then not used and deleted" do
|
214
|
+
@doc.task(:optimize, compact: true)
|
215
|
+
assert(@font_wrapper.pdf_object.null?)
|
216
|
+
@doc.dispatch_message(:complete_objects)
|
217
|
+
assert(@font_wrapper.pdf_object.null?)
|
218
|
+
end
|
212
219
|
end
|
213
220
|
|
214
221
|
describe "font file embedding" do
|
@@ -140,5 +140,12 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
140
140
|
it "makes sure that the PDF dictionaries are indirect" do
|
141
141
|
assert(@times_wrapper.pdf_object.indirect?)
|
142
142
|
end
|
143
|
+
|
144
|
+
it "handles the case where the font is added but then not used and deleted" do
|
145
|
+
@doc.task(:optimize, compact: true)
|
146
|
+
assert(@times_wrapper.pdf_object.null?)
|
147
|
+
@doc.dispatch_message(:complete_objects)
|
148
|
+
assert(@times_wrapper.pdf_object.null?)
|
149
|
+
end
|
143
150
|
end
|
144
151
|
end
|
@@ -27,6 +27,16 @@ describe HexaPDF::Font::TrueType::Subsetter do
|
|
27
27
|
assert_equal(value, @subsetter.subset_glyph_id(5))
|
28
28
|
end
|
29
29
|
|
30
|
+
it "doesn't use certain subset glyph IDs for performance reasons" do
|
31
|
+
1.upto(93) {|i| @subsetter.use_glyph(i) }
|
32
|
+
# glyph 0, 93 used glyph, 4 special glyphs
|
33
|
+
assert_equal(1 + 93 + 4, @subsetter.instance_variable_get(:@glyph_map).size)
|
34
|
+
1.upto(12) {|i| assert_equal(i, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
35
|
+
13.upto(38) {|i| assert_equal(i + 1, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
36
|
+
39.upto(88) {|i| assert_equal(i + 3, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
37
|
+
89.upto(93) {|i| assert_equal(i + 4, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
38
|
+
end
|
39
|
+
|
30
40
|
it "creates the subset font file" do
|
31
41
|
gid = @font[:cmap].preferred_table[0x41]
|
32
42
|
@subsetter.use_glyph(gid)
|
@@ -738,6 +738,30 @@ describe HexaPDF::Layout::Style do
|
|
738
738
|
end
|
739
739
|
end
|
740
740
|
|
741
|
+
describe "each_property" do
|
742
|
+
it "yields all set properties with their values" do
|
743
|
+
@style.font_size = 5
|
744
|
+
@style.line_spacing = 1.2
|
745
|
+
assert_equal(0.005, @style.scaled_font_size)
|
746
|
+
assert_equal([[:font, @style.font], [:font_size, 5], [:horizontal_scaling, 100],
|
747
|
+
[:line_spacing, @style.line_spacing], [:subscript, false], [:superscript, false]],
|
748
|
+
@style.each_property.to_a.sort)
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
describe "merge" do
|
753
|
+
it "merges all set properties" do
|
754
|
+
@style.font_size = 5
|
755
|
+
@style.line_spacing = 1.2
|
756
|
+
new_style = HexaPDF::Layout::Style.new
|
757
|
+
new_style.update(font_size: 3, line_spacing: {type: :fixed, value: 2.5})
|
758
|
+
new_style.merge(@style)
|
759
|
+
assert_equal(5, new_style.font_size)
|
760
|
+
assert_equal(:proportional, new_style.line_spacing.type)
|
761
|
+
assert_equal(1.2, new_style.line_spacing.value)
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
741
765
|
it "has several simple and dynamically generated properties with default values" do
|
742
766
|
@style = HexaPDF::Layout::Style.new
|
743
767
|
assert_raises(HexaPDF::Error) { @style.font }
|
@@ -780,6 +804,7 @@ describe HexaPDF::Layout::Style do
|
|
780
804
|
assert_equal(:left, @style.align)
|
781
805
|
assert_equal(:top, @style.valign)
|
782
806
|
assert_equal(:default, @style.mask_mode)
|
807
|
+
assert_equal({}, @style.box_options)
|
783
808
|
end
|
784
809
|
|
785
810
|
it "allows using a non-standard setter for generated properties" do
|
@@ -790,8 +815,8 @@ describe HexaPDF::Layout::Style do
|
|
790
815
|
@style.stroke_dash_pattern(5, 2)
|
791
816
|
assert_equal([[5], 2], @style.stroke_dash_pattern.to_operands)
|
792
817
|
|
793
|
-
@style.line_spacing(
|
794
|
-
assert_equal([:proportional,
|
818
|
+
@style.line_spacing(HexaPDF::Layout::Style::LineSpacing.new(type: :double))
|
819
|
+
assert_equal([:proportional, 2], [@style.line_spacing.type, @style.line_spacing.value])
|
795
820
|
end
|
796
821
|
|
797
822
|
it "allows checking for valid values" do
|
@@ -99,7 +99,7 @@ describe HexaPDF::Task::Optimize do
|
|
99
99
|
objstm = @doc.add({}, type: HexaPDF::Type::ObjectStream)
|
100
100
|
@doc.add({}, type: HexaPDF::Type::XRefStream)
|
101
101
|
objstm.add_object(@doc.add({Type: :Test}))
|
102
|
-
@doc.write(io)
|
102
|
+
@doc.write(io, compact: false)
|
103
103
|
io.rewind
|
104
104
|
@doc = HexaPDF::Document.new(io: io)
|
105
105
|
end
|