hexapdf 0.44.0 → 0.46.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 +106 -47
- data/examples/019-acro_form.rb +5 -0
- data/examples/027-composer_optional_content.rb +6 -4
- data/examples/030-pdfa.rb +12 -11
- data/lib/hexapdf/cli/inspect.rb +5 -0
- data/lib/hexapdf/composer.rb +23 -1
- data/lib/hexapdf/configuration.rb +8 -0
- data/lib/hexapdf/content/canvas.rb +3 -3
- data/lib/hexapdf/content/canvas_composer.rb +1 -0
- data/lib/hexapdf/digital_signature/cms_handler.rb +31 -3
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +9 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +5 -1
- data/lib/hexapdf/document/layout.rb +63 -30
- data/lib/hexapdf/document.rb +24 -2
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/importer.rb +15 -5
- data/lib/hexapdf/layout/box.rb +48 -36
- data/lib/hexapdf/layout/column_box.rb +3 -11
- data/lib/hexapdf/layout/container_box.rb +4 -4
- data/lib/hexapdf/layout/frame.rb +7 -6
- data/lib/hexapdf/layout/inline_box.rb +17 -23
- data/lib/hexapdf/layout/list_box.rb +27 -42
- data/lib/hexapdf/layout/page_style.rb +23 -16
- data/lib/hexapdf/layout/style.rb +5 -5
- data/lib/hexapdf/layout/table_box.rb +14 -10
- data/lib/hexapdf/layout/text_box.rb +60 -36
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +7 -8
- data/lib/hexapdf/parser.rb +5 -1
- data/lib/hexapdf/rectangle.rb +4 -4
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/stream.rb +3 -3
- data/lib/hexapdf/tokenizer.rb +3 -2
- data/lib/hexapdf/type/acro_form/button_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/field.rb +8 -0
- data/lib/hexapdf/type/acro_form/form.rb +2 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas_composer.rb +13 -8
- data/test/hexapdf/digital_signature/common.rb +66 -84
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +7 -0
- data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +9 -0
- data/test/hexapdf/digital_signature/test_cms_handler.rb +41 -1
- data/test/hexapdf/digital_signature/test_handler.rb +2 -1
- data/test/hexapdf/document/test_layout.rb +44 -5
- data/test/hexapdf/layout/test_box.rb +23 -5
- data/test/hexapdf/layout/test_frame.rb +21 -2
- data/test/hexapdf/layout/test_inline_box.rb +17 -28
- data/test/hexapdf/layout/test_list_box.rb +8 -8
- data/test/hexapdf/layout/test_page_style.rb +7 -2
- data/test/hexapdf/layout/test_table_box.rb +8 -1
- data/test/hexapdf/layout/test_text_box.rb +51 -29
- data/test/hexapdf/layout/test_text_layouter.rb +0 -3
- data/test/hexapdf/test_composer.rb +14 -5
- data/test/hexapdf/test_document.rb +27 -0
- data/test/hexapdf/test_importer.rb +17 -0
- data/test/hexapdf/test_revisions.rb +54 -41
- data/test/hexapdf/test_serializer.rb +1 -0
- data/test/hexapdf/type/acro_form/test_form.rb +9 -0
- metadata +2 -2
@@ -77,9 +77,9 @@ module HexaPDF
|
|
77
77
|
#
|
78
78
|
# One style property, Layout::Style#font, is handled specially:
|
79
79
|
#
|
80
|
-
# * If no font is set on a style, the font
|
81
|
-
#
|
82
|
-
# valid default value).
|
80
|
+
# * If no font is set on a style, the default font specified via the configuration option
|
81
|
+
# 'font.default' is automatically set because otherwise there would be problems with text
|
82
|
+
# drawing operations (font is the only style property that has no valid default value).
|
83
83
|
#
|
84
84
|
# * Standard style objects only allow font wrapper objects to be set via the Layout::Style#font
|
85
85
|
# method. This class makes usage easier by allowing strings or an array [name, options_hash]
|
@@ -151,7 +151,9 @@ module HexaPDF
|
|
151
151
|
# :nodoc:
|
152
152
|
def method_missing(name, *args, **kwargs, &block)
|
153
153
|
if @layout.box_creation_method?(name)
|
154
|
-
|
154
|
+
box = @layout.send(name, *args, **kwargs, &block)
|
155
|
+
@children << box
|
156
|
+
box
|
155
157
|
else
|
156
158
|
super
|
157
159
|
end
|
@@ -175,9 +177,6 @@ module HexaPDF
|
|
175
177
|
|
176
178
|
end
|
177
179
|
|
178
|
-
# The mapping of style name (a Symbol) to Layout::Style instance.
|
179
|
-
attr_reader :styles
|
180
|
-
|
181
180
|
# Creates a new Layout object for the given PDF document.
|
182
181
|
def initialize(document)
|
183
182
|
@document = document
|
@@ -219,6 +218,21 @@ module HexaPDF
|
|
219
218
|
style
|
220
219
|
end
|
221
220
|
|
221
|
+
# :call-seq:
|
222
|
+
# layout.styles -> styles
|
223
|
+
# layout.styles(**mapping) -> styles
|
224
|
+
#
|
225
|
+
# Returns the mapping of style names to Layout::Style instances. If +mapping+ is provided,
|
226
|
+
# also defines the given styles using #style.
|
227
|
+
#
|
228
|
+
# The argument +mapping+ needs to be a hash mapping a style name (a Symbol) to style
|
229
|
+
# properties. The special key +:base+ can be used to define the base style. For details see
|
230
|
+
# #style.
|
231
|
+
def styles(**mapping)
|
232
|
+
mapping.each {|name, properties| style(name, **properties) } unless mapping.empty?
|
233
|
+
@styles
|
234
|
+
end
|
235
|
+
|
222
236
|
# Creates an inline box for use together with text fragments.
|
223
237
|
#
|
224
238
|
# The +valign+ argument ist used to specify the vertical alignment of the box within the text
|
@@ -342,6 +356,7 @@ module HexaPDF
|
|
342
356
|
width: width, height: height, properties: properties,
|
343
357
|
style: box_style)
|
344
358
|
end
|
359
|
+
alias text text_box
|
345
360
|
|
346
361
|
# Creates a HexaPDF::Layout::TextBox like #text_box but allows parts of the text to be
|
347
362
|
# formatted differently.
|
@@ -444,6 +459,7 @@ module HexaPDF
|
|
444
459
|
box_class_for_name(:text).new(items: data, width: width, height: height,
|
445
460
|
properties: properties, style: box_style)
|
446
461
|
end
|
462
|
+
alias formatted_text formatted_text_box
|
447
463
|
|
448
464
|
# Creates a HexaPDF::Layout::ImageBox for the given image.
|
449
465
|
#
|
@@ -465,6 +481,7 @@ module HexaPDF
|
|
465
481
|
box_class_for_name(:image).new(image: image, width: width, height: height,
|
466
482
|
properties: properties, style: style)
|
467
483
|
end
|
484
|
+
alias image image_box
|
468
485
|
|
469
486
|
# This helper class is used by Layout#table_box to allow specifying the keyword arguments used
|
470
487
|
# when converting cell data to box instances.
|
@@ -483,8 +500,25 @@ module HexaPDF
|
|
483
500
|
@number_of_columns = number_of_columns
|
484
501
|
end
|
485
502
|
|
486
|
-
# Stores the
|
487
|
-
#
|
503
|
+
# Stores the hash +args+ containing styling properties for the cells specified via the given
|
504
|
+
# 0-based rows and columns.
|
505
|
+
#
|
506
|
+
# Rows and columns can either be single numbers, ranges of numbers or stepped ranges (i.e.
|
507
|
+
# Enumerator::ArithmeticSequence instances).
|
508
|
+
#
|
509
|
+
# Examples:
|
510
|
+
#
|
511
|
+
# # Gray background for all cells
|
512
|
+
# args[] = {cell: {background_color: "gray"}}
|
513
|
+
#
|
514
|
+
# # Cell at (2, 3) gets a bigger font size
|
515
|
+
# args[2, 3] = {font_size: 50}
|
516
|
+
#
|
517
|
+
# # First column of every row has bold font
|
518
|
+
# args[0..-1, 0] = {font: 'Helvetica bold'}
|
519
|
+
#
|
520
|
+
# # Every second row has a blue background
|
521
|
+
# args[(0..-1).step(2)] = {cell: {background_color: "blue"}}
|
488
522
|
def []=(rows = 0..-1, cols = 0..-1, args)
|
489
523
|
rows = adjust_range(rows.kind_of?(Integer) ? rows..rows : rows, @number_of_rows)
|
490
524
|
cols = adjust_range(cols.kind_of?(Integer) ? cols..cols : cols, @number_of_columns)
|
@@ -497,7 +531,7 @@ module HexaPDF
|
|
497
531
|
# is merged.
|
498
532
|
def retrieve_arguments_for(row, col)
|
499
533
|
@argument_infos.each_with_object({}) do |arg_info, result|
|
500
|
-
next unless arg_info.rows.
|
534
|
+
next unless arg_info.rows.include?(row) && arg_info.cols.include?(col)
|
501
535
|
if arg_info.args[:cell]
|
502
536
|
arg_info.args[:cell] = (result[:cell] || {}).merge(arg_info.args[:cell])
|
503
537
|
end
|
@@ -510,7 +544,8 @@ module HexaPDF
|
|
510
544
|
# Adjusts the +range+ so that both the begin and the end of the range are zero or positive
|
511
545
|
# integers smaller than +max+.
|
512
546
|
def adjust_range(range, max)
|
513
|
-
(range.begin % max)..(range.end % max)
|
547
|
+
r = (range.begin % max)..(range.end % max)
|
548
|
+
range.kind_of?(Range) ? r : r.step(range.step)
|
514
549
|
end
|
515
550
|
|
516
551
|
end
|
@@ -528,7 +563,8 @@ module HexaPDF
|
|
528
563
|
# Additional arguments for the #text_box invocations can be specified using the optional block
|
529
564
|
# that yields a CellArgumentCollector instance. This allows customization of the text boxes.
|
530
565
|
# By specifying the special key +:cell+ it is also possible to assign style properties to the
|
531
|
-
# cells themselves.
|
566
|
+
# cells themselves, irrespective of the type of content of the cells. See
|
567
|
+
# CellArgumentCollector#[]= for details.
|
532
568
|
#
|
533
569
|
# See HexaPDF::Layout::TableBox::new for details on +column_widths+, +header+, +footer+, and
|
534
570
|
# +cell_style+.
|
@@ -574,6 +610,7 @@ module HexaPDF
|
|
574
610
|
footer: footer, cell_style: cell_style, width: width,
|
575
611
|
height: height, properties: properties, style: style)
|
576
612
|
end
|
613
|
+
alias table table_box
|
577
614
|
|
578
615
|
LOREM_IPSUM = [ # :nodoc:
|
579
616
|
"Lorem ipsum dolor sit amet, con\u{00AD}sectetur adipis\u{00AD}cing elit, sed " \
|
@@ -593,22 +630,13 @@ module HexaPDF
|
|
593
630
|
def lorem_ipsum_box(sentences: 4, count: 1, **text_box_properties)
|
594
631
|
text_box(([LOREM_IPSUM[0, sentences].join(" ")] * count).join("\n\n"), **text_box_properties)
|
595
632
|
end
|
633
|
+
alias lorem_ipsum lorem_ipsum_box
|
596
634
|
|
597
|
-
|
598
|
-
|
599
|
-
# Allows creating boxes using more convenient method names:
|
600
|
-
#
|
601
|
-
# * #text for #text_box
|
602
|
-
# * #formatted_text for #formatted_text_box
|
603
|
-
# * #image for #image_box
|
604
|
-
# * #lorem_ipsum for #lorem_ipsum_box
|
605
|
-
# * The name of a pre-defined box class like #column will invoke #box appropriately. Same if
|
606
|
-
# used with a '_box' suffix.
|
635
|
+
# Allows creating boxes using more convenient method names: The name of a pre-defined box
|
636
|
+
# class like #column will invoke #box appropriately. Same if used with a '_box' suffix.
|
607
637
|
def method_missing(name, *args, **kwargs, &block)
|
608
638
|
name_without_box = name.to_s.sub(/_box$/, '').intern
|
609
|
-
if
|
610
|
-
send("#{name}_box", *args, **kwargs, &block)
|
611
|
-
elsif @document.config['layout.boxes.map'].key?(name_without_box)
|
639
|
+
if @document.config['layout.boxes.map'].key?(name_without_box)
|
612
640
|
box(name_without_box, *args, **kwargs, &block)
|
613
641
|
else
|
614
642
|
super
|
@@ -620,6 +648,8 @@ module HexaPDF
|
|
620
648
|
box_creation_method?(name) || super
|
621
649
|
end
|
622
650
|
|
651
|
+
BOX_METHOD_NAMES = [:text, :formatted_text, :image, :table, :lorem_ipsum] #:nodoc:
|
652
|
+
|
623
653
|
# :nodoc:
|
624
654
|
def box_creation_method?(name)
|
625
655
|
name = name.to_s.sub(/_box$/, '').intern
|
@@ -636,8 +666,8 @@ module HexaPDF
|
|
636
666
|
end
|
637
667
|
end
|
638
668
|
|
639
|
-
# Retrieves the appropriate HexaPDF::Layout::Style object based on the +style+ and
|
640
|
-
# arguments.
|
669
|
+
# Retrieves the appropriate HexaPDF::Layout::Style object based on the +style+ and
|
670
|
+
# +properties+ arguments.
|
641
671
|
#
|
642
672
|
# The +style+ argument specifies the style to retrieve. It can either be a registered style
|
643
673
|
# name (see #style), a hash with style properties or +nil+. In the latter case the registered
|
@@ -646,15 +676,18 @@ module HexaPDF
|
|
646
676
|
# If the +properties+ hash is not empty, the retrieved style is duplicated and the properties
|
647
677
|
# hash is applied to it.
|
648
678
|
#
|
649
|
-
# Finally, a default font (the one from the :base style or otherwise
|
650
|
-
# necessary to ensure that the style object
|
679
|
+
# Finally, a default font (the one from the :base style or otherwise the one set using the
|
680
|
+
# configuration option 'font.default') is set if necessary to ensure that the style object
|
681
|
+
# works in all cases.
|
651
682
|
def retrieve_style(style, properties = nil)
|
652
683
|
if style.kind_of?(Symbol) && !@styles.key?(style)
|
653
684
|
raise HexaPDF::Error, "Style #{style} not defined"
|
654
685
|
end
|
655
686
|
style = HexaPDF::Layout::Style.create(@styles[style] || style || @styles[:base])
|
656
687
|
style = style.dup.update(**properties) unless properties.nil? || properties.empty?
|
657
|
-
|
688
|
+
unless style.font?
|
689
|
+
style.font(@styles[:base].font? && @styles[:base].font || @document.config['font.default'])
|
690
|
+
end
|
658
691
|
unless style.font.respond_to?(:pdf_object)
|
659
692
|
name, options = *style.font
|
660
693
|
style.font(@document.fonts.add(name, **(options || {})))
|
data/lib/hexapdf/document.rb
CHANGED
@@ -278,8 +278,9 @@ module HexaPDF
|
|
278
278
|
# If the same argument is provided in multiple invocations, the import is done only once and
|
279
279
|
# the previously imported object is returned.
|
280
280
|
#
|
281
|
-
# Note: If you first create a PDF document from scratch
|
282
|
-
# into another PDF document, you need to run the
|
281
|
+
# Note: If you first create a PDF document from scratch or if you modify an existing document,
|
282
|
+
# and then want to import objects from it into another PDF document, you need to run the
|
283
|
+
# following on the source document:
|
283
284
|
#
|
284
285
|
# doc.dispatch_message(:complete_objects)
|
285
286
|
# doc.validate
|
@@ -701,6 +702,27 @@ module HexaPDF
|
|
701
702
|
result
|
702
703
|
end
|
703
704
|
|
705
|
+
# Returns an in-memory copy of the PDF document.
|
706
|
+
#
|
707
|
+
# In the context of this method this means that the returned PDF document contains the same PDF
|
708
|
+
# object tree as this document, starting at the trailer. A possibly set encryption is not
|
709
|
+
# transferred to the returned document.
|
710
|
+
#
|
711
|
+
# Note: If this PDF document was created from scratch or if it is an existing document that was
|
712
|
+
# modified, the following commands need to be run on this document beforehand:
|
713
|
+
#
|
714
|
+
# doc.dispatch_message(:complete_objects)
|
715
|
+
# doc.validate
|
716
|
+
#
|
717
|
+
# This ensures that all the necessary PDF structures set-up correctly.
|
718
|
+
def duplicate
|
719
|
+
dest = HexaPDF::Document.new
|
720
|
+
dupped_trailer = HexaPDF::Importer.copy(dest, trailer, allow_all: true)
|
721
|
+
dest.revisions.current.trailer.value.replace(dupped_trailer.value)
|
722
|
+
dest.trailer.delete(:Encrypt)
|
723
|
+
dest
|
724
|
+
end
|
725
|
+
|
704
726
|
# :call-seq:
|
705
727
|
# doc.write(filename, incremental: false, validate: true, update_fields: true, optimize: false)
|
706
728
|
# doc.write(io, incremental: false, validate: true, update_fields: true, optimize: false)
|
@@ -51,7 +51,7 @@ module HexaPDF
|
|
51
51
|
attr_accessor :name
|
52
52
|
|
53
53
|
# Character bounding box as array of four numbers, specifying the x- and y-coordinates of
|
54
|
-
# the bottom
|
54
|
+
# the bottom-left corner and the x- and y-coordinates of the top-right corner.
|
55
55
|
attr_accessor :bbox
|
56
56
|
|
57
57
|
end
|
@@ -63,7 +63,7 @@ module HexaPDF
|
|
63
63
|
attr_accessor :weight
|
64
64
|
|
65
65
|
# The font bounding box as array of four numbers, specifying the x- and y-coordinates of the
|
66
|
-
# bottom
|
66
|
+
# bottom-left corner and the x- and y-coordinates of the top-right corner.
|
67
67
|
attr_accessor :bounding_box
|
68
68
|
|
69
69
|
# The y-value of the top of the capital H (or 0 or nil if the font doesn't contain a capital
|
data/lib/hexapdf/importer.rb
CHANGED
@@ -71,14 +71,18 @@ module HexaPDF
|
|
71
71
|
# Imports the given +object+ (belonging to the +source+ document) by completely copying it and
|
72
72
|
# all referenced objects into the +destination+ object.
|
73
73
|
#
|
74
|
+
# If the +allow_all+ argument is set to +true+, then the usually omitted catalog and page tree
|
75
|
+
# node objects (see the class description for details) are also copied which allows one to make
|
76
|
+
# an in-memory duplicate of a HexaPDF::Document object.
|
77
|
+
#
|
74
78
|
# Specifying +source+ is optionial if it can be determined through +object+.
|
75
79
|
#
|
76
80
|
# After the operation is finished, all state is discarded. This means that another call to this
|
77
81
|
# method for the same object will yield a new - and different - object. This is in contrast to
|
78
82
|
# using ::for together with #import which remembers and returns already imported objects (which
|
79
83
|
# is generally what one wants).
|
80
|
-
def self.copy(destination, object, source: nil)
|
81
|
-
new(NullableWeakRef.new(destination)).import(object, source: source)
|
84
|
+
def self.copy(destination, object, allow_all: false, source: nil)
|
85
|
+
new(NullableWeakRef.new(destination), allow_all: allow_all).import(object, source: source)
|
82
86
|
end
|
83
87
|
|
84
88
|
private_class_method :new
|
@@ -86,9 +90,10 @@ module HexaPDF
|
|
86
90
|
attr_reader :destination #:nodoc:
|
87
91
|
|
88
92
|
# Initializes a new importer that can import objects to the +destination+ document.
|
89
|
-
def initialize(destination)
|
93
|
+
def initialize(destination, allow_all: false)
|
90
94
|
@destination = destination
|
91
95
|
@mapper = {}
|
96
|
+
@allow_all = allow_all
|
92
97
|
end
|
93
98
|
|
94
99
|
SourceWrapper = Struct.new(:source) #:nodoc:
|
@@ -136,7 +141,7 @@ module HexaPDF
|
|
136
141
|
internal_import(wrapper.source.object(object), wrapper)
|
137
142
|
when HexaPDF::Object
|
138
143
|
wrapper.source ||= object.document
|
139
|
-
if object.type == :Catalog || object.type == :Pages
|
144
|
+
if !@allow_all && (object.type == :Catalog || object.type == :Pages)
|
140
145
|
@mapper[object.data] = nil
|
141
146
|
elsif (mapped_object = @mapper[object.data]&.__getobj__) && !mapped_object.null?
|
142
147
|
mapped_object
|
@@ -149,7 +154,12 @@ module HexaPDF
|
|
149
154
|
obj.data.gen = 0
|
150
155
|
@destination.add(obj) if object.indirect?
|
151
156
|
|
152
|
-
|
157
|
+
stream = obj.data.stream
|
158
|
+
if stream.kind_of?(String)
|
159
|
+
obj.data.stream = stream.dup
|
160
|
+
elsif stream&.source.kind_of?(FiberDoubleForString)
|
161
|
+
obj.data.stream = stream.fiber.resume.dup
|
162
|
+
end
|
153
163
|
obj.data.value = duplicate(obj.data.value, wrapper)
|
154
164
|
obj.data.value.update(duplicate(object.copy_inherited_values, wrapper)) if object.type == :Page
|
155
165
|
obj
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -69,6 +69,10 @@ module HexaPDF
|
|
69
69
|
# If the subclass supports the value :flow of the 'position' style property, this method
|
70
70
|
# needs to be overridden to return +true+.
|
71
71
|
#
|
72
|
+
# Additionally, if a box object uses flow positioning, #fit_result.x should be set to the
|
73
|
+
# correct value since Frame#fit can't determine this and uses Frame#left in the absence of a
|
74
|
+
# set value.
|
75
|
+
#
|
72
76
|
# #empty?::
|
73
77
|
# This method should return +true+ if the subclass won't draw anything when #draw is called.
|
74
78
|
#
|
@@ -89,28 +93,13 @@ module HexaPDF
|
|
89
93
|
# This method draws the box specific content and is called from #draw which already handles
|
90
94
|
# things like drawing the border and background. So #draw should usually not be overridden.
|
91
95
|
#
|
92
|
-
# This base class provides various
|
93
|
-
#
|
94
|
-
# +reserved_width+, +reserved_height+::
|
95
|
-
# Returns the width respectively the height of the reserved space inside the box that is
|
96
|
-
# used for the border and padding.
|
97
|
-
#
|
98
|
-
# +reserved_width_left+, +reserved_width_right+, +reserved_height_top+,
|
99
|
-
# +reserved_height_bottom+::
|
100
|
-
# Returns the reserved space inside the box at the specified edge (left, right, top,
|
101
|
-
# bottom).
|
102
|
-
#
|
103
|
-
# +update_content_width+, +update_content_height+::
|
104
|
-
# Takes a block that should return the content width respectively height and sets the box's
|
105
|
-
# width respectively height accordingly.
|
106
|
-
#
|
107
|
-
# +create_split_box+::
|
108
|
-
# Creates a new box based on this one and resets the internal data back to their original
|
109
|
-
# values.
|
96
|
+
# This base class also provides various protected helper methods for use in the above methods:
|
110
97
|
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
98
|
+
# * #reserved_width, #reserved_height
|
99
|
+
# * #reserved_width_left, #reserved_width_right, #reserved_height_top,
|
100
|
+
# #reserved_height_bottom
|
101
|
+
# * #update_content_width, #update_content_height
|
102
|
+
# * #create_split_box
|
114
103
|
class Box
|
115
104
|
|
116
105
|
include HexaPDF::Utils
|
@@ -323,10 +312,12 @@ module HexaPDF
|
|
323
312
|
# current region of the frame, adjusted for this box. The frame itself is provided as third
|
324
313
|
# argument.
|
325
314
|
#
|
326
|
-
#
|
327
|
-
#
|
328
|
-
#
|
329
|
-
#
|
315
|
+
# If the box uses flow positioning, the width is set to the frame's width and the height to
|
316
|
+
# the remaining height in the frame. Otherwise the given available width and height are used
|
317
|
+
# for the width and height if they were initially set to 0. Otherwise the intially specified
|
318
|
+
# dimensions are used. The method returns early if the thus configured box already doesn't
|
319
|
+
# fit. Otherwise, the #fit_content method is called which allows sub-classes to fit their
|
320
|
+
# content.
|
330
321
|
#
|
331
322
|
# The following variables are set that may later be used during splitting or drawing:
|
332
323
|
#
|
@@ -334,10 +325,25 @@ module HexaPDF
|
|
334
325
|
# used to adjust the drawing position in #draw_content if necessary.
|
335
326
|
def fit(available_width, available_height, frame)
|
336
327
|
@fit_result.reset(frame)
|
337
|
-
|
338
|
-
@
|
339
|
-
|
340
|
-
|
328
|
+
position_flow = supports_position_flow? && style.position == :flow
|
329
|
+
@width = if @initial_width > 0
|
330
|
+
@initial_width
|
331
|
+
elsif position_flow
|
332
|
+
frame.width
|
333
|
+
else
|
334
|
+
available_width
|
335
|
+
end
|
336
|
+
@height = if @initial_height > 0
|
337
|
+
@initial_height
|
338
|
+
elsif position_flow
|
339
|
+
frame.y - frame.bottom
|
340
|
+
else
|
341
|
+
available_height
|
342
|
+
end
|
343
|
+
return @fit_result if !position_flow && (float_compare(@width, available_width) > 0 ||
|
344
|
+
float_compare(@height, available_height) > 0 ||
|
345
|
+
@width - reserved_width < 0 ||
|
346
|
+
@height - reserved_height < 0)
|
341
347
|
|
342
348
|
fit_content(available_width, available_height, frame)
|
343
349
|
|
@@ -379,7 +385,7 @@ module HexaPDF
|
|
379
385
|
# system is translated so that the origin is at the bottom left corner of the **content box**.
|
380
386
|
#
|
381
387
|
# Subclasses should not rely on the +@draw_block+ but implement the #draw_content method. The
|
382
|
-
# coordinates passed to it are also modified to represent the bottom
|
388
|
+
# coordinates passed to it are also modified to represent the bottom-left corner of the
|
383
389
|
# content box but the coordinate system is not translated.
|
384
390
|
def draw(canvas, x, y)
|
385
391
|
if @fit_result.overflow? && @initial_height > 0 && style.overflow == :error
|
@@ -417,7 +423,7 @@ module HexaPDF
|
|
417
423
|
(style.overlays? && !style.overlays.none?))
|
418
424
|
end
|
419
425
|
|
420
|
-
|
426
|
+
protected
|
421
427
|
|
422
428
|
# Returns the width that is reserved by the padding and border style properties.
|
423
429
|
def reserved_width
|
@@ -465,12 +471,18 @@ module HexaPDF
|
|
465
471
|
result
|
466
472
|
end
|
467
473
|
|
474
|
+
# :call-seq:
|
475
|
+
# update_content_width { block }
|
476
|
+
#
|
468
477
|
# Updates the width of the box using the content width returned by the block.
|
469
478
|
def update_content_width
|
470
479
|
return if @initial_width > 0
|
471
480
|
@width = yield + reserved_width
|
472
481
|
end
|
473
482
|
|
483
|
+
# :call-seq:
|
484
|
+
# update_content_height { block }
|
485
|
+
#
|
474
486
|
# Updates the height of the box using the content height returned by the block.
|
475
487
|
def update_content_height
|
476
488
|
return if @initial_height > 0
|
@@ -479,13 +491,12 @@ module HexaPDF
|
|
479
491
|
|
480
492
|
# Fits the content of the box and returns whether fitting was successful.
|
481
493
|
#
|
482
|
-
# This is just a stub implementation that sets the #fit_result status to success
|
483
|
-
#
|
484
|
-
# specific behaviour.
|
494
|
+
# This is just a stub implementation that sets the #fit_result status to success. Subclasses
|
495
|
+
# should override it to provide the box specific behaviour.
|
485
496
|
#
|
486
497
|
# See #fit for details.
|
487
498
|
def fit_content(_available_width, _available_height, _frame)
|
488
|
-
fit_result.success!
|
499
|
+
fit_result.success!
|
489
500
|
end
|
490
501
|
|
491
502
|
# Splits the content of the box.
|
@@ -510,7 +521,8 @@ module HexaPDF
|
|
510
521
|
end
|
511
522
|
end
|
512
523
|
|
513
|
-
# Creates a new box based on this one and resets the data back to their original
|
524
|
+
# Creates a new box based on this one and resets the internal data back to their original
|
525
|
+
# values.
|
514
526
|
#
|
515
527
|
# The variable +@split_box+ is set to +split_box_value+ (defaults to +true+) to make the new
|
516
528
|
# box aware that it is a split box. If needed, subclasses can set the variable to other truthy
|
@@ -142,19 +142,11 @@ module HexaPDF
|
|
142
142
|
|
143
143
|
# Fits the column box into the current region of the frame.
|
144
144
|
#
|
145
|
-
def fit_content(
|
145
|
+
def fit_content(_available_width, _available_height, frame)
|
146
146
|
initial_fit_successful = (@equal_height && @columns.size > 1 ? nil : false)
|
147
147
|
tries = 0
|
148
|
-
width =
|
149
|
-
|
150
|
-
else
|
151
|
-
(@initial_width > 0 ? @initial_width : available_width) - reserved_width
|
152
|
-
end
|
153
|
-
height = if style.position == :flow
|
154
|
-
(@initial_height > 0 ? @initial_height : frame.y - frame.bottom) - reserved_height
|
155
|
-
else
|
156
|
-
(@initial_height > 0 ? @initial_height : available_height) - reserved_height
|
157
|
-
end
|
148
|
+
width = @width - reserved_width
|
149
|
+
height = @height - reserved_height
|
158
150
|
|
159
151
|
columns = calculate_columns(width)
|
160
152
|
return if columns.empty?
|
@@ -52,7 +52,7 @@ module HexaPDF
|
|
52
52
|
# setting the style properties 'mask_mode', 'align' and 'valign', it is possible to lay out the
|
53
53
|
# children bottom to top, left to right, or right to left:
|
54
54
|
#
|
55
|
-
# * The standard top
|
55
|
+
# * The standard top-to-bottom layout:
|
56
56
|
#
|
57
57
|
# #>pdf-composer100
|
58
58
|
# composer.container do |container|
|
@@ -61,7 +61,7 @@ module HexaPDF
|
|
61
61
|
# container.box(:base, height: 20, style: {background_color: "hp-blue-light"})
|
62
62
|
# end
|
63
63
|
#
|
64
|
-
# * The bottom
|
64
|
+
# * The bottom-to-top layout (using valign = :bottom to fill up from the bottom and mask_mode =
|
65
65
|
# :fill_horizontal to only remove the area to the left and right of the box):
|
66
66
|
#
|
67
67
|
# #>pdf-composer100
|
@@ -74,7 +74,7 @@ module HexaPDF
|
|
74
74
|
# mask_mode: :fill_horizontal, valign: :bottom})
|
75
75
|
# end
|
76
76
|
#
|
77
|
-
# * The left
|
77
|
+
# * The left-to-right layout (using mask_mode = :fill_vertical to fill the area to the top and
|
78
78
|
# bottom of the box):
|
79
79
|
#
|
80
80
|
# #>pdf-composer100
|
@@ -87,7 +87,7 @@ module HexaPDF
|
|
87
87
|
# mask_mode: :fill_vertical})
|
88
88
|
# end
|
89
89
|
#
|
90
|
-
# * The right
|
90
|
+
# * The right-to-left layout (using align = :right to fill up from the right and mask_mode =
|
91
91
|
# :fill_vertical to fill the area to the top and bottom of the box):
|
92
92
|
#
|
93
93
|
# #>pdf-composer100
|
data/lib/hexapdf/layout/frame.rb
CHANGED
@@ -54,7 +54,7 @@ module HexaPDF
|
|
54
54
|
#
|
55
55
|
# The method #fit is also called for absolutely positioned boxes but since these boxes are not
|
56
56
|
# subject to the normal constraints, the provided available width and height are the width and
|
57
|
-
# height inside the frame to the right and top of the bottom
|
57
|
+
# height inside the frame to the right and top of the bottom-left corner of the box.
|
58
58
|
#
|
59
59
|
# * If the box didn't fit, call #find_next_region to determine the next region for placing the
|
60
60
|
# box. If a new region was found, start over with #fit. Otherwise the frame has no more space
|
@@ -84,10 +84,10 @@ module HexaPDF
|
|
84
84
|
|
85
85
|
include HexaPDF::Utils
|
86
86
|
|
87
|
-
# The x-coordinate of the bottom
|
87
|
+
# The x-coordinate of the bottom-left corner.
|
88
88
|
attr_reader :left
|
89
89
|
|
90
|
-
# The y-coordinate of the bottom
|
90
|
+
# The y-coordinate of the bottom-left corner.
|
91
91
|
attr_reader :bottom
|
92
92
|
|
93
93
|
# The width of the frame.
|
@@ -127,7 +127,7 @@ module HexaPDF
|
|
127
127
|
|
128
128
|
# An array of box objects representing the parent boxes.
|
129
129
|
#
|
130
|
-
# The immediate parent is the last array entry, the top
|
130
|
+
# The immediate parent is the last array entry, the top-most parent the first one. All boxes
|
131
131
|
# that are fitted into this frame have to be child boxes of the immediate parent box.
|
132
132
|
attr_reader :parent_boxes
|
133
133
|
|
@@ -216,6 +216,7 @@ module HexaPDF
|
|
216
216
|
end
|
217
217
|
|
218
218
|
fit_result = box.fit(aw, ah, self)
|
219
|
+
return fit_result if fit_result.failure?
|
219
220
|
|
220
221
|
width = box.width
|
221
222
|
height = box.height
|
@@ -251,7 +252,7 @@ module HexaPDF
|
|
251
252
|
end
|
252
253
|
end
|
253
254
|
when :flow
|
254
|
-
x =
|
255
|
+
x = fit_result.x || left
|
255
256
|
y = @y - height
|
256
257
|
else
|
257
258
|
raise HexaPDF::Error, "Invalid value '#{position}' for style property position"
|
@@ -382,7 +383,7 @@ module HexaPDF
|
|
382
383
|
# Since not all text may start at the top of the frame, the offset argument can be used to
|
383
384
|
# specify a vertical offset from the top of the frame where layouting should start.
|
384
385
|
#
|
385
|
-
# To be compatible with TextLayouter, the top
|
386
|
+
# To be compatible with TextLayouter, the top-left corner of the bounding box of the frame's
|
386
387
|
# shape is the origin of the coordinate system for the width specification, with positive
|
387
388
|
# x-values to the right and positive y-values downwards.
|
388
389
|
#
|