Class: HexaPDF::Layout::Box

Inherits:
Object
  • Object
show all
Defined in:
lib/hexapdf/layout/box.rb

Overview

The base class for all layout boxes.

HexaPDF uses the following box model:

  • Each box can specify a width and height. Padding and border are inside, the margin outside of this rectangle.

  • The #content_width and #content_height accessors can be used to get the width and height of the content box without padding and the border.

  • If width or height is set to zero, they are determined automatically during layouting.

Direct Known Subclasses

ImageBox, TextBox

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(width: 0, height: 0, style: nil, &block) ⇒ Box

:call-seq:

Box.new(width: 0, height: 0, style: nil) {|canv, box| block} -> box

Creates a new Box object with the given width and height that uses the provided block when it is asked to draw itself on a canvas (see #draw).

Since the final location of the box is not known beforehand, the drawing operations inside the block should draw inside the rectangle (0, 0, content_width, content_height) - note that the width and height of the box may not be known beforehand.



100
101
102
103
104
105
# File 'lib/hexapdf/layout/box.rb', line 100

def initialize(width: 0, height: 0, style: nil, &block)
  @width = @initial_width = width
  @height = @initial_height = height
  @style = Style.create(style)
  @draw_block = block
end

Instance Attribute Details

#heightObject (readonly)

The height of the box, including padding and/or borders.



77
78
79
# File 'lib/hexapdf/layout/box.rb', line 77

def height
  @height
end

#styleObject (readonly)

The style to be applied.

Only the following properties are used:

  • Style#background_color

  • Style#background_alpha

  • Style#padding

  • Style#border

  • Style#overlays

  • Style#underlays



89
90
91
# File 'lib/hexapdf/layout/box.rb', line 89

def style
  @style
end

#widthObject (readonly)

The width of the box, including padding and/or borders.



74
75
76
# File 'lib/hexapdf/layout/box.rb', line 74

def width
  @width
end

Class Method Details

.create(width: 0, height: 0, content_box: false, style: nil, **style_properties, &block) ⇒ Object

Creates a new Box object, using the provided block as drawing block (see ::new).

If content_box is true, the width and height are taken to mean the content width and height and the style's padding and border are removed from them appropriately.

The style argument defines the Style object (see Style::create for details) for the box. Any additional keyword arguments have to be style properties and are applied to the style object.



62
63
64
65
66
67
68
69
70
71
# File 'lib/hexapdf/layout/box.rb', line 62

def self.create(width: 0, height: 0, content_box: false, style: nil, **style_properties, &block)
  style = Style.create(style).update(**style_properties)
  if content_box
    width += style.padding.left + style.padding.right +
      style.border.width.left + style.border.width.right
    height += style.padding.top + style.padding.bottom +
      style.border.width.top + style.border.width.bottom
  end
  new(width: width, height: height, style: style, &block)
end

Instance Method Details

#content_heightObject

The height of the content box, i.e. without padding and/or borders.



114
115
116
117
# File 'lib/hexapdf/layout/box.rb', line 114

def content_height
  height = @height - reserved_height
  height < 0 ? 0 : height
end

#content_widthObject

The width of the content box, i.e. without padding and/or borders.



108
109
110
111
# File 'lib/hexapdf/layout/box.rb', line 108

def content_width
  width = @width - reserved_width
  width < 0 ? 0 : width
end

#draw(canvas, x, y) ⇒ Object

Draws the content of the box onto the canvas at the position (x, y).

The coordinate system is translated so that the origin is at the bottom left corner of the **content box** during the drawing operations.

The block specified when creating the box is invoked with the canvas and the box as arguments. Subclasses can specify an on-demand drawing method by setting the @draw_block instance variable to nil or a valid block. This is useful to avoid unnecessary set-up operations when the block does nothing.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/hexapdf/layout/box.rb', line 157

def draw(canvas, x, y)
  if style.background_color? && style.background_color
    canvas.save_graphics_state do
      canvas.opacity(fill_alpha: style.background_alpha).
        fill_color(style.background_color).rectangle(x, y, width, height).fill
    end
  end

  style.underlays.draw(canvas, x, y, self) if style.underlays?
  style.border.draw(canvas, x, y, width, height) if style.border?

  cx = x
  cy = y
  (cx += style.padding.left; cy += style.padding.bottom) if style.padding?
  (cx += style.border.width.left; cy += style.border.width.bottom) if style.border?
  draw_content(canvas, cx, cy)

  style.overlays.draw(canvas, x, y, self) if style.overlays?
end

#empty?Boolean

Returns true if no drawing operations are performed.

Returns:

  • (Boolean)


178
179
180
181
182
183
184
# File 'lib/hexapdf/layout/box.rb', line 178

def empty?
  !(@draw_block ||
    (style.background_color? && style.background_color) ||
    (style.underlays? && !style.underlays.none?) ||
    (style.border? && !style.border.none?) ||
    (style.overlays? && !style.overlays.none?))
end

#fit(available_width, available_height, _frame) ⇒ Object

Fits the box into the Frame and returns true if fitting was successful.

The default implementation uses the whole available space for width and height if they were initially set to 0. Otherwise the specified dimensions are used.



123
124
125
126
127
# File 'lib/hexapdf/layout/box.rb', line 123

def fit(available_width, available_height, _frame)
  @width = (@initial_width > 0 ? @initial_width : available_width)
  @height = (@initial_height > 0 ? @initial_height : available_height)
  @width <= available_width && @height <= available_height
end

#split(_available_width, _available_height, _frame) ⇒ Object

Tries to split the box into two, the first of which needs to fit into the available space, and returns the parts as array.

In many cases the first box in the list will be this box, meaning that even when #fit fails, a part of the box may still fit. Note that #fit may not be called if the first box is this box since it is assumed that it is already fitted. If not even a part of this box fits into the available space, nil should be returned as the first array element.

Possible return values:

[self]

The box fully fits into the available space.

[nil, self]

The box can't be split or no part of the box fits into the available space.

[self, new_box]

A part of the box fits and a new box is returned for the rest.

This default implementation provides no splitting functionality.



144
145
146
# File 'lib/hexapdf/layout/box.rb', line 144

def split(_available_width, _available_height, _frame)
  [nil, self]
end