hexapdf 0.32.2 → 0.34.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 +104 -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/examples/026-optional_content.rb +55 -0
- data/examples/027-composer_optional_content.rb +83 -0
- data/lib/hexapdf/cli/command.rb +12 -3
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +5 -7
- data/lib/hexapdf/composer.rb +106 -53
- data/lib/hexapdf/configuration.rb +65 -40
- data/lib/hexapdf/content/canvas.rb +445 -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 +54 -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 +7 -5
- data/lib/hexapdf/dictionary_fields.rb +43 -16
- 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 +3 -4
- 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 +170 -39
- data/lib/hexapdf/document/pages.rb +4 -3
- data/lib/hexapdf/document.rb +96 -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 +11 -21
- 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 +32 -5
- data/lib/hexapdf/layout/box_fitter.rb +2 -2
- data/lib/hexapdf/layout/column_box.rb +20 -5
- data/lib/hexapdf/layout/frame.rb +53 -18
- data/lib/hexapdf/layout/image_box.rb +5 -0
- data/lib/hexapdf/layout/inline_box.rb +21 -9
- data/lib/hexapdf/layout/list_box.rb +50 -20
- data/lib/hexapdf/layout/page_style.rb +6 -5
- data/lib/hexapdf/layout/style.rb +64 -9
- data/lib/hexapdf/layout/table_box.rb +684 -0
- data/lib/hexapdf/layout/text_box.rb +12 -3
- data/lib/hexapdf/layout/text_fragment.rb +29 -3
- data/lib/hexapdf/layout/text_layouter.rb +32 -8
- 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 +5 -4
- 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/set_ocg_state.rb +86 -0
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +2 -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 +2 -3
- data/lib/hexapdf/type/annotations/widget.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +11 -2
- data/lib/hexapdf/type/cid_font.rb +18 -4
- 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 +76 -6
- 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/optional_content_configuration.rb +170 -0
- data/lib/hexapdf/type/optional_content_group.rb +370 -0
- data/lib/hexapdf/type/optional_content_membership.rb +63 -0
- data/lib/hexapdf/type/optional_content_properties.rb +158 -0
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +46 -21
- data/lib/hexapdf/type/page_label.rb +5 -9
- 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/type.rb +4 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
- 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 +49 -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 +105 -2
- data/test/hexapdf/document/test_pages.rb +6 -6
- 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 +14 -5
- data/test/hexapdf/layout/test_column_box.rb +65 -21
- data/test/hexapdf/layout/test_frame.rb +27 -15
- data/test/hexapdf/layout/test_image_box.rb +4 -0
- data/test/hexapdf/layout/test_inline_box.rb +17 -3
- data/test/hexapdf/layout/test_list_box.rb +84 -33
- data/test/hexapdf/layout/test_page_style.rb +3 -2
- data/test/hexapdf/layout/test_style.rb +60 -0
- data/test/hexapdf/layout/test_table_box.rb +728 -0
- data/test/hexapdf/layout/test_text_box.rb +26 -0
- data/test/hexapdf/layout/test_text_fragment.rb +33 -0
- data/test/hexapdf/layout/test_text_layouter.rb +36 -5
- data/test/hexapdf/test_composer.rb +10 -0
- data/test/hexapdf/test_dictionary.rb +10 -0
- data/test/hexapdf/test_dictionary_fields.rb +4 -1
- data/test/hexapdf/test_document.rb +5 -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/actions/test_set_ocg_state.rb +40 -0
- data/test/hexapdf/type/test_catalog.rb +11 -0
- data/test/hexapdf/type/test_form.rb +119 -0
- data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
- data/test/hexapdf/type/test_optional_content_group.rb +158 -0
- data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
- data/test/hexapdf/type/test_page.rb +20 -6
- metadata +28 -8
data/lib/hexapdf/type/page.rb
CHANGED
|
@@ -54,7 +54,7 @@ module HexaPDF
|
|
|
54
54
|
# Field inheritance means that if a field is not set on the page object itself, the value is
|
|
55
55
|
# taken from the nearest page tree ancestor that has this value set.
|
|
56
56
|
#
|
|
57
|
-
# See:
|
|
57
|
+
# See: PDF2.0 s7.7.3.3, s7.7.3.4, Pages
|
|
58
58
|
class Page < Dictionary
|
|
59
59
|
|
|
60
60
|
# The predefined paper sizes in points (1/72 inch):
|
|
@@ -223,7 +223,7 @@ module HexaPDF
|
|
|
223
223
|
# The art box defines the region of the page's meaningful content as intended by the
|
|
224
224
|
# author. The default is the crop box.
|
|
225
225
|
#
|
|
226
|
-
# See:
|
|
226
|
+
# See: PDF2.0 s14.11.2
|
|
227
227
|
def box(type = :crop, rectangle = nil)
|
|
228
228
|
if rectangle
|
|
229
229
|
case type
|
|
@@ -261,12 +261,23 @@ module HexaPDF
|
|
|
261
261
|
# Rotates the page +angle+ degrees counterclockwise where +angle+ has to be a multiple of 90.
|
|
262
262
|
#
|
|
263
263
|
# Positive values rotate the page to the left, negative values to the right. If +flatten+ is
|
|
264
|
-
# +true+, the rotation is not done via the page's meta data but by
|
|
265
|
-
# itself
|
|
264
|
+
# +true+, the rotation is not done via the page's meta (i.e. the /Rotate key) data but by
|
|
265
|
+
# rotating the canvas itself and all other necessary objects like the various page boxes and
|
|
266
|
+
# annotations.
|
|
266
267
|
#
|
|
267
|
-
#
|
|
268
|
-
#
|
|
269
|
-
#
|
|
268
|
+
# Notes:
|
|
269
|
+
#
|
|
270
|
+
# * The given +angle+ is applied in addition to a possibly already existing rotation
|
|
271
|
+
# (specified via the /Rotate key) and does not replace it.
|
|
272
|
+
#
|
|
273
|
+
# * Specifying 0 for +angle+ is valid and means that no additional rotation should be applied.
|
|
274
|
+
# The only meaningful usage of 0 for +angle+ is when +flatten+ is set to +true+ (so that the
|
|
275
|
+
# /Rotate key is removed and the existing rotation information incorporated into the canvas,
|
|
276
|
+
# page boxes and annotations).
|
|
277
|
+
#
|
|
278
|
+
# * The /Rotate key of a page object describes the angle in a clockwise orientation but this
|
|
279
|
+
# method uses counterclockwise rotation to be consistent with other rotation methods (e.g.
|
|
280
|
+
# HexaPDF::Content::Canvas#rotate).
|
|
270
281
|
def rotate(angle, flatten: false)
|
|
271
282
|
if angle % 90 != 0
|
|
272
283
|
raise ArgumentError, "Page rotation has to be multiple of 90 degrees"
|
|
@@ -423,7 +434,7 @@ module HexaPDF
|
|
|
423
434
|
#
|
|
424
435
|
# To check whether the origin has been translated or not, use
|
|
425
436
|
#
|
|
426
|
-
# canvas.
|
|
437
|
+
# canvas.pos(0, 0)
|
|
427
438
|
#
|
|
428
439
|
# and check whether the result is [0, 0]. If it is, then the origin has not been
|
|
429
440
|
# translated.
|
|
@@ -522,8 +533,8 @@ module HexaPDF
|
|
|
522
533
|
# Yields each annotation of this page.
|
|
523
534
|
def each_annotation
|
|
524
535
|
return to_enum(__method__) unless block_given?
|
|
525
|
-
self[:Annots]
|
|
526
|
-
next unless annotation
|
|
536
|
+
Array(self[:Annots]).each do |annotation|
|
|
537
|
+
next unless annotation&.key?(:Subtype) && annotation&.key?(:Rect)
|
|
527
538
|
yield(document.wrap(annotation, type: :Annot))
|
|
528
539
|
end
|
|
529
540
|
self
|
|
@@ -539,14 +550,18 @@ module HexaPDF
|
|
|
539
550
|
# If an annotation is a form field widget, only the widget will be deleted but not the form
|
|
540
551
|
# field itself.
|
|
541
552
|
def flatten_annotations(annotations = self[:Annots])
|
|
542
|
-
not_flattened = (annotations || []
|
|
553
|
+
not_flattened = Array(annotations) || []
|
|
543
554
|
return not_flattened unless key?(:Annots)
|
|
544
555
|
|
|
545
|
-
annotations =
|
|
556
|
+
annotations = if annotations == self[:Annots]
|
|
557
|
+
not_flattened
|
|
558
|
+
else
|
|
559
|
+
not_flattened & Array(self[:Annots])
|
|
560
|
+
end
|
|
546
561
|
return not_flattened if annotations.empty?
|
|
547
562
|
|
|
548
563
|
canvas = self.canvas(type: :overlay)
|
|
549
|
-
if (pos = canvas.
|
|
564
|
+
if (pos = canvas.pos(0, 0)) != [0, 0]
|
|
550
565
|
canvas.save_graphics_state
|
|
551
566
|
canvas.translate(-pos[0], -pos[1])
|
|
552
567
|
end
|
|
@@ -554,6 +569,11 @@ module HexaPDF
|
|
|
554
569
|
to_delete = []
|
|
555
570
|
not_flattened -= annotations
|
|
556
571
|
annotations.each do |annotation|
|
|
572
|
+
unless annotation&.key?(:Subtype) && annotation&.key?(:Rect)
|
|
573
|
+
to_delete << annotation if annotation
|
|
574
|
+
next
|
|
575
|
+
end
|
|
576
|
+
|
|
557
577
|
annotation = document.wrap(annotation, type: :Annot)
|
|
558
578
|
appearance = annotation.appearance
|
|
559
579
|
if annotation.flagged?(:hidden) || annotation.flagged?(:invisible)
|
|
@@ -567,8 +587,8 @@ module HexaPDF
|
|
|
567
587
|
rect = annotation[:Rect]
|
|
568
588
|
box = appearance.box
|
|
569
589
|
|
|
570
|
-
#
|
|
571
|
-
# Step
|
|
590
|
+
# PDF2.0 12.5.5 algorithm
|
|
591
|
+
# Step 1) Calculate smallest rectangle containing transformed bounding box
|
|
572
592
|
matrix = HexaPDF::Content::TransformationMatrix.new(*appearance[:Matrix].value)
|
|
573
593
|
llx, lly = matrix.evaluate(box.left, box.bottom)
|
|
574
594
|
ulx, uly = matrix.evaluate(box.left, box.top)
|
|
@@ -582,14 +602,19 @@ module HexaPDF
|
|
|
582
602
|
next
|
|
583
603
|
end
|
|
584
604
|
|
|
585
|
-
# Step
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
605
|
+
# Step 2) Fit calculated rectangle to annotation rectangle by translating/scaling
|
|
606
|
+
|
|
607
|
+
# The final matrix is composed by translating the bottom-left corner of the transformed
|
|
608
|
+
# bounding box to the bottom-left corner of the annotation rectangle and scaling from the
|
|
609
|
+
# bottom-left corner of the transformed bounding box.
|
|
610
|
+
sx = rect.width.fdiv(right - left)
|
|
611
|
+
sy = rect.height.fdiv(top - bottom)
|
|
612
|
+
tx = rect.left - left + left - left * sx
|
|
613
|
+
ty = rect.bottom - bottom + bottom - bottom * sy
|
|
589
614
|
|
|
590
|
-
# Step
|
|
615
|
+
# Step 3) Premultiply form matrix - done implicitly when drawing the XObject
|
|
591
616
|
|
|
592
|
-
canvas.transform(
|
|
617
|
+
canvas.transform(sx, 0, 0, sy, tx, ty) do
|
|
593
618
|
# Use [box.left, box.bottom] to counter default translation in #xobject since that
|
|
594
619
|
# is already taken care of in matrix a
|
|
595
620
|
canvas.xobject(appearance, at: [box.left, box.bottom])
|
|
@@ -70,7 +70,7 @@ module HexaPDF
|
|
|
70
70
|
#
|
|
71
71
|
# "", "", "", ... (i.e. always the empty string)
|
|
72
72
|
#
|
|
73
|
-
# See:
|
|
73
|
+
# See: PDF2.0 s12.4.2, HexaPDF::Document::Pages, HexaPDF::Type::Catalog
|
|
74
74
|
class PageLabel < Dictionary
|
|
75
75
|
|
|
76
76
|
define_type :PageLabel
|
|
@@ -103,8 +103,7 @@ module HexaPDF
|
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
# :nodoc:
|
|
107
|
-
NUMBERING_STYLE_MAPPING = {
|
|
106
|
+
NUMBERING_STYLE_MAPPING = { # :nodoc:
|
|
108
107
|
decimal: :D, D: :D,
|
|
109
108
|
uppercase_roman: :R, R: :R,
|
|
110
109
|
lowercase_roman: :r, r: :r,
|
|
@@ -113,8 +112,7 @@ module HexaPDF
|
|
|
113
112
|
none: nil
|
|
114
113
|
}
|
|
115
114
|
|
|
116
|
-
# :nodoc:
|
|
117
|
-
REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse]
|
|
115
|
+
REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse] # :nodoc:
|
|
118
116
|
|
|
119
117
|
# :call-seq:
|
|
120
118
|
# page_label.numbering_style -> numbering_style
|
|
@@ -174,8 +172,7 @@ module HexaPDF
|
|
|
174
172
|
|
|
175
173
|
private
|
|
176
174
|
|
|
177
|
-
# :nodoc:
|
|
178
|
-
ALPHABET = ('A'..'Z').to_a
|
|
175
|
+
ALPHABET = ('A'..'Z').to_a # :nodoc:
|
|
179
176
|
|
|
180
177
|
# Maps the given number to uppercase (or, if +lowercase+ is +true+, lowercase) letters (e.g. 1
|
|
181
178
|
# -> A, 27 -> AA, 28 -> AB, ...).
|
|
@@ -188,8 +185,7 @@ module HexaPDF
|
|
|
188
185
|
lowercase ? result.downcase : result
|
|
189
186
|
end
|
|
190
187
|
|
|
191
|
-
# :nodoc:
|
|
192
|
-
ROMAN_NUMERAL_MAPPING = {
|
|
188
|
+
ROMAN_NUMERAL_MAPPING = { # :nodoc:
|
|
193
189
|
1000 => "M",
|
|
194
190
|
900 => "CM",
|
|
195
191
|
500 => "D",
|
data/lib/hexapdf/type/trailer.rb
CHANGED
|
@@ -54,7 +54,7 @@ module HexaPDF
|
|
|
54
54
|
# HexaPDF::Revision object's trailer dictionary is always of this type. Only when a
|
|
55
55
|
# cross-reference stream is written is the trailer integrated into the stream's dictionary.
|
|
56
56
|
#
|
|
57
|
-
# See:
|
|
57
|
+
# See: PDF2.0 s7.5.5, s14.4; XRefStream
|
|
58
58
|
class Trailer < Dictionary
|
|
59
59
|
|
|
60
60
|
define_type :XXTrailer
|
|
@@ -79,7 +79,7 @@ module HexaPDF
|
|
|
79
79
|
|
|
80
80
|
# Sets the /ID field to an array of two copies of a random string and returns this array.
|
|
81
81
|
#
|
|
82
|
-
# See:
|
|
82
|
+
# See: PDF2.0 14.4
|
|
83
83
|
def set_random_id
|
|
84
84
|
value[:ID] = [Digest::MD5.digest(rand.to_s)] * 2
|
|
85
85
|
end
|
|
@@ -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
|
data/lib/hexapdf/type.rb
CHANGED
|
@@ -76,6 +76,10 @@ module HexaPDF
|
|
|
76
76
|
autoload(:OutlineItem, 'hexapdf/type/outline_item')
|
|
77
77
|
autoload(:PageLabel, 'hexapdf/type/page_label')
|
|
78
78
|
autoload(:MarkInformation, 'hexapdf/type/mark_information')
|
|
79
|
+
autoload(:OptionalContentGroup, 'hexapdf/type/optional_content_group')
|
|
80
|
+
autoload(:OptionalContentMembership, 'hexapdf/type/optional_content_membership')
|
|
81
|
+
autoload(:OptionalContentProperties, 'hexapdf/type/optional_content_properties')
|
|
82
|
+
autoload(:OptionalContentConfiguration, 'hexapdf/type/optional_content_configuration')
|
|
79
83
|
|
|
80
84
|
end
|
|
81
85
|
|
|
@@ -49,10 +49,9 @@ 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
|
-
# :nodoc:
|
|
56
55
|
CHARACTER_MAP = %W[\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
|
|
57
56
|
\uFFFD \u0009 \u000A \uFFFD \uFFFD \u000D \uFFFD \uFFFD
|
|
58
57
|
\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
|
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)
|
|
@@ -71,6 +71,13 @@ describe HexaPDF::Content::Canvas do
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
describe "pos" do
|
|
75
|
+
it "returns the transformed position" do
|
|
76
|
+
@canvas.translate(9, 4)
|
|
77
|
+
assert_equal([10, 5], @canvas.pos(1, 1))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
74
81
|
describe "save_graphics_state" do
|
|
75
82
|
it "invokes the operator implementation" do
|
|
76
83
|
assert_operator_invoked(:q) { @canvas.save_graphics_state }
|
|
@@ -1193,7 +1200,6 @@ describe HexaPDF::Content::Canvas do
|
|
|
1193
1200
|
assert_operators(@canvas.contents, [[:set_font_and_size, [:F1, 10]],
|
|
1194
1201
|
[:begin_text],
|
|
1195
1202
|
[:set_text_matrix, [1, 0, 0, 1, 100, 100]],
|
|
1196
|
-
[:set_leading, [10]],
|
|
1197
1203
|
[:show_text_with_positioning, [["Hallo"]]]])
|
|
1198
1204
|
end
|
|
1199
1205
|
|
|
@@ -1281,6 +1287,48 @@ describe HexaPDF::Content::Canvas do
|
|
|
1281
1287
|
end
|
|
1282
1288
|
end
|
|
1283
1289
|
|
|
1290
|
+
describe "optional_content" do
|
|
1291
|
+
it "invokes the marked-sequence operator implementation" do
|
|
1292
|
+
assert_operator_invoked(:BDC, :OC, :P1) { @canvas.optional_content('Test') }
|
|
1293
|
+
end
|
|
1294
|
+
|
|
1295
|
+
it "is serialized correctly when no block is used" do
|
|
1296
|
+
@canvas.optional_content('Test')
|
|
1297
|
+
assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]]])
|
|
1298
|
+
end
|
|
1299
|
+
|
|
1300
|
+
it "is serialized correctly when a block is used" do
|
|
1301
|
+
@canvas.optional_content('Test') {}
|
|
1302
|
+
assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
|
|
1303
|
+
[:end_marked_content]])
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
it "uses the provided OCG dictionary" do
|
|
1307
|
+
ocg = @doc.optional_content.add_ocg('Test')
|
|
1308
|
+
@canvas.optional_content(ocg)
|
|
1309
|
+
assert_equal(ocg, @page.resources.property_list(:P1))
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
it "uses an existing OCG specified by name" do
|
|
1313
|
+
ocg = @doc.optional_content.add_ocg('Test')
|
|
1314
|
+
@canvas.optional_content('Test')
|
|
1315
|
+
assert_equal(ocg, @page.resources.property_list(:P1))
|
|
1316
|
+
end
|
|
1317
|
+
|
|
1318
|
+
it "creates an OCG if the named one doesn't yet exist" do
|
|
1319
|
+
@canvas.optional_content('Test')
|
|
1320
|
+
assert_equal(@doc.optional_content.ocg('Test'), @page.resources.property_list(:P1))
|
|
1321
|
+
end
|
|
1322
|
+
|
|
1323
|
+
it "always creates a new OCG if use_existing_ocg is false" do
|
|
1324
|
+
ocg = @doc.optional_content.add_ocg('Test')
|
|
1325
|
+
@canvas.optional_content('Test', use_existing_ocg: false)
|
|
1326
|
+
pl_item = @page.resources.property_list(:P1)
|
|
1327
|
+
refute_equal(ocg, pl_item)
|
|
1328
|
+
assert_equal(@doc.optional_content.ocgs.last, pl_item)
|
|
1329
|
+
end
|
|
1330
|
+
end
|
|
1331
|
+
|
|
1284
1332
|
describe "color_from_specification "do
|
|
1285
1333
|
it "accepts a color string" do
|
|
1286
1334
|
assert_equal([1, 0, 0], @canvas.color_from_specification("red").components)
|
|
@@ -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
|
|
@@ -171,6 +219,10 @@ describe HexaPDF::Document::Layout do
|
|
|
171
219
|
box = @layout.text_box("Test", box_style: :named)
|
|
172
220
|
assert_equal(20, box.style.font_size)
|
|
173
221
|
end
|
|
222
|
+
|
|
223
|
+
it "raises an error if the to-be-used style doesn't exist" do
|
|
224
|
+
assert_raises(HexaPDF::Error) { @layout.text_box("Test", style: :unknown) }
|
|
225
|
+
end
|
|
174
226
|
end
|
|
175
227
|
|
|
176
228
|
describe "formatted_text" do
|
|
@@ -257,9 +309,10 @@ describe HexaPDF::Document::Layout do
|
|
|
257
309
|
|
|
258
310
|
it "allows creating an inline box through a hash with a :box key" do
|
|
259
311
|
block = lambda {|item| item.box(:base, width: 5, height: 15) }
|
|
260
|
-
box = @layout.formatted_text_box([{box: :
|
|
312
|
+
box = @layout.formatted_text_box([{box: :column, columns: 1, width: 100, block: block}])
|
|
261
313
|
ibox = box.instance_variable_get(:@items).first
|
|
262
|
-
|
|
314
|
+
ibox.fit_wrapped_box(nil)
|
|
315
|
+
assert_equal(100, ibox.width)
|
|
263
316
|
assert_equal(15, ibox.height)
|
|
264
317
|
end
|
|
265
318
|
|
|
@@ -290,6 +343,56 @@ describe HexaPDF::Document::Layout do
|
|
|
290
343
|
end
|
|
291
344
|
end
|
|
292
345
|
|
|
346
|
+
describe "table_box" do
|
|
347
|
+
it "creates a table box" do
|
|
348
|
+
box = @layout.table_box([['m']], column_widths: [100], header: proc { [['a']] },
|
|
349
|
+
footer: proc { [['b']] }, cell_style: {background_color: "red"},
|
|
350
|
+
width: 100, height: 300, style: {background_color: "blue"},
|
|
351
|
+
properties: {key: :value}, border: {width: 1})
|
|
352
|
+
assert_equal(100, box.width)
|
|
353
|
+
assert_equal(300, box.height)
|
|
354
|
+
assert_equal("blue", box.style.background_color)
|
|
355
|
+
assert_equal(1, box.style.border.width.left)
|
|
356
|
+
assert_equal({key: :value}, box.properties)
|
|
357
|
+
assert_equal(HexaPDF::Layout::TextBox, box.cells[0, 0].children.class)
|
|
358
|
+
assert_equal([100], box.column_widths)
|
|
359
|
+
assert_equal('a', box.header_cells[0, 0].children)
|
|
360
|
+
assert_equal('b', box.footer_cells[0, 0].children)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it "doesn't modify the children of cells if they are already in the correct form" do
|
|
364
|
+
image_path = File.join(TEST_DATA_DIR, 'images', 'gray.jpg')
|
|
365
|
+
cell0 = @layout.text('a')
|
|
366
|
+
cell1 = [@layout.text('b'), @layout.image(image_path)]
|
|
367
|
+
box = @layout.table_box([[cell0, cell1]])
|
|
368
|
+
assert_same(cell0, box.cells[0, 0].children)
|
|
369
|
+
assert_same(cell1, box.cells[0, 1].children)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
it "converts cells containing other than Box and Array instances" do
|
|
373
|
+
box = @layout.table_box([['a', 5]])
|
|
374
|
+
assert_kind_of(HexaPDF::Layout::TextBox, box.cells[0, 0].children)
|
|
375
|
+
assert_kind_of(HexaPDF::Layout::TextBox, box.cells[0, 1].children)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
it "allows customizing the creation arguments" do
|
|
379
|
+
box = @layout.table_box([['a']]) do |args|
|
|
380
|
+
args[] = {font_size: 20}
|
|
381
|
+
end
|
|
382
|
+
assert_equal(20, box.cells[0, 0].children.style.font_size)
|
|
383
|
+
refute_equal(20, box.cells[0, 0].style.font_size)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
it "allows styling table cells themselves" do
|
|
387
|
+
box = @layout.table_box([['a', @layout.text('b')]]) do |args|
|
|
388
|
+
args[] = {cell: {background_color: "green"}}
|
|
389
|
+
end
|
|
390
|
+
assert_equal('green', box.cells[0, 0].style.background_color)
|
|
391
|
+
assert_nil(box.cells[0, 0].children.style.background_color)
|
|
392
|
+
assert_equal('green', box.cells[0, 1].style.background_color)
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
293
396
|
describe "lorem_ipsum_box" do
|
|
294
397
|
it "creates a standard lorem ipsum box" do
|
|
295
398
|
box = @layout.lorem_ipsum_box(width: 10, height: 15, font_size: 15)
|