Class: HexaPDF::Layout::ListBox
- Defined in:
- lib/hexapdf/layout/list_box.rb
Overview
A ListBox arranges its children as unordered or ordered list items.
The indentation of the contents from the left (#content_indentation) as well as the marker type of the items (#marker_type) can be specified. Additionally, it is possible to define the start number for ordered lists (#start_number) and the amount of spacing between items (#item_spacing).
If the list box has padding and/or borders specified, they are handled like with any other box. This means they are around all items and their contents and are not used separately for each item.
The following style properties are used (additionally to those used by the parent class):
- Style#position
-
If this is set to :flow, the frames created for the list items will take the shape of the frame into account. This also means that the
available_width
andavailable_height
arguments are ignored.
Defined Under Namespace
Classes: ItemResult
Instance Attribute Summary collapse
-
#children ⇒ Object
readonly
The child boxes of this ListBox.
-
#content_indentation ⇒ Object
readonly
The indentation of the list content in PDF points.
-
#item_spacing ⇒ Object
readonly
The spacing between two consecutive list items.
-
#marker_type ⇒ Object
readonly
The type of list item marker to be rendered before the list item contents.
-
#start_number ⇒ Object
readonly
The start number when using a #marker_type that represents an ordered list.
Attributes inherited from Box
#height, #properties, #style, #width
Instance Method Summary collapse
-
#empty? ⇒ Boolean
Returns
true
if no box was fitted into the list box. -
#fit(available_width, available_height, frame) ⇒ Object
Fits the list box into the current region of the frame.
-
#initialize(children: [], marker_type: :disc, content_indentation: nil, start_number: 1, item_spacing: 0, **kwargs) ⇒ ListBox
constructor
Creates a new ListBox object for the given child boxes in
children
. -
#supports_position_flow? ⇒ Boolean
Returns
true
as the ‘position’ style property value :flow is supported.
Methods inherited from Box
#content_height, #content_width, create, #draw, #split, #split_box?
Constructor Details
#initialize(children: [], marker_type: :disc, content_indentation: nil, start_number: 1, item_spacing: 0, **kwargs) ⇒ ListBox
Creates a new ListBox object for the given child boxes in children
.
166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/hexapdf/layout/list_box.rb', line 166 def initialize(children: [], marker_type: :disc, content_indentation: nil, start_number: 1, item_spacing: 0, **kwargs) super(**kwargs) @children = children @marker_type = marker_type @content_indentation = content_indentation || 2 * style.font_size @start_number = start_number @item_spacing = item_spacing @results = nil @results_item_marker_x = nil end |
Instance Attribute Details
#children ⇒ Object (readonly)
The child boxes of this ListBox. They need to be finalized before #fit is called.
68 69 70 |
# File 'lib/hexapdf/layout/list_box.rb', line 68 def children @children end |
#content_indentation ⇒ Object (readonly)
The indentation of the list content in PDF points. The item marker will be inside this indentation.
The default value is two times the font size.
Example:
#>pdf-composer100
composer.box(:list) {|list| list.lorem_ipsum_box(sentences: 1) }
composer.box(:list, content_indentation: 50) do |list|
list.lorem_ipsum_box(sentences: 1)
end
151 152 153 |
# File 'lib/hexapdf/layout/list_box.rb', line 151 def content_indentation @content_indentation end |
#item_spacing ⇒ Object (readonly)
The spacing between two consecutive list items.
The default value is zero.
Example:
#>pdf-composer
composer.box(:list, item_spacing: 10) do |list|
3.times { list.lorem_ipsum_box(sentences: 1) }
end
163 164 165 |
# File 'lib/hexapdf/layout/list_box.rb', line 163 def item_spacing @item_spacing end |
#marker_type ⇒ Object (readonly)
The type of list item marker to be rendered before the list item contents.
The following values are supported (and :disc is the default):
- :disc
-
Draws a filled disc for the items of the unordered list.
#>pdf-composer100 composer.box(:list, marker_type: :disc) do |list| list.lorem_ipsum_box(sentences: 1) end
- :circle
-
Draws an unfilled circle for the items of the unordered list.
#>pdf-composer100 composer.box(:list, marker_type: :circle) do |list| list.lorem_ipsum_box(sentences: 1) end
- :square
-
Draws a filled square for the items of the unordered list.
#>pdf-composer100 composer.box(:list, marker_type: :square) do |list| list.lorem_ipsum_box(sentences: 1) end
- :decimal
-
Draws the numbers in decimal form, starting from #start_number) for the items of the ordered list.
#>pdf-composer100 composer.box(:list, marker_type: :decimal) do |list| 5.times { list.lorem_ipsum_box(sentences: 1) } end
- custom marker
-
Additionally, it is possible to specify an object as value that responds to #call(document, box, index) where
document
is the HexaPDF::Document,box
is the list box, andindex
is the current item index, starting at 0. The return value needs to be a Box object which is then fit into the content indentation area and drawn.#>pdf-composer100 image = lambda do |document, box, index| document.layout.image_box(machu_picchu, height: box.style.font_size) end composer.box(:list, marker_type: image) do |list| 2.times { list.lorem_ipsum_box(sentences: 1) } end
125 126 127 |
# File 'lib/hexapdf/layout/list_box.rb', line 125 def marker_type @marker_type end |
#start_number ⇒ Object (readonly)
The start number when using a #marker_type that represents an ordered list.
The default value for this is 1.
Example:
#>pdf-composer100
composer.box(:list, marker_type: :decimal, start_number: 3) do |list|
2.times { list.lorem_ipsum_box(sentences: 1) }
end
137 138 139 |
# File 'lib/hexapdf/layout/list_box.rb', line 137 def start_number @start_number end |
Instance Method Details
#empty? ⇒ Boolean
Returns true
if no box was fitted into the list box.
185 186 187 |
# File 'lib/hexapdf/layout/list_box.rb', line 185 def empty? super && (!@results || @results.all? {|result| result.box_fitter.fit_results.empty? }) end |
#fit(available_width, available_height, frame) ⇒ Object
Fits the list box into the current region of the frame.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/hexapdf/layout/list_box.rb', line 190 def fit(available_width, available_height, frame) @width = if @initial_width > 0 @initial_width else (style.position == :flow ? frame.width : available_width) end height = if @initial_height > 0 @initial_height - reserved_height else (style.position == :flow ? frame.y - frame.bottom : available_height) - reserved_height end width = @width - reserved_width left = (style.position == :flow ? frame.left : frame.x) + reserved_width_left top = frame.y - reserved_height_top # The left side of the frame of an item is always indented, regardless of style.position item_frame_left = left + @content_indentation item_frame_width = width - @content_indentation # We can remove the content indentation for a rectangle by just modifying left and width unless style.position == :flow left = item_frame_left width = item_frame_width end @results = [] @children.each_with_index do |child, index| item_result = ItemResult.new shape = Geom2D::Polygon([left, top - height], [left + width, top - height], [left + width, top], [left, top]) if style.position == :flow shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, shape, :intersection) remove_indent_from_frame_shape(shape) unless shape.polygons.empty? end item_frame = Frame.new(item_frame_left, top - height, item_frame_width, height, shape: shape, context: frame.context) if index != 0 || !split_box? || @split_box == :show_first_marker box = item_marker_box(frame.document, index) break unless box.fit(content_indentation, height, nil) item_result.marker = box item_result.marker_pos_x = item_frame.x - content_indentation item_result.height = box.height end box_fitter = BoxFitter.new([item_frame]) Array(child).each {|box| box_fitter.fit(box) } item_result.box_fitter = box_fitter item_result.height = [item_result.height.to_i, box_fitter.content_heights[0]].max @results << item_result top -= item_result.height + item_spacing height -= item_result.height + item_spacing break if !box_fitter.fit_successful? || height <= 0 end @height = @results.sum {|item_result| item_result.height } + (@results.count - 1) * item_spacing + reserved_height @draw_pos_x = frame.x + reserved_width_left @draw_pos_y = frame.y - @height + reserved_height_bottom @fit_successful = @results.all? {|r| r.box_fitter.fit_successful? } && @results.size == @children.size end |
#supports_position_flow? ⇒ Boolean
Returns true
as the ‘position’ style property value :flow is supported.
180 181 182 |
# File 'lib/hexapdf/layout/list_box.rb', line 180 def supports_position_flow? true end |