hexapdf 0.32.2 → 0.33.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 +63 -1
- data/README.md +9 -0
- data/examples/002-graphics.rb +15 -17
- data/examples/003-arcs.rb +9 -9
- data/examples/009-text_layouter_alignment.rb +1 -1
- data/examples/010-text_layouter_inline_boxes.rb +2 -2
- data/examples/011-text_layouter_line_wrapping.rb +1 -1
- data/examples/012-text_layouter_styling.rb +7 -7
- data/examples/013-text_layouter_shapes.rb +1 -1
- data/examples/014-text_in_polygon.rb +1 -1
- data/examples/015-boxes.rb +8 -7
- data/examples/016-frame_automatic_box_placement.rb +2 -2
- data/examples/017-frame_text_flow.rb +2 -1
- data/examples/018-composer.rb +1 -1
- data/examples/020-column_box.rb +2 -1
- data/examples/025-table_box.rb +46 -0
- data/lib/hexapdf/cli/command.rb +5 -2
- data/lib/hexapdf/cli/form.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +3 -3
- data/lib/hexapdf/composer.rb +104 -52
- data/lib/hexapdf/configuration.rb +44 -39
- data/lib/hexapdf/content/canvas.rb +393 -267
- data/lib/hexapdf/content/color_space.rb +72 -25
- data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
- data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
- data/lib/hexapdf/content/graphic_object.rb +6 -7
- data/lib/hexapdf/content/graphics_state.rb +54 -45
- data/lib/hexapdf/content/operator.rb +52 -54
- data/lib/hexapdf/content/parser.rb +2 -2
- data/lib/hexapdf/content/processor.rb +15 -15
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +5 -0
- data/lib/hexapdf/dictionary.rb +6 -5
- data/lib/hexapdf/dictionary_fields.rb +42 -14
- data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
- data/lib/hexapdf/digital_signature/handler.rb +1 -1
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
- data/lib/hexapdf/digital_signature/signature.rb +6 -6
- data/lib/hexapdf/digital_signature/signatures.rb +13 -12
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
- data/lib/hexapdf/digital_signature/signing.rb +4 -0
- data/lib/hexapdf/digital_signature/verification_result.rb +2 -2
- data/lib/hexapdf/digital_signature.rb +7 -2
- data/lib/hexapdf/document/destinations.rb +12 -11
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/layout.rb +167 -39
- data/lib/hexapdf/document/pages.rb +3 -2
- data/lib/hexapdf/document.rb +89 -55
- data/lib/hexapdf/encryption/aes.rb +5 -5
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +2 -2
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +31 -24
- data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
- data/lib/hexapdf/encryption.rb +7 -2
- data/lib/hexapdf/error.rb +18 -0
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +55 -6
- data/lib/hexapdf/font/cmap/parser.rb +2 -2
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +3 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
- data/lib/hexapdf/font/type1_wrapper.rb +19 -4
- data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
- data/lib/hexapdf/font_loader/from_file.rb +5 -5
- data/lib/hexapdf/font_loader/standard14.rb +3 -3
- data/lib/hexapdf/font_loader.rb +3 -0
- data/lib/hexapdf/image_loader/jpeg.rb +2 -2
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +13 -0
- data/lib/hexapdf/layout/box.rb +9 -2
- data/lib/hexapdf/layout/box_fitter.rb +2 -2
- data/lib/hexapdf/layout/column_box.rb +18 -4
- data/lib/hexapdf/layout/frame.rb +30 -12
- data/lib/hexapdf/layout/image_box.rb +5 -0
- data/lib/hexapdf/layout/inline_box.rb +1 -0
- data/lib/hexapdf/layout/list_box.rb +17 -1
- data/lib/hexapdf/layout/page_style.rb +4 -4
- data/lib/hexapdf/layout/style.rb +18 -3
- data/lib/hexapdf/layout/table_box.rb +682 -0
- data/lib/hexapdf/layout/text_box.rb +5 -3
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +12 -4
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +18 -7
- data/lib/hexapdf/parser.rb +7 -7
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +3 -3
- data/lib/hexapdf/serializer.rb +15 -15
- data/lib/hexapdf/stream.rb +4 -2
- data/lib/hexapdf/tokenizer.rb +14 -14
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +2 -2
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
- data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +3 -3
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +3 -3
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +3 -3
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +1 -1
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +1 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/mark_information.rb +1 -1
- data/lib/hexapdf/type/names.rb +2 -2
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +19 -10
- data/lib/hexapdf/type/page_label.rb +1 -1
- data/lib/hexapdf/type/page_tree_node.rb +1 -1
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +2 -2
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +2 -2
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +4 -4
- data/lib/hexapdf/xref_section.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
- data/test/hexapdf/content/test_canvas.rb +0 -1
- data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
- data/test/hexapdf/document/test_files.rb +2 -2
- data/test/hexapdf/document/test_layout.rb +98 -0
- data/test/hexapdf/encryption/test_security_handler.rb +12 -11
- data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
- data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
- data/test/hexapdf/layout/test_box.rb +1 -1
- data/test/hexapdf/layout/test_column_box.rb +65 -21
- data/test/hexapdf/layout/test_frame.rb +14 -14
- data/test/hexapdf/layout/test_image_box.rb +4 -0
- data/test/hexapdf/layout/test_inline_box.rb +5 -0
- data/test/hexapdf/layout/test_list_box.rb +40 -6
- data/test/hexapdf/layout/test_page_style.rb +3 -2
- data/test/hexapdf/layout/test_style.rb +50 -0
- data/test/hexapdf/layout/test_table_box.rb +722 -0
- data/test/hexapdf/layout/test_text_box.rb +18 -0
- data/test/hexapdf/layout/test_text_layouter.rb +4 -0
- data/test/hexapdf/test_dictionary_fields.rb +4 -1
- data/test/hexapdf/test_document.rb +1 -0
- data/test/hexapdf/test_filter.rb +8 -0
- data/test/hexapdf/test_importer.rb +9 -0
- data/test/hexapdf/test_object.rb +16 -5
- data/test/hexapdf/test_stream.rb +7 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
- data/test/hexapdf/type/acro_form/test_form.rb +4 -3
- data/test/hexapdf/type/test_page.rb +18 -4
- metadata +17 -8
|
@@ -65,7 +65,7 @@ module HexaPDF
|
|
|
65
65
|
# cross-reference section and trailer information and then written. Otherwise a normal
|
|
66
66
|
# cross-reference section plus trailer are written.
|
|
67
67
|
#
|
|
68
|
-
# See:
|
|
68
|
+
# See: PDF2.0 s7.5.8
|
|
69
69
|
class XRefStream < HexaPDF::Stream
|
|
70
70
|
|
|
71
71
|
define_type :XRef
|
|
@@ -161,7 +161,7 @@ module HexaPDF
|
|
|
161
161
|
when TYPE_COMPRESSED
|
|
162
162
|
xref.add_compressed_entry(oid, field2, field3)
|
|
163
163
|
else
|
|
164
|
-
nil # Ignore entry as per
|
|
164
|
+
nil # Ignore entry as per PDF2.0 s7.5.8.3
|
|
165
165
|
end
|
|
166
166
|
start_pos = end_pos
|
|
167
167
|
end
|
|
@@ -49,7 +49,7 @@ module HexaPDF
|
|
|
49
49
|
# When a PDF file is written, text strings are automatically encoded in either PDFDocEncoding
|
|
50
50
|
# or UTF-16BE depending on the characters in the text string.
|
|
51
51
|
#
|
|
52
|
-
# See:
|
|
52
|
+
# See: PDF2.0 s7.9.2, D.1, D.3
|
|
53
53
|
module PDFDocEncoding
|
|
54
54
|
|
|
55
55
|
# :nodoc:
|
data/lib/hexapdf/version.rb
CHANGED
data/lib/hexapdf/writer.rb
CHANGED
|
@@ -124,7 +124,7 @@ module HexaPDF
|
|
|
124
124
|
|
|
125
125
|
# Writes the PDF file header.
|
|
126
126
|
#
|
|
127
|
-
# See:
|
|
127
|
+
# See: PDF2.0 s7.5.2
|
|
128
128
|
def write_file_header
|
|
129
129
|
@io << "%PDF-#{@document.version}\n%\xCF\xEC\xFF\xE8\xD7\xCB\xCD\n"
|
|
130
130
|
end
|
|
@@ -225,7 +225,7 @@ module HexaPDF
|
|
|
225
225
|
|
|
226
226
|
# Writes the cross-reference section.
|
|
227
227
|
#
|
|
228
|
-
# See:
|
|
228
|
+
# See: PDF2.0 s7.5.4
|
|
229
229
|
def write_xref_section(xref_section)
|
|
230
230
|
@io << "xref\n"
|
|
231
231
|
xref_section.each_subsection do |entries|
|
|
@@ -245,14 +245,14 @@ module HexaPDF
|
|
|
245
245
|
|
|
246
246
|
# Writes the trailer dictionary.
|
|
247
247
|
#
|
|
248
|
-
# See:
|
|
248
|
+
# See: PDF2.0 s7.5.5
|
|
249
249
|
def write_trailer(trailer)
|
|
250
250
|
@io << "trailer\n#{@serializer.serialize(trailer)}\n"
|
|
251
251
|
end
|
|
252
252
|
|
|
253
253
|
# Writes the startxref line needed for cross-reference sections and cross-reference streams.
|
|
254
254
|
#
|
|
255
|
-
# See:
|
|
255
|
+
# See: PDF2.0 s7.5.5, s7.5.8
|
|
256
256
|
def write_startxref(startxref)
|
|
257
257
|
@io << "startxref\n#{startxref}\n%%EOF\n"
|
|
258
258
|
end
|
data/lib/hexapdf/xref_section.rb
CHANGED
|
@@ -46,7 +46,7 @@ module HexaPDF
|
|
|
46
46
|
#
|
|
47
47
|
# Note that a cross-reference section may contain a single object number only once.
|
|
48
48
|
#
|
|
49
|
-
# See: HexaPDF::Revision,
|
|
49
|
+
# See: HexaPDF::Revision, PDF2.0 s7.5.4, s7.5.8
|
|
50
50
|
class XRefSection < Utils::ObjectHash
|
|
51
51
|
|
|
52
52
|
# One entry of a cross-reference section or stream.
|
|
@@ -69,7 +69,7 @@ module HexaPDF
|
|
|
69
69
|
#
|
|
70
70
|
# Objects in an object stream always have a generation number of 0!
|
|
71
71
|
#
|
|
72
|
-
# See:
|
|
72
|
+
# See: PDF2.0 s7.5.4, s7.5.8
|
|
73
73
|
Entry = Struct.new(:type, :oid, :gen, :pos, :objstm) do
|
|
74
74
|
def free?
|
|
75
75
|
type == :free
|
|
@@ -16,13 +16,15 @@ describe HexaPDF::Content::GraphicObject::EndpointArc do
|
|
|
16
16
|
assert_equal(0, arc.inclination)
|
|
17
17
|
assert(arc.large_arc)
|
|
18
18
|
refute(arc.clockwise)
|
|
19
|
+
assert_nil(arc.max_curves)
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
describe "configure" do
|
|
23
24
|
it "changes the values" do
|
|
24
25
|
arc = HexaPDF::Content::GraphicObject::EndpointArc.new
|
|
25
|
-
arc.configure(x: 1, y: 2, a: 3, b: 4, inclination: 5, large_arc: false, clockwise: true
|
|
26
|
+
arc.configure(x: 1, y: 2, a: 3, b: 4, inclination: 5, large_arc: false, clockwise: true,
|
|
27
|
+
max_curves: 8)
|
|
26
28
|
assert_equal(1, arc.x)
|
|
27
29
|
assert_equal(2, arc.y)
|
|
28
30
|
assert_equal(3, arc.a)
|
|
@@ -30,6 +32,7 @@ describe HexaPDF::Content::GraphicObject::EndpointArc do
|
|
|
30
32
|
assert_equal(5, arc.inclination)
|
|
31
33
|
refute(arc.large_arc)
|
|
32
34
|
assert(arc.clockwise)
|
|
35
|
+
assert_equal(8, arc.max_curves)
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
@@ -94,5 +97,12 @@ describe HexaPDF::Content::GraphicObject::EndpointArc do
|
|
|
94
97
|
clockwise: false)
|
|
95
98
|
assert_equal(arc_data, @page.contents)
|
|
96
99
|
end
|
|
100
|
+
|
|
101
|
+
it "assigns the max curves to the generated arc" do
|
|
102
|
+
arc = HexaPDF::Content::GraphicObject::EndpointArc.new
|
|
103
|
+
arc.configure(a: 1, b: 1, x: -1, y: 0, max_curves: 10)
|
|
104
|
+
hash = arc.send(:compute_arc_values, 1, 0)
|
|
105
|
+
assert_equal(10, hash[:max_curves])
|
|
106
|
+
end
|
|
97
107
|
end
|
|
98
108
|
end
|
|
@@ -51,6 +51,13 @@ describe HexaPDF::Content::GraphicObject::Geom2D do
|
|
|
51
51
|
[[:move_to, [5, 6]], [:line_to, [10, 11]], [:stroke_path]])
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
it "draws a Geom2D::Rectangle onto the canvas" do
|
|
55
|
+
@obj.object = Geom2D::Rectangle(5, 6, 20, 50)
|
|
56
|
+
@obj.draw(@canvas)
|
|
57
|
+
assert_operators(@canvas.contents,
|
|
58
|
+
[[:append_rectangle, [5, 6, 20, 50]], [:stroke_path]])
|
|
59
|
+
end
|
|
60
|
+
|
|
54
61
|
it "draws a Geom2D::Polygon onto the canvas" do
|
|
55
62
|
@obj.object = Geom2D::Polygon([5, 6], [10, 11], [7, 9])
|
|
56
63
|
@obj.draw(@canvas)
|
|
@@ -1193,7 +1193,6 @@ describe HexaPDF::Content::Canvas do
|
|
|
1193
1193
|
assert_operators(@canvas.contents, [[:set_font_and_size, [:F1, 10]],
|
|
1194
1194
|
[:begin_text],
|
|
1195
1195
|
[:set_text_matrix, [1, 0, 0, 1, 100, 100]],
|
|
1196
|
-
[:set_leading, [10]],
|
|
1197
1196
|
[:show_text_with_positioning, [["Hallo"]]]])
|
|
1198
1197
|
end
|
|
1199
1198
|
|
|
@@ -107,6 +107,28 @@ describe HexaPDF::DigitalSignature::Signatures do
|
|
|
107
107
|
assert_equal(1, field.each_widget.count)
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
it "creates an empty widget on the first page for the signature field if necessary" do
|
|
111
|
+
@doc.pages.add
|
|
112
|
+
field = @doc.acro_form(create: true).create_signature_field('Signature2')
|
|
113
|
+
field.field_value = sig = @doc.add({Type: :Sig, key: :value})
|
|
114
|
+
@doc.signatures.add(@io, @handler, signature: sig)
|
|
115
|
+
widgets = field.each_widget.to_a
|
|
116
|
+
assert_equal(1, widgets.size)
|
|
117
|
+
assert_equal(@doc.pages[0], widgets[0][:P])
|
|
118
|
+
assert_equal([0, 0, 0, 0], widgets[0][:Rect])
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "handles a bug in Adobe Acrobat related to images not showing without a /Resources entry" do
|
|
122
|
+
field = @doc.acro_form(create: true).create_signature_field('Signature')
|
|
123
|
+
image = @doc.add({Type: :XObject, Subtype: :Image, Width: 1, Height: 1, ColorSpace: :DeviceGray,
|
|
124
|
+
BitsPerComponent: 8}, stream: 'A')
|
|
125
|
+
field.create_widget(@doc.pages[0], Rect: [0, 0, 100, 100]).create_appearance.
|
|
126
|
+
canvas.xobject(image, at: [0, 0])
|
|
127
|
+
@doc.signatures.add(@io, @handler, signature: field)
|
|
128
|
+
assert(image.key?(:Resources))
|
|
129
|
+
assert_equal({}, image[:Resources])
|
|
130
|
+
end
|
|
131
|
+
|
|
110
132
|
it "handles different xref section types correctly when determing the offsets" do
|
|
111
133
|
@doc.delete(7)
|
|
112
134
|
sig = @doc.signatures.add(@io, @handler, write_options: {update_fields: false})
|
|
@@ -55,8 +55,8 @@ describe HexaPDF::Document::Files do
|
|
|
55
55
|
spec2 = @doc.add({Type: :Filespec})
|
|
56
56
|
@doc.pages.add # page without annot
|
|
57
57
|
@doc.pages.add[:Annots] = [
|
|
58
|
-
{Subtype: :FileAttachment, FS: HexaPDF::Reference.new(spec1.oid, spec1.gen)},
|
|
59
|
-
{Subtype: :FileAttachment, FS: spec2},
|
|
58
|
+
{Subtype: :FileAttachment, Rect: [0, 0, 0, 0], FS: HexaPDF::Reference.new(spec1.oid, spec1.gen)},
|
|
59
|
+
{Subtype: :FileAttachment, Rect: [0, 0, 0, 0], FS: spec2},
|
|
60
60
|
{},
|
|
61
61
|
]
|
|
62
62
|
assert_equal([spec1, spec2], @doc.files.to_a)
|
|
@@ -59,6 +59,54 @@ describe HexaPDF::Document::Layout::ChildrenCollector do
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
describe HexaPDF::Document::Layout::CellArgumentCollector do
|
|
63
|
+
before do
|
|
64
|
+
@args = HexaPDF::Document::Layout::CellArgumentCollector.new(20, 10)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "[]" do
|
|
68
|
+
def check_argument_info(info, rows, cols, args)
|
|
69
|
+
assert_equal(rows, info.rows)
|
|
70
|
+
assert_equal(cols, info.cols)
|
|
71
|
+
assert_equal(args, info.args)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "allows assigning to all cells" do
|
|
75
|
+
@args[] = {key: :value}
|
|
76
|
+
check_argument_info(@args.argument_infos.first, 0..19, 0..9, {key: :value})
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "allows assigning to all columns of a range of rows" do
|
|
80
|
+
@args[1..4] = {key: :value}
|
|
81
|
+
check_argument_info(@args.argument_infos.first, 1..4, 0..9, {key: :value})
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "allows assigning to the intersection of a range of rows with a range of columns" do
|
|
85
|
+
@args[1..4, 3..5] = {key: :value}
|
|
86
|
+
check_argument_info(@args.argument_infos.first, 1..4, 3..5, {key: :value})
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "allows selecting a single row or column" do
|
|
90
|
+
@args[1, 3] = {key: :value}
|
|
91
|
+
check_argument_info(@args.argument_infos.first, 1..1, 3..3, {key: :value})
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "allows using negative indices" do
|
|
95
|
+
@args[-3..-1, -5..-2] = {key: :value}
|
|
96
|
+
check_argument_info(@args.argument_infos.first, 17..19, 5..8, {key: :value})
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "retrieve_arguments_for" do
|
|
101
|
+
it "merges all argument hashes, with later defined ones overridding prior ones" do
|
|
102
|
+
@args[] = {key: :value, a: :b}
|
|
103
|
+
@args[3..7] = {a: :c}
|
|
104
|
+
@args[5, 6] = {e: :f}
|
|
105
|
+
assert_equal({key: :value, a: :c, e: :f}, @args.retrieve_arguments_for(5, 6))
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
62
110
|
describe HexaPDF::Document::Layout do
|
|
63
111
|
before do
|
|
64
112
|
@doc = HexaPDF::Document.new
|
|
@@ -290,6 +338,56 @@ describe HexaPDF::Document::Layout do
|
|
|
290
338
|
end
|
|
291
339
|
end
|
|
292
340
|
|
|
341
|
+
describe "table_box" do
|
|
342
|
+
it "creates a table box" do
|
|
343
|
+
box = @layout.table_box([['m']], column_widths: [100], header: proc { [['a']] },
|
|
344
|
+
footer: proc { [['b']] }, cell_style: {background_color: "red"},
|
|
345
|
+
width: 100, height: 300, style: {background_color: "blue"},
|
|
346
|
+
properties: {key: :value}, border: {width: 1})
|
|
347
|
+
assert_equal(100, box.width)
|
|
348
|
+
assert_equal(300, box.height)
|
|
349
|
+
assert_equal("blue", box.style.background_color)
|
|
350
|
+
assert_equal(1, box.style.border.width.left)
|
|
351
|
+
assert_equal({key: :value}, box.properties)
|
|
352
|
+
assert_equal(HexaPDF::Layout::TextBox, box.cells[0, 0].children.class)
|
|
353
|
+
assert_equal([100], box.column_widths)
|
|
354
|
+
assert_equal('a', box.header_cells[0, 0].children)
|
|
355
|
+
assert_equal('b', box.footer_cells[0, 0].children)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "doesn't modify the children of cells if they are already in the correct form" do
|
|
359
|
+
image_path = File.join(TEST_DATA_DIR, 'images', 'gray.jpg')
|
|
360
|
+
cell0 = @layout.text('a')
|
|
361
|
+
cell1 = [@layout.text('b'), @layout.image(image_path)]
|
|
362
|
+
box = @layout.table_box([[cell0, cell1]])
|
|
363
|
+
assert_same(cell0, box.cells[0, 0].children)
|
|
364
|
+
assert_same(cell1, box.cells[0, 1].children)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
it "converts cells containing other than Box and Array instances" do
|
|
368
|
+
box = @layout.table_box([['a', 5]])
|
|
369
|
+
assert_kind_of(HexaPDF::Layout::TextBox, box.cells[0, 0].children)
|
|
370
|
+
assert_kind_of(HexaPDF::Layout::TextBox, box.cells[0, 1].children)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
it "allows customizing the creation arguments" do
|
|
374
|
+
box = @layout.table_box([['a']]) do |args|
|
|
375
|
+
args[] = {font_size: 20}
|
|
376
|
+
end
|
|
377
|
+
assert_equal(20, box.cells[0, 0].children.style.font_size)
|
|
378
|
+
refute_equal(20, box.cells[0, 0].style.font_size)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
it "allows styling table cells themselves" do
|
|
382
|
+
box = @layout.table_box([['a', @layout.text('b')]]) do |args|
|
|
383
|
+
args[] = {cell: {background_color: "green"}}
|
|
384
|
+
end
|
|
385
|
+
assert_equal('green', box.cells[0, 0].style.background_color)
|
|
386
|
+
assert_nil(box.cells[0, 0].children.style.background_color)
|
|
387
|
+
assert_equal('green', box.cells[0, 1].style.background_color)
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
293
391
|
describe "lorem_ipsum_box" do
|
|
294
392
|
it "creates a standard lorem ipsum box" do
|
|
295
393
|
box = @layout.lorem_ipsum_box(width: 10, height: 15, font_size: 15)
|
|
@@ -17,13 +17,6 @@ describe HexaPDF::Encryption::EncryptionDictionary do
|
|
|
17
17
|
assert(@dict.must_be_indirect?)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
it "validates the /V value" do
|
|
21
|
-
@dict[:V] = 1
|
|
22
|
-
assert(@dict.validate)
|
|
23
|
-
@dict[:V] = 3
|
|
24
|
-
refute(@dict.validate)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
20
|
it "validates the /Length field when /V=2" do
|
|
28
21
|
@dict[:V] = 2
|
|
29
22
|
refute(@dict.validate)
|
|
@@ -240,6 +233,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
|
240
233
|
[:identity, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :None}}}],
|
|
241
234
|
[:identity, 128, {V: 4, CF: {Mine: {CFM: :AESV2}}}],
|
|
242
235
|
].each do |alg, length, dict|
|
|
236
|
+
dict[:Filter] = :Test
|
|
243
237
|
@enc.strf = alg
|
|
244
238
|
@enc.set_up_encryption(key_length: length, algorithm: (alg == :identity ? :aes : alg))
|
|
245
239
|
@obj[:X] = @enc.encrypt_string('data', @obj)
|
|
@@ -249,7 +243,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
|
249
243
|
end
|
|
250
244
|
|
|
251
245
|
it "selects the correct algorithm for string, stream and embedded file decryption" do
|
|
252
|
-
@handler.set_up_decryption({V: 4, StrF: :Mine, StmF: :Mine, EFF: :Mine,
|
|
246
|
+
@handler.set_up_decryption({Filter: :Test, V: 4, StrF: :Mine, StmF: :Mine, EFF: :Mine,
|
|
253
247
|
CF: {Mine: {CFM: :V2}}})
|
|
254
248
|
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:embedded_file_algorithm))
|
|
255
249
|
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:string_algorithm))
|
|
@@ -263,16 +257,23 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
|
263
257
|
@handler.encryption_details)
|
|
264
258
|
end
|
|
265
259
|
|
|
260
|
+
it "fails if the encryption dictionary is not valid" do
|
|
261
|
+
exp = assert_raises(HexaPDF::Error) do
|
|
262
|
+
@handler.set_up_decryption({V: 5})
|
|
263
|
+
end
|
|
264
|
+
assert_match(/Validation error for encryption dictionary.*Required field Filter/i, exp.message)
|
|
265
|
+
end
|
|
266
|
+
|
|
266
267
|
it "fails for unsupported /V values in the dict" do
|
|
267
268
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
268
|
-
@handler.set_up_decryption({V: 3})
|
|
269
|
+
@handler.set_up_decryption({Filter: :Text, V: 3})
|
|
269
270
|
end
|
|
270
271
|
assert_match(/Unsupported encryption version/i, exp.message)
|
|
271
272
|
end
|
|
272
273
|
|
|
273
274
|
it "fails for unsupported crypt filter encryption methods" do
|
|
274
275
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
275
|
-
@handler.set_up_decryption({V: 4, StrF: :Mine, CF: {Mine: {CFM: :Unknown}}})
|
|
276
|
+
@handler.set_up_decryption({Filter: :Test, V: 4, StrF: :Mine, CF: {Mine: {CFM: :Unknown}}})
|
|
276
277
|
end
|
|
277
278
|
assert_match(/Unsupported encryption method/i, exp.message)
|
|
278
279
|
end
|
|
@@ -280,7 +281,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
|
280
281
|
|
|
281
282
|
describe "decrypt" do
|
|
282
283
|
before do
|
|
283
|
-
@handler.set_up_decryption({V: 1})
|
|
284
|
+
@handler.set_up_decryption({Filter: :Test, V: 1})
|
|
284
285
|
@encrypted = @handler.encrypt_string('string', @obj)
|
|
285
286
|
@obj.value = {Key: @encrypted.dup, Array: [@encrypted.dup], Hash: {Another: @encrypted.dup}}
|
|
286
287
|
end
|
|
@@ -21,13 +21,6 @@ describe HexaPDF::Encryption::StandardEncryptionDictionary do
|
|
|
21
21
|
@dict[:Perms] = 'test' * 8
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
it "validates the /R value" do
|
|
25
|
-
@dict[:R] = 2
|
|
26
|
-
assert(@dict.validate)
|
|
27
|
-
@dict[:R] = 5
|
|
28
|
-
refute(@dict.validate)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
24
|
[:U, :O].each do |field|
|
|
32
25
|
it "validates the length of /#{field} field for R <= 4" do
|
|
33
26
|
@dict[field] = 'test'
|
|
@@ -35,19 +28,36 @@ describe HexaPDF::Encryption::StandardEncryptionDictionary do
|
|
|
35
28
|
end
|
|
36
29
|
end
|
|
37
30
|
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
describe "validation for R=6" do
|
|
32
|
+
before do
|
|
40
33
|
@dict[:R] = 6
|
|
41
|
-
@dict[
|
|
42
|
-
|
|
34
|
+
@dict[:U] = 't' * 48
|
|
35
|
+
@dict[:O] = 't' * 48
|
|
36
|
+
@dict[:UE] = 't' * 32
|
|
37
|
+
@dict[:OE] = 't' * 32
|
|
38
|
+
@dict[:Perms] = 't' * 16
|
|
43
39
|
end
|
|
44
|
-
end
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
[:U, :O, :UE, :OE, :Perms].each do |field|
|
|
42
|
+
it "validates the length of /#{field}" do
|
|
43
|
+
@dict[field] = 'test'
|
|
44
|
+
refute(@dict.validate)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
[:U, :O].each do |field|
|
|
49
|
+
it "auto-corrects /#{field} if it is larger and only padded with 0 bytes" do
|
|
50
|
+
@dict[field] = 't' * 48 + "\x00" * 20
|
|
51
|
+
assert(@dict.validate)
|
|
52
|
+
assert_equal('t' * 48, @dict[field])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
[:UE, :OE, :Perms].each do |field|
|
|
57
|
+
it "validates the existence of the /#{field} field" do
|
|
58
|
+
@dict.delete(field)
|
|
59
|
+
refute(@dict.validate)
|
|
60
|
+
end
|
|
51
61
|
end
|
|
52
62
|
end
|
|
53
63
|
end
|
|
@@ -211,22 +221,24 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
|
211
221
|
describe "prepare_decryption" do
|
|
212
222
|
it "fails if the /Filter value is incorrect" do
|
|
213
223
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
214
|
-
@handler.set_up_decryption({Filter: :NonStandard, V: 2
|
|
224
|
+
@handler.set_up_decryption({Filter: :NonStandard, V: 2, R: 4, O: 't' * 32, U: 't' * 32, P: 0,
|
|
225
|
+
Length: 128})
|
|
215
226
|
end
|
|
216
227
|
assert_match(/Invalid \/Filter value NonStandard/i, exp.message)
|
|
217
228
|
end
|
|
218
229
|
|
|
219
230
|
it "fails if the /R value is incorrect" do
|
|
220
231
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
221
|
-
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5
|
|
232
|
+
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5, O: 't' * 32, U: 't' * 32, P: 0,
|
|
233
|
+
Length: 128})
|
|
222
234
|
end
|
|
223
235
|
assert_match(/Invalid \/R value 5/i, exp.message)
|
|
224
236
|
end
|
|
225
237
|
|
|
226
238
|
it "fails if the supplied password is invalid" do
|
|
227
239
|
exp = assert_raises(HexaPDF::EncryptionError) do
|
|
228
|
-
@handler.set_up_decryption({Filter: :Standard, V:
|
|
229
|
-
UE: 'a' * 32, OE: 'a' * 32})
|
|
240
|
+
@handler.set_up_decryption({Filter: :Standard, V: 5, R: 6, U: 'a' * 48, O: 'a' * 48,
|
|
241
|
+
UE: 'a' * 32, OE: 'a' * 32, P: 0, Perms: 'a' * 16})
|
|
230
242
|
end
|
|
231
243
|
assert_match(/Invalid password/i, exp.message)
|
|
232
244
|
end
|
|
@@ -234,7 +246,7 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
|
234
246
|
it "assigns empty strings to the trailer's ID field if it is missing" do
|
|
235
247
|
refute(@document.trailer.key?(:ID))
|
|
236
248
|
exp = assert_raises(HexaPDF::EncryptionError) do
|
|
237
|
-
@handler.set_up_decryption({Filter: :Standard, V: 1, R: 2, U: 'a' *
|
|
249
|
+
@handler.set_up_decryption({Filter: :Standard, V: 1, R: 2, U: 'a' * 32, O: 'a' * 32, P: 15})
|
|
238
250
|
end
|
|
239
251
|
assert_match(/Invalid password/i, exp.message)
|
|
240
252
|
assert_equal(['', ''], @document.trailer[:ID].value)
|
|
@@ -286,7 +298,7 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
|
286
298
|
it "returns an array of permission symbols" do
|
|
287
299
|
perms = @handler.class::Permissions::MODIFY_CONTENT | @handler.class::Permissions::COPY_CONTENT
|
|
288
300
|
@handler.set_up_encryption(permissions: perms)
|
|
289
|
-
assert_equal([:copy_content, :modify_content], @handler.permissions.sort)
|
|
301
|
+
assert_equal([:copy_content, :extract_content, :modify_content], @handler.permissions.sort)
|
|
290
302
|
end
|
|
291
303
|
|
|
292
304
|
describe "handling of metadata streams" do
|
|
@@ -66,6 +66,20 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
describe "custom_glyph" do
|
|
70
|
+
it "returns the specified glyph object" do
|
|
71
|
+
glyph = @font_wrapper.custom_glyph(0, "str")
|
|
72
|
+
assert_equal(0, glyph.id)
|
|
73
|
+
assert_equal("str", glyph.str)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "fails if an invalid glyph id is specified" do
|
|
77
|
+
exp = assert_raises(HexaPDF::Error) { @font_wrapper.custom_glyph(-5, 'c') }
|
|
78
|
+
assert_match(/Glyph ID -5 is invalid for font 'Ubuntu-Title'/, exp.message)
|
|
79
|
+
assert_raises(HexaPDF::Error) { @font_wrapper.custom_glyph(9999, 'c') }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
69
83
|
describe "encode" do
|
|
70
84
|
it "returns the encoded glyph ID for fonts that are subset" do
|
|
71
85
|
code = @font_wrapper.encode(@font_wrapper.glyph(3))
|
|
@@ -83,7 +97,10 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
83
97
|
end
|
|
84
98
|
|
|
85
99
|
it "raises an error if an InvalidGlyph is encoded" do
|
|
86
|
-
assert_raises(HexaPDF::
|
|
100
|
+
exp = assert_raises(HexaPDF::MissingGlyphError) do
|
|
101
|
+
@font_wrapper.encode(@font_wrapper.decode_utf8("ö").first)
|
|
102
|
+
end
|
|
103
|
+
assert_match(/No glyph for "ö" in font 'Ubuntu-Title'/, exp.message)
|
|
87
104
|
end
|
|
88
105
|
end
|
|
89
106
|
|
|
@@ -65,6 +65,19 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
describe "custom_glyph" do
|
|
69
|
+
it "returns the specified glyph object" do
|
|
70
|
+
glyph = @times_wrapper.custom_glyph(:question, "str")
|
|
71
|
+
assert_equal(:question, glyph.name)
|
|
72
|
+
assert_equal("str", glyph.str)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "fails if the provided glyph name is not available for the font" do
|
|
76
|
+
exp = assert_raises(HexaPDF::Error) { @times_wrapper.custom_glyph(:handicap, 'c') }
|
|
77
|
+
assert_match(/Glyph named :handicap not found in font 'Times Roman'/, exp.message)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
68
81
|
describe "encode" do
|
|
69
82
|
describe "uses WinAnsiEncoding as initial encoding for non-symbolic fonts" do
|
|
70
83
|
it "returns the PDF font dictionary using WinAnsiEncoding and encoded glyph" do
|
|
@@ -75,7 +88,8 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
75
88
|
end
|
|
76
89
|
|
|
77
90
|
it "fails if an InvalidGlyph is encoded" do
|
|
78
|
-
assert_raises(HexaPDF::
|
|
91
|
+
exp = assert_raises(HexaPDF::MissingGlyphError) { @times_wrapper.encode(@times_wrapper.glyph(:ffi)) }
|
|
92
|
+
assert_match(/No glyph for "ffi" in font 'Times Roman'/, exp.message)
|
|
79
93
|
end
|
|
80
94
|
|
|
81
95
|
it "fails if the encoding does not support the given glyph" do
|
|
@@ -207,7 +207,7 @@ describe HexaPDF::Layout::Box do
|
|
|
207
207
|
end
|
|
208
208
|
|
|
209
209
|
describe "empty?" do
|
|
210
|
-
it "is
|
|
210
|
+
it "is empty when no drawing operation is specified" do
|
|
211
211
|
assert(create_box.empty?)
|
|
212
212
|
refute(create_box {}.empty?)
|
|
213
213
|
refute(create_box(style: {background_color: [5]}).empty?)
|
|
@@ -22,7 +22,7 @@ describe HexaPDF::Layout::ColumnBox do
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def check_box(box, width, height, fit_pos = nil)
|
|
25
|
-
assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box fit
|
|
25
|
+
assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box didn't fit")
|
|
26
26
|
assert_equal(width, box.width, "box width")
|
|
27
27
|
assert_equal(height, box.height, "box height")
|
|
28
28
|
if fit_pos
|
|
@@ -46,6 +46,24 @@ describe HexaPDF::Layout::ColumnBox do
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
describe "empty?" do
|
|
50
|
+
it "is empty if nothing is fit yet" do
|
|
51
|
+
assert(create_box.empty?)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "is empty if no box fits" do
|
|
55
|
+
box = create_box(children: [@fixed_size_boxes[0]], columns: [10])
|
|
56
|
+
box.fit(@frame.available_width, @frame.available_height, @frame)
|
|
57
|
+
assert(box.empty?)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "is not empty if at least one box fits" do
|
|
61
|
+
box = create_box(children: [@fixed_size_boxes[0]], columns: [30])
|
|
62
|
+
check_box(box, 30, 10)
|
|
63
|
+
refute(box.empty?)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
49
67
|
describe "fit" do
|
|
50
68
|
[:default, :flow].each do |position|
|
|
51
69
|
it "respects the set initial width, position #{position}" do
|
|
@@ -132,27 +150,53 @@ describe HexaPDF::Layout::ColumnBox do
|
|
|
132
150
|
assert_equal(5, box_b.children.size)
|
|
133
151
|
end
|
|
134
152
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
describe "draw_content" do
|
|
154
|
+
before do
|
|
155
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "draws the result onto the canvas" do
|
|
159
|
+
box = create_box(children: @fixed_size_boxes)
|
|
160
|
+
box.fit(100, 100, @frame)
|
|
161
|
+
box.draw(@canvas, 0, 100 - box.height)
|
|
162
|
+
operators = 90.step(to: 20, by: -10).map do |y|
|
|
163
|
+
[[:save_graphics_state],
|
|
164
|
+
[:concatenate_matrix, [1, 0, 0, 1, 0, y]],
|
|
165
|
+
[:move_to, [0, 0]],
|
|
166
|
+
[:end_path],
|
|
167
|
+
[:restore_graphics_state]]
|
|
168
|
+
end
|
|
169
|
+
operators.concat(90.step(to: 30, by: -10).map do |y|
|
|
170
|
+
[[:save_graphics_state],
|
|
171
|
+
[:concatenate_matrix, [1, 0, 0, 1, 55, y]],
|
|
172
|
+
[:move_to, [0, 0]],
|
|
173
|
+
[:end_path],
|
|
174
|
+
[:restore_graphics_state]]
|
|
175
|
+
end)
|
|
176
|
+
operators.flatten!(1)
|
|
177
|
+
assert_operators(@canvas.contents, operators)
|
|
178
|
+
end
|
|
138
179
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
180
|
+
it "takes a different final location into account" do
|
|
181
|
+
box = create_box(children: @fixed_size_boxes[0, 2])
|
|
182
|
+
box.fit(100, 100, @frame)
|
|
183
|
+
box.draw(@canvas, 20, 10)
|
|
184
|
+
operators = [
|
|
185
|
+
[:save_graphics_state],
|
|
186
|
+
[:concatenate_matrix, [1, 0, 0, 1, 20, -80]],
|
|
187
|
+
[:save_graphics_state],
|
|
188
|
+
[:concatenate_matrix, [1, 0, 0, 1, 0, 90]],
|
|
189
|
+
[:move_to, [0, 0]],
|
|
190
|
+
[:end_path],
|
|
191
|
+
[:restore_graphics_state],
|
|
192
|
+
[:save_graphics_state],
|
|
193
|
+
[:concatenate_matrix, [1, 0, 0, 1, 55, 90]],
|
|
194
|
+
[:move_to, [0, 0]],
|
|
195
|
+
[:end_path],
|
|
196
|
+
[:restore_graphics_state],
|
|
197
|
+
[:restore_graphics_state],
|
|
198
|
+
]
|
|
199
|
+
assert_operators(@canvas.contents, operators)
|
|
147
200
|
end
|
|
148
|
-
operators.concat(90.step(to: 30, by: -10).map do |y|
|
|
149
|
-
[[:save_graphics_state],
|
|
150
|
-
[:concatenate_matrix, [1, 0, 0, 1, 55, y]],
|
|
151
|
-
[:move_to, [0, 0]],
|
|
152
|
-
[:end_path],
|
|
153
|
-
[:restore_graphics_state]]
|
|
154
|
-
end)
|
|
155
|
-
operators.flatten!(1)
|
|
156
|
-
assert_operators(@canvas.contents, operators)
|
|
157
201
|
end
|
|
158
202
|
end
|