hexapdf 1.2.0 → 1.4.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 +90 -0
- data/README.md +1 -1
- data/lib/hexapdf/cli/form.rb +9 -4
- data/lib/hexapdf/cli/inspect.rb +13 -4
- data/lib/hexapdf/composer.rb +14 -0
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +1 -2
- data/lib/hexapdf/document/annotations.rb +107 -2
- data/lib/hexapdf/document/layout.rb +94 -15
- data/lib/hexapdf/document/metadata.rb +10 -3
- data/lib/hexapdf/document.rb +9 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +7 -2
- data/lib/hexapdf/error.rb +11 -3
- data/lib/hexapdf/font/true_type/subsetter.rb +15 -2
- data/lib/hexapdf/layout/box.rb +5 -0
- data/lib/hexapdf/layout/container_box.rb +63 -28
- data/lib/hexapdf/layout/style.rb +129 -20
- data/lib/hexapdf/layout/table_box.rb +20 -2
- 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/annotation.rb +12 -0
- data/lib/hexapdf/type/annotations/appearance_generator.rb +169 -16
- data/lib/hexapdf/type/annotations/border_effect.rb +99 -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 +5 -192
- data/lib/hexapdf/type/annotations/line_ending_styling.rb +208 -0
- data/lib/hexapdf/type/annotations/markup_annotation.rb +0 -1
- data/lib/hexapdf/type/annotations/polygon.rb +64 -0
- data/lib/hexapdf/type/annotations/polygon_polyline.rb +109 -0
- data/lib/hexapdf/type/annotations/polyline.rb +64 -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 +50 -20
- data/lib/hexapdf/type/annotations.rb +9 -0
- data/lib/hexapdf/type/measure.rb +57 -0
- data/lib/hexapdf/type.rb +1 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +0 -1
- data/test/hexapdf/document/test_annotations.rb +42 -0
- data/test/hexapdf/document/test_layout.rb +38 -10
- data/test/hexapdf/document/test_metadata.rb +13 -1
- data/test/hexapdf/encryption/test_standard_security_handler.rb +2 -1
- data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
- data/test/hexapdf/layout/test_box.rb +8 -0
- data/test/hexapdf/layout/test_container_box.rb +34 -6
- data/test/hexapdf/layout/test_page_style.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +46 -2
- data/test/hexapdf/layout/test_table_box.rb +14 -1
- data/test/hexapdf/test_composer.rb +7 -0
- data/test/hexapdf/test_dictionary_fields.rb +1 -0
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_pdf_array.rb +36 -3
- 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/annotations/test_appearance_generator.rb +210 -0
- data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
- data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
- data/test/hexapdf/type/annotations/test_line.rb +0 -45
- data/test/hexapdf/type/annotations/test_line_ending_styling.rb +42 -0
- data/test/hexapdf/type/annotations/test_polygon_polyline.rb +29 -0
- data/test/hexapdf/type/annotations/test_widget.rb +35 -0
- metadata +16 -2
@@ -63,6 +63,16 @@ module HexaPDF
|
|
63
63
|
def use_glyph(glyph_id)
|
64
64
|
return @glyph_map[glyph_id] if @glyph_map.key?(glyph_id)
|
65
65
|
@last_id += 1
|
66
|
+
# Handle codes for ASCII characters \r (13), (, ) (40, 41) and \ (92) specially so that
|
67
|
+
# they never appear in the output (PDF serialization would need to escape them)
|
68
|
+
if @last_id == 13 || @last_id == 40 || @last_id == 92
|
69
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
70
|
+
if @last_id == 40
|
71
|
+
@last_id += 1
|
72
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
73
|
+
end
|
74
|
+
@last_id += 1
|
75
|
+
end
|
66
76
|
@glyph_map[glyph_id] = @last_id
|
67
77
|
end
|
68
78
|
|
@@ -107,7 +117,7 @@ module HexaPDF
|
|
107
117
|
locations = []
|
108
118
|
|
109
119
|
@glyph_map.each_key do |old_gid|
|
110
|
-
glyph = orig_glyf[old_gid]
|
120
|
+
glyph = orig_glyf[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
111
121
|
locations << table.size
|
112
122
|
data = glyph.raw_data
|
113
123
|
if glyph.compound?
|
@@ -166,7 +176,10 @@ module HexaPDF
|
|
166
176
|
# Adds the components of compound glyphs to the subset.
|
167
177
|
def add_glyph_components
|
168
178
|
glyf = @font[:glyf]
|
169
|
-
@glyph_map.keys.each
|
179
|
+
@glyph_map.keys.each do |gid|
|
180
|
+
next if gid.kind_of?(Symbol)
|
181
|
+
glyf[gid].components&.each {|cgid| use_glyph(cgid) }
|
182
|
+
end
|
170
183
|
end
|
171
184
|
|
172
185
|
end
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -44,21 +44,22 @@ module HexaPDF
|
|
44
44
|
# under the :container name.
|
45
45
|
#
|
46
46
|
# The box does not support the value :flow for the style property position, so the child boxes
|
47
|
-
# are laid out in the current region only.
|
48
|
-
# box doesn't fit, the whole container doesn't fit. Splitting the container is also not possible
|
49
|
-
# for the same reason.
|
47
|
+
# are laid out in the current region only.
|
50
48
|
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
49
|
+
# If #splitable is +false+ (the default) and if any box doesn't fit, the whole container doesn't
|
50
|
+
# fit.
|
51
|
+
#
|
52
|
+
# By default the child boxes are laid out from top to bottom. By appropriately setting the style
|
53
|
+
# properties 'mask_mode', 'align' and 'valign', it is possible to lay out the children bottom to
|
54
|
+
# top, left to right, or right to left:
|
54
55
|
#
|
55
56
|
# * The standard top-to-bottom layout:
|
56
57
|
#
|
57
58
|
# #>pdf-composer100
|
58
59
|
# composer.container do |container|
|
59
|
-
# container.box(
|
60
|
-
# container.box(
|
61
|
-
# container.box(
|
60
|
+
# container.box(height: 20, style: {background_color: "hp-blue-dark"})
|
61
|
+
# container.box(height: 20, style: {background_color: "hp-blue"})
|
62
|
+
# container.box(height: 20, style: {background_color: "hp-blue-light"})
|
62
63
|
# end
|
63
64
|
#
|
64
65
|
# * The bottom-to-top layout (using valign = :bottom to fill up from the bottom and mask_mode =
|
@@ -66,12 +67,12 @@ module HexaPDF
|
|
66
67
|
#
|
67
68
|
# #>pdf-composer100
|
68
69
|
# composer.container do |container|
|
69
|
-
# container.box(
|
70
|
-
#
|
71
|
-
# container.box(
|
72
|
-
#
|
73
|
-
# container.box(
|
74
|
-
#
|
70
|
+
# container.box(height: 20, style: {background_color: "hp-blue-dark",
|
71
|
+
# mask_mode: :fill_horizontal, valign: :bottom})
|
72
|
+
# container.box(height: 20, style: {background_color: "hp-blue",
|
73
|
+
# mask_mode: :fill_horizontal, valign: :bottom})
|
74
|
+
# container.box(height: 20, style: {background_color: "hp-blue-light",
|
75
|
+
# mask_mode: :fill_horizontal, valign: :bottom})
|
75
76
|
# end
|
76
77
|
#
|
77
78
|
# * The left-to-right layout (using mask_mode = :fill_vertical to fill the area to the top and
|
@@ -79,12 +80,12 @@ module HexaPDF
|
|
79
80
|
#
|
80
81
|
# #>pdf-composer100
|
81
82
|
# composer.container do |container|
|
82
|
-
# container.box(
|
83
|
-
#
|
84
|
-
# container.box(
|
85
|
-
#
|
86
|
-
# container.box(
|
87
|
-
#
|
83
|
+
# container.box(width: 20, style: {background_color: "hp-blue-dark",
|
84
|
+
# mask_mode: :fill_vertical})
|
85
|
+
# container.box(width: 20, style: {background_color: "hp-blue",
|
86
|
+
# mask_mode: :fill_vertical})
|
87
|
+
# container.box(width: 20, style: {background_color: "hp-blue-light",
|
88
|
+
# mask_mode: :fill_vertical})
|
88
89
|
# end
|
89
90
|
#
|
90
91
|
# * The right-to-left layout (using align = :right to fill up from the right and mask_mode =
|
@@ -92,18 +93,42 @@ module HexaPDF
|
|
92
93
|
#
|
93
94
|
# #>pdf-composer100
|
94
95
|
# composer.container do |container|
|
95
|
-
# container.box(
|
96
|
-
#
|
97
|
-
# container.box(
|
98
|
-
#
|
99
|
-
# container.box(
|
100
|
-
#
|
96
|
+
# container.box(width: 20, style: {background_color: "hp-blue-dark",
|
97
|
+
# mask_mode: :fill_vertical, align: :right})
|
98
|
+
# container.box(width: 20, style: {background_color: "hp-blue",
|
99
|
+
# mask_mode: :fill_vertical, align: :right})
|
100
|
+
# container.box(width: 20, style: {background_color: "hp-blue-light",
|
101
|
+
# mask_mode: :fill_vertical, align: :right})
|
101
102
|
# end
|
102
103
|
class ContainerBox < Box
|
103
104
|
|
104
105
|
# The child boxes of this ContainerBox. They need to be finalized before #fit is called.
|
105
106
|
attr_reader :children
|
106
107
|
|
108
|
+
# Specifies whether the container box allows splitting its content.
|
109
|
+
#
|
110
|
+
# If splitting is not allowed (the default), all child boxes must fit together into one
|
111
|
+
# region.
|
112
|
+
#
|
113
|
+
# Examples:
|
114
|
+
#
|
115
|
+
# # Fails with an error because the content of the container box is too big
|
116
|
+
# composer.column do |col|
|
117
|
+
# col.container do |container|
|
118
|
+
# container.lorem_ipsum
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# ---
|
123
|
+
#
|
124
|
+
# #>pdf-composer
|
125
|
+
# composer.column do |col|
|
126
|
+
# col.container(splitable: true) do |container|
|
127
|
+
# container.lorem_ipsum
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
attr_reader :splitable
|
131
|
+
|
107
132
|
# Creates a new container box, optionally accepting an array of child boxes.
|
108
133
|
#
|
109
134
|
# Example:
|
@@ -117,9 +142,10 @@ module HexaPDF
|
|
117
142
|
# container.text("here", mask_mode: :fill_vertical, valign: :bottom)
|
118
143
|
# end
|
119
144
|
# composer.text("Another paragraph")
|
120
|
-
def initialize(children: [], **kwargs)
|
145
|
+
def initialize(children: [], splitable: false, **kwargs)
|
121
146
|
super(**kwargs)
|
122
147
|
@children = children
|
148
|
+
@splitable = splitable
|
123
149
|
end
|
124
150
|
|
125
151
|
# Returns +true+ if no box was fitted into the container.
|
@@ -144,9 +170,18 @@ module HexaPDF
|
|
144
170
|
end
|
145
171
|
update_content_height { @box_fitter.content_heights.max }
|
146
172
|
fit_result.success!
|
173
|
+
elsif !@box_fitter.fit_results.empty? && @splitable
|
174
|
+
fit_result.overflow!
|
147
175
|
end
|
148
176
|
end
|
149
177
|
|
178
|
+
# Splits the content of the container box. This method is called from Box#split.
|
179
|
+
def split_content
|
180
|
+
box = create_split_box
|
181
|
+
box.instance_variable_set(:@children, @box_fitter.remaining_boxes)
|
182
|
+
[self, box]
|
183
|
+
end
|
184
|
+
|
150
185
|
# Draws the children onto the canvas at position [x, y].
|
151
186
|
def draw_content(canvas, x, y)
|
152
187
|
dx = x - @fit_x
|
data/lib/hexapdf/layout/style.rb
CHANGED
@@ -150,14 +150,15 @@ module HexaPDF
|
|
150
150
|
end
|
151
151
|
|
152
152
|
# :call-seq:
|
153
|
-
# quad.set(value)
|
154
|
-
# quad.set(array)
|
155
|
-
# quad.set(quad
|
153
|
+
# quad.set(value) -> quad
|
154
|
+
# quad.set(array) -> quad
|
155
|
+
# quad.set(hash) -> quad
|
156
|
+
# quad.set(quad) -> quad
|
156
157
|
#
|
157
|
-
# Sets all values of the quad.
|
158
|
+
# Sets all values of the quad and returns it.
|
158
159
|
#
|
159
|
-
# * If a single value is provided that is neither a Quad nor an array, it is
|
160
|
-
# an array with one value was given.
|
160
|
+
# * If a single value is provided that is neither a Quad nor an array nor a hash, it is
|
161
|
+
# handled as if an array with one value was given.
|
161
162
|
#
|
162
163
|
# * If a Quad is provided, its values are used.
|
163
164
|
#
|
@@ -170,6 +171,9 @@ module HexaPDF
|
|
170
171
|
# third value.
|
171
172
|
# * Four or more values: Top is set to the first, right to the second, bottom to the third
|
172
173
|
# and left to the fourth value.
|
174
|
+
#
|
175
|
+
# * If a hash is provided, the keys +:top+, +:bottom+, +:left+ and +:right+ are used to set
|
176
|
+
# the respective value. All unspecified keys that have not been set before are set to 0.
|
173
177
|
def set(obj)
|
174
178
|
case obj
|
175
179
|
when Quad
|
@@ -182,9 +186,15 @@ module HexaPDF
|
|
182
186
|
@bottom = obj[2] || obj[0]
|
183
187
|
@left = obj[3] || obj[1] || obj[0]
|
184
188
|
@right = obj[1] || obj[0]
|
189
|
+
when Hash
|
190
|
+
@top = obj[:top] || @top || 0
|
191
|
+
@bottom = obj[:bottom] || @bottom || 0
|
192
|
+
@left = obj[:left] || @left || 0
|
193
|
+
@right = obj[:right] || @right || 0
|
185
194
|
else
|
186
195
|
@top = @bottom = @left = @right = obj
|
187
196
|
end
|
197
|
+
self
|
188
198
|
end
|
189
199
|
|
190
200
|
# Returns +true+ if the quad effectively contains only one value.
|
@@ -398,6 +408,9 @@ module HexaPDF
|
|
398
408
|
# bottom-left corner of the box during the drawing operations.
|
399
409
|
class Layers
|
400
410
|
|
411
|
+
# The array holding all raw layer definitions.
|
412
|
+
attr_reader :layers
|
413
|
+
|
401
414
|
# Creates a new Layers object popuplated with the given +layers+.
|
402
415
|
def initialize(layers = nil)
|
403
416
|
@layers = []
|
@@ -603,11 +616,36 @@ module HexaPDF
|
|
603
616
|
# style.update(**properties) -> style
|
604
617
|
#
|
605
618
|
# Updates the style's properties using the key-value pairs specified by the +properties+ hash.
|
619
|
+
#
|
620
|
+
# Also see: #merge
|
606
621
|
def update(**properties)
|
607
622
|
properties.each {|key, value| send(key, value) }
|
608
623
|
self
|
609
624
|
end
|
610
625
|
|
626
|
+
# Yields all set properties.
|
627
|
+
def each_property # :yield: property, value
|
628
|
+
return to_enum(__method__) unless block_given?
|
629
|
+
instance_variables.each do |iv|
|
630
|
+
(val = PROPERTIES[iv]) && yield(val, instance_variable_get(iv))
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
# :call-seq:
|
635
|
+
# style.merge(other_style) -> style
|
636
|
+
#
|
637
|
+
# Merges the set properties of the +other_style+ object into this one.
|
638
|
+
#
|
639
|
+
# Note that merging is done on a per-property basis. So if a complex property is set on
|
640
|
+
# +other_style+ and also on +self+, the +other_style+ value completely overwrites the one from
|
641
|
+
# +self+.
|
642
|
+
#
|
643
|
+
# Also see: #update
|
644
|
+
def merge(other)
|
645
|
+
other.each_property {|property, value| send(property, value) }
|
646
|
+
self
|
647
|
+
end
|
648
|
+
|
611
649
|
##
|
612
650
|
# :method: font
|
613
651
|
# :call-seq:
|
@@ -615,8 +653,9 @@ module HexaPDF
|
|
615
653
|
#
|
616
654
|
# The font to be used, must be set to a valid font wrapper object before it can be used.
|
617
655
|
#
|
618
|
-
# HexaPDF::
|
619
|
-
# to a font wrapper object before doing
|
656
|
+
# HexaPDF::Document::Layout handles this property - together with #font_bold and #font_italic
|
657
|
+
# - specially in that it resolves a set string or array to a font wrapper object before doing
|
658
|
+
# anything else with the style object.
|
620
659
|
#
|
621
660
|
# This is the only style property without a default value!
|
622
661
|
#
|
@@ -633,6 +672,48 @@ module HexaPDF
|
|
633
672
|
# composer.text("Courier Bold", font: "Courier bold")
|
634
673
|
# composer.text("Courier Bold also", font: ["Courier", variant: :bold])
|
635
674
|
|
675
|
+
##
|
676
|
+
# :method: font_bold
|
677
|
+
# :call-seq:
|
678
|
+
# font_bold(bold = false)
|
679
|
+
#
|
680
|
+
# Specifies whether the bold variant of the font is used.
|
681
|
+
#
|
682
|
+
# Note that this property only has affect if #font is not already set to a font wrapper
|
683
|
+
# object and if it is set explicitly (i.e. #font_bold? returns +true+).
|
684
|
+
#
|
685
|
+
# See #font, #font_italic
|
686
|
+
#
|
687
|
+
# Examples:
|
688
|
+
#
|
689
|
+
# #>pdf-composer100
|
690
|
+
# composer.text("Helvetica bold", font: "Helvetica", font_bold: true)
|
691
|
+
#
|
692
|
+
# helvetica_bold = composer.document.fonts.add("Helvetica", variant: :bold)
|
693
|
+
# composer.text("Helvetica bold", font: helvetica_bold, font_bold: false)
|
694
|
+
# composer.text("Helvetica", font: ["Helvetica", {variant: :bold}], font_bold: false)
|
695
|
+
|
696
|
+
##
|
697
|
+
# :method: font_italic
|
698
|
+
# :call-seq:
|
699
|
+
# font_italic(bold = false)
|
700
|
+
#
|
701
|
+
# Specifies whether the italic variant of the font is used.
|
702
|
+
#
|
703
|
+
# Note that this property only has affect if #font is not already set to a font wrapper
|
704
|
+
# object and if it is set explicitly (i.e. #font_italic? returns +true+).
|
705
|
+
#
|
706
|
+
# See #font, #font_bold.
|
707
|
+
#
|
708
|
+
# Examples:
|
709
|
+
#
|
710
|
+
# #>pdf-composer100
|
711
|
+
# composer.text("Helvetica italic", font: "Helvetica", font_italic: true)
|
712
|
+
#
|
713
|
+
# helvetica_bold = composer.document.fonts.add("Helvetica", variant: :italic)
|
714
|
+
# composer.text("Helvetica italic", font: helvetica_bold, font_italic: false)
|
715
|
+
# composer.text("Helvetica", font: ["Helvetica", {variant: :italic}], font_italic: false)
|
716
|
+
|
636
717
|
##
|
637
718
|
# :method: font_size
|
638
719
|
# :call-seq:
|
@@ -1021,7 +1102,7 @@ module HexaPDF
|
|
1021
1102
|
#
|
1022
1103
|
# This method can set the line spacing in two ways:
|
1023
1104
|
#
|
1024
|
-
# * Using
|
1105
|
+
# * Using the positional, mandatory argument +type+ and the optional +value+.
|
1025
1106
|
# * Or a hash with the keys +type+ and +value+.
|
1026
1107
|
#
|
1027
1108
|
# Note that the last line has no additional spacing after it by default. Set #last_line_gap
|
@@ -1422,8 +1503,33 @@ module HexaPDF
|
|
1422
1503
|
# composer.text("This is some longer text that does not appear in two lines.",
|
1423
1504
|
# height: 15, overflow: :truncate)
|
1424
1505
|
|
1425
|
-
|
1506
|
+
##
|
1507
|
+
# :method: box_options
|
1508
|
+
# :call-seq:
|
1509
|
+
# box_options(**options)
|
1510
|
+
#
|
1511
|
+
# Contains initialization arguments for the box instance that is created with this
|
1512
|
+
# style. Together with the other style properties this allows the complete specification of a
|
1513
|
+
# box instance just via a Style instance.
|
1514
|
+
#
|
1515
|
+
# Note that this property is only used by the HexaPDF::Document::Layout methods when a box
|
1516
|
+
# instance is created. If a box instance is created directly, this property has no effect.
|
1517
|
+
#
|
1518
|
+
# Examples:
|
1519
|
+
#
|
1520
|
+
# #>pdf-composer100
|
1521
|
+
# composer.style(:my_list, box_options: {marker_type: :decimal, item_spacing: 15})
|
1522
|
+
# composer.list(style: :my_list) do |list|
|
1523
|
+
# list.text("This is some text.")
|
1524
|
+
# list.text("This is some other text.")
|
1525
|
+
# end
|
1526
|
+
|
1527
|
+
|
1528
|
+
# :nodoc:
|
1529
|
+
PROPERTIES = [
|
1426
1530
|
[:font, "raise HexaPDF::Error, 'No font set'"],
|
1531
|
+
[:font_bold, false],
|
1532
|
+
[:font_italic, false],
|
1427
1533
|
[:font_size, 10],
|
1428
1534
|
[:line_height, nil],
|
1429
1535
|
[:character_spacing, 0],
|
@@ -1434,10 +1540,10 @@ module HexaPDF
|
|
1434
1540
|
[:text_rendering_mode, "Content::TextRenderingMode::FILL",
|
1435
1541
|
{setter: "Content::TextRenderingMode.normalize(value)"}],
|
1436
1542
|
[:subscript, false,
|
1437
|
-
{setter: "value; superscript(false) if superscript",
|
1543
|
+
{setter: "value; superscript(false) if value && superscript? && superscript",
|
1438
1544
|
valid_values: [true, false]}],
|
1439
1545
|
[:superscript, false,
|
1440
|
-
{setter: "value; subscript(false) if subscript",
|
1546
|
+
{setter: "value; subscript(false) if value && subscript? && subscript",
|
1441
1547
|
valid_values: [true, false]}],
|
1442
1548
|
[:underline, false, {valid_values: [true, false]}],
|
1443
1549
|
[:strikeout, false, {valid_values: [true, false]}],
|
@@ -1457,15 +1563,17 @@ module HexaPDF
|
|
1457
1563
|
[:text_valign, :top, {valid_values: [:top, :center, :bottom]}],
|
1458
1564
|
[:text_indent, 0],
|
1459
1565
|
[:line_spacing, "LineSpacing.new(type: :single)",
|
1460
|
-
{setter: "LineSpacing.new(**(value.kind_of?(Symbol) || value.kind_of?(Numeric)
|
1461
|
-
"{type: value, value: extra_arg} : value))",
|
1566
|
+
{setter: "LineSpacing.new(**(value.kind_of?(Symbol) || value.kind_of?(Numeric) || " \
|
1567
|
+
"value.kind_of?(LineSpacing) ? {type: value, value: extra_arg} : value))",
|
1462
1568
|
extra_args: ", extra_arg = nil"}],
|
1463
1569
|
[:last_line_gap, false, {valid_values: [true, false]}],
|
1464
1570
|
[:fill_horizontal, nil],
|
1465
1571
|
[:background_color, nil],
|
1466
1572
|
[:background_alpha, 1],
|
1467
|
-
[:padding, "Quad.new(0)",
|
1468
|
-
|
1573
|
+
[:padding, "Quad.new(0)",
|
1574
|
+
{setter: "value.kind_of?(Hash) && @name ? @name.set(value) : Quad.new(value)"}],
|
1575
|
+
[:margin, "Quad.new(0)",
|
1576
|
+
{setter: "value.kind_of?(Hash) && @name ? @name.set(value) : Quad.new(value)"}],
|
1469
1577
|
[:border, "Border.new", {setter: "Border.new(**value)"}],
|
1470
1578
|
[:overlays, "Layers.new", {setter: "Layers.new(value)"}],
|
1471
1579
|
[:underlays, "Layers.new", {setter: "Layers.new(value)"}],
|
@@ -1475,6 +1583,7 @@ module HexaPDF
|
|
1475
1583
|
[:mask_mode, :default, {valid_values: [:default, :none, :box, :fill_horizontal,
|
1476
1584
|
:fill_frame_horizontal, :fill_vertical, :fill]}],
|
1477
1585
|
[:overflow, :error],
|
1586
|
+
[:box_options, {}],
|
1478
1587
|
].each do |name, default, options = {}|
|
1479
1588
|
default = default.inspect unless default.kind_of?(String)
|
1480
1589
|
setter = options.delete(:setter) || "value"
|
@@ -1500,7 +1609,7 @@ module HexaPDF
|
|
1500
1609
|
end
|
1501
1610
|
EOF
|
1502
1611
|
alias_method("#{name}=", name)
|
1503
|
-
end
|
1612
|
+
end.each_with_object({}) {|arr, hash| hash[:"@#{arr.first}"] = arr.first }
|
1504
1613
|
|
1505
1614
|
##
|
1506
1615
|
# :method: text_segmentation_algorithm
|
@@ -1547,9 +1656,9 @@ module HexaPDF
|
|
1547
1656
|
|
1548
1657
|
# The calculated text rise, taking superscript and subscript into account.
|
1549
1658
|
def calculated_text_rise
|
1550
|
-
if superscript
|
1659
|
+
if superscript? && superscript
|
1551
1660
|
text_rise + font_size * 0.33
|
1552
|
-
elsif subscript
|
1661
|
+
elsif subscript? && subscript
|
1553
1662
|
text_rise - font_size * 0.20
|
1554
1663
|
else
|
1555
1664
|
text_rise
|
@@ -1558,7 +1667,7 @@ module HexaPDF
|
|
1558
1667
|
|
1559
1668
|
# The calculated font size, taking superscript and subscript into account.
|
1560
1669
|
def calculated_font_size
|
1561
|
-
(superscript || subscript ? 0.583 : 1) * font_size
|
1670
|
+
((superscript? && superscript) || (subscript? && subscript) ? 0.583 : 1) * font_size
|
1562
1671
|
end
|
1563
1672
|
|
1564
1673
|
# Returns the correct offset from the baseline for the underline.
|
@@ -126,6 +126,15 @@ module HexaPDF
|
|
126
126
|
# [layout.text('E'), layout.text('F')]]
|
127
127
|
# composer.column(height: 90) {|col| col.table(cells, header: header, footer: footer) }
|
128
128
|
#
|
129
|
+
# While the width of a cell is determined by the #column_widths array, the height is
|
130
|
+
# automatically determined during fitting of the content. However, it is also possible to use a
|
131
|
+
# fixed height (only if the actual content is smaller or equal than it):
|
132
|
+
#
|
133
|
+
# #>pdf-composer
|
134
|
+
# cells = [[{content: layout.text('A'), height: 5}, layout.text('B')],
|
135
|
+
# [{content: layout.text('C'), height: 40}, layout.text('D')]]
|
136
|
+
# composer.table(cells)
|
137
|
+
#
|
129
138
|
# The cells can be styled using a callable object for more complex styling:
|
130
139
|
#
|
131
140
|
# #>pdf-composer
|
@@ -191,13 +200,14 @@ module HexaPDF
|
|
191
200
|
attr_accessor :children
|
192
201
|
|
193
202
|
# Creates a new Cell instance.
|
194
|
-
def initialize(row:, column:, children: nil, row_span: nil, col_span: nil, **kwargs)
|
203
|
+
def initialize(row:, column:, children: nil, min_height: nil, row_span: nil, col_span: nil, **kwargs)
|
195
204
|
super(**kwargs, width: 0, height: 0)
|
196
205
|
@children = children
|
197
206
|
@row = row
|
198
207
|
@column = column
|
199
208
|
@row_span = row_span || 1
|
200
209
|
@col_span = col_span || 1
|
210
|
+
@min_height = min_height
|
201
211
|
style.border.width.set(1) unless style.border?
|
202
212
|
style.border.draw_on_bounds = true
|
203
213
|
style.padding.set(5) unless style.padding?
|
@@ -257,6 +267,11 @@ module HexaPDF
|
|
257
267
|
@fit_results = []
|
258
268
|
fit_result.success!
|
259
269
|
end
|
270
|
+
|
271
|
+
if @min_height && @height < @min_height
|
272
|
+
@height = @preferred_height = @min_height
|
273
|
+
fit_result.failure! if available_height < @height
|
274
|
+
end
|
260
275
|
end
|
261
276
|
|
262
277
|
# Draws the content of the cell.
|
@@ -298,6 +313,8 @@ module HexaPDF
|
|
298
313
|
#
|
299
314
|
# +:col_span+:: An integer specifying the number of columsn this cell should span.
|
300
315
|
#
|
316
|
+
# +:min_height+:: A number specifying the minimum height of the table cell.
|
317
|
+
#
|
301
318
|
# +:properties+:: A hash of properties (see Box#properties) to be set on the cell itself.
|
302
319
|
#
|
303
320
|
# All other key-value pairs are taken to be cell styling information (like
|
@@ -513,11 +530,12 @@ module HexaPDF
|
|
513
530
|
children = content.delete(:content)
|
514
531
|
row_span = content.delete(:row_span)
|
515
532
|
col_span = content.delete(:col_span)
|
533
|
+
min_height = content.delete(:min_height)
|
516
534
|
properties = content.delete(:properties)
|
517
535
|
style = content
|
518
536
|
end
|
519
537
|
cell = Cell.new(children: children, row: row_index, column: col_index,
|
520
|
-
row_span: row_span, col_span: col_span)
|
538
|
+
row_span: row_span, col_span: col_span, min_height: min_height)
|
521
539
|
cell_style_block&.call(cell)
|
522
540
|
cell.style.update(**style) if style
|
523
541
|
cell.properties.update(properties) if properties
|
data/lib/hexapdf/object.rb
CHANGED
@@ -305,8 +305,8 @@ module HexaPDF
|
|
305
305
|
result
|
306
306
|
rescue HexaPDF::Error
|
307
307
|
raise
|
308
|
-
rescue StandardError
|
309
|
-
yield("
|
308
|
+
rescue StandardError => e
|
309
|
+
yield("Unexpected error encountered: #{e.message}", false, self) if block_given?
|
310
310
|
false
|
311
311
|
end
|
312
312
|
|
data/lib/hexapdf/pdf_array.rb
CHANGED
@@ -143,10 +143,32 @@ module HexaPDF
|
|
143
143
|
# array.reject! {|item| block } -> array or nil
|
144
144
|
# array.reject! -> Enumerator
|
145
145
|
#
|
146
|
-
# Deletes all elements from the array for which the block returns +true
|
147
|
-
# done, returns +nil+.
|
146
|
+
# Deletes all elements from the array for which the block returns +true+ and returns +self+. If
|
147
|
+
# no changes were done, returns +nil+.
|
148
148
|
def reject!
|
149
|
-
|
149
|
+
return to_enum(__method__) unless block_given?
|
150
|
+
value.reject! {|item| yield(process_entry(item)) } && self
|
151
|
+
end
|
152
|
+
|
153
|
+
# :call-seq:
|
154
|
+
# array.map! {|item| block } -> array
|
155
|
+
# array.map! -> Enumerator
|
156
|
+
#
|
157
|
+
# Maps all elements from the array in-place to the respective return value of the block+ and
|
158
|
+
# returns +self+.
|
159
|
+
def map!
|
160
|
+
return to_enum(__method__) unless block_given?
|
161
|
+
value.map! {|item| yield(process_entry(item)) }
|
162
|
+
self
|
163
|
+
end
|
164
|
+
|
165
|
+
# :call-seq:
|
166
|
+
# array.compact! -> array or nil
|
167
|
+
#
|
168
|
+
# Removes all +nil+ elements from the array. Returns +self+ if any elements were removed, +nil+
|
169
|
+
# otherwise.
|
170
|
+
def compact!
|
171
|
+
value.compact! && self
|
150
172
|
end
|
151
173
|
|
152
174
|
# :call-seq:
|
data/lib/hexapdf/tokenizer.rb
CHANGED
@@ -278,6 +278,9 @@ module HexaPDF
|
|
278
278
|
|
279
279
|
REFERENCE_RE = /[#{WHITESPACE}]+([+]?\d+)[#{WHITESPACE}]+R#{WHITESPACE_OR_DELIMITER_RE}/ # :nodoc:
|
280
280
|
|
281
|
+
WHITESPACE_OR_DELIMITER_LUT = [] # :nodoc:
|
282
|
+
(WHITESPACE + DELIMITER).each_byte {|x| WHITESPACE_OR_DELIMITER_LUT[x] = true }
|
283
|
+
|
281
284
|
# Parses the number (integer or real) at the current position.
|
282
285
|
#
|
283
286
|
# See: PDF2.0 s7.3.3
|
@@ -285,7 +288,7 @@ module HexaPDF
|
|
285
288
|
prepare_string_scanner(40)
|
286
289
|
pos = self.pos
|
287
290
|
if (tmp = @ss.scan_integer)
|
288
|
-
if @ss.eos? || @ss.
|
291
|
+
if @ss.eos? || WHITESPACE_OR_DELIMITER_LUT[@ss.peek_byte]
|
289
292
|
# Handle object references, see PDF2.0 s7.3.10
|
290
293
|
prepare_string_scanner(10)
|
291
294
|
if @ss.scan(REFERENCE_RE)
|