class HexaPDF::Layout::Line
A Line
describes a line of text and can contain TextFragment
objects or InlineBox
objects.
The items of a line fragment are aligned along the x-axis which coincides with the text baseline. The vertical alignment is determined by the value of the valign method:
- :text_top
-
Align the top of the box with the top of the text of the
Line
. - :text_bottom
-
Align the bottom of the box with the bottom of the text of the
Line
. - :baseline
-
Align the bottom of the box with the baseline of the
Line
. - :top
-
Align the top of the box with the top of the
Line
. - :bottom
-
Align the bottom of the box with the bottom of the
Line
. - :text
-
This is a special alignment value for text fragment objects. The text fragment is aligned on the baseline and its minimum and maximum y-coordinates are used when calculating the line's
text_y_min
andtext_y_max
.This value may be used by other objects if they should be handled similar to text fragments, e.g. graphical representation of characters (think: emoji fonts).
Item Requirements¶ ↑
Each item of a line fragment has to respond to the following methods:
x_min
-
The minimum x-coordinate of the item.
x_max
-
The maximum x-coordinate of the item.
width
-
The width of the item.
- valign
-
The vertical alignment of the item (see above).
- draw(canvas, x, y)
-
Should draw the item onto the canvas at the position (x, y).
If an item has a vertical alignment of :text, it additionally has to respond to the following methods:
y_min
-
The minimum y-coordinate of the item.
y_max
-
The maximum y-coordinate of the item.
Otherwise (i.e. a vertical alignment different from :text), the following method must be implemented:
height
-
The height of the item.
Attributes
The items: TextFragment
and InlineBox
objects
An optional horizontal offset that should be taken into account when positioning the line.
An optional vertical offset that should be taken into account when positioning the line.
For the first line in a paragraph this describes the offset from the top of the box to the baseline of the line. For all other lines it describes the offset from the previous baseline to the baseline of this line.
Public Class Methods
Creates a new Line
object, adding all given items to it.
# File lib/hexapdf/layout/line.rb, line 185 def initialize(items = []) @items = [] items.each {|i| add(i) } @x_offset = 0 @y_offset = 0 end
Public Instance Methods
Adds the given item at the end of the item list.
If both the item and the last item in the item list are TextFragment
objects and they have the same style, they are combined.
Note: The cache is not cleared!
# File lib/hexapdf/layout/line.rb, line 198 def add(item) last = @items.last if last.instance_of?(item.class) && item.kind_of?(TextFragment) && last.style == item.style if last.items.frozen? @items[-1] = last = last.dup last.items = last.items.dup end last.items[last.items.length, 0] = item.items last.clear_cache else @items << item end self end
Clears all cached values.
This method needs to be called if the line's items are changed!
# File lib/hexapdf/layout/line.rb, line 296 def clear_cache @x_max = @y_min = @y_max = @text_y_min = @text_y_max = @width = nil self end
Yields each item together with its horizontal offset from 0 and vertical offset from the baseline.
# File lib/hexapdf/layout/line.rb, line 219 def each x = 0 @items.each do |item| y = case item.valign when :text, :baseline then 0 when :top then y_max - item.height when :text_top then text_y_max - item.height when :text_bottom then text_y_min when :bottom then y_min else raise HexaPDF::Error, "Unknown inline box alignment #{item.valign}" end yield(item, x, y) x += item.width end end
The height of the line fragment.
# File lib/hexapdf/layout/line.rb, line 276 def height y_max - y_min end
Specifies that this line should not be justified if line justification is used.
# File lib/hexapdf/layout/line.rb, line 281 def ignore_justification! @ignore_justification = true end
Returns true
if justification should be ignored for this line.
# File lib/hexapdf/layout/line.rb, line 286 def ignore_justification? defined?(@ignore_justification) && @ignore_justification end
The maximum y-coordinate of any TextFragment
item of the line.
# File lib/hexapdf/layout/line.rb, line 266 def text_y_max @text_y_max ||= calculate_y_dimensions[3] end
The minimum y-coordinate of any TextFragment
item of the line.
# File lib/hexapdf/layout/line.rb, line 254 def text_y_min @text_y_min ||= calculate_y_dimensions[2] end
The width of the line fragment.
# File lib/hexapdf/layout/line.rb, line 271 def width @width ||= @items.sum(&:width) end
The maximum x-coordinate of the whole line.
# File lib/hexapdf/layout/line.rb, line 242 def x_max @x_max ||= width + (items[-1].x_max - items[-1].width) end
The minimum x-coordinate of the whole line.
# File lib/hexapdf/layout/line.rb, line 237 def x_min @items[0].x_min end
The maximum y-coordinate of any item of the line.
It is always greater than or equal to zero.
# File lib/hexapdf/layout/line.rb, line 261 def y_max @y_max ||= calculate_y_dimensions[1] end
The minimum y-coordinate of any item of the line.
It is always lower than or equal to zero.
# File lib/hexapdf/layout/line.rb, line 249 def y_min @y_min ||= calculate_y_dimensions[0] end
Private Instance Methods
Calculates all y-values and returns them as array.
The following algorithm is used for the calculations:
-
Calculate
text_y_min
andtext_y_max
by using only the items with valign :text. -
Calculate the temporary
y_min
by using either the maximum height of all items with valign :text_top subtraced fromtext_y_max
, ortext_y_min
, whichever is smaller.For the temporary
y_max
, use either the maximum height of all items with valign equal to :text_bottom added totext_y_min
, or the maximum height of all items with valign :baseline, ortext_y_max
, whichever is larger. -
Calculate the final
y_min
by using either the maximum height of all items with valign :top subtracted from the temporaryy_min
, or the temporaryy_min
, whichever is smaller.Calculate the final
y_max
by using either the maximum height of all items with valign :bottom added toy_min
, or the temporaryy_max
, whichever is larger.
In certain cases there is no unique solution to the values of y_min
and y_max
, for example, it depends on the order of the calculations in part 3.
# File lib/hexapdf/layout/line.rb, line 327 def calculate_y_dimensions @y_min, @y_max, @text_y_min, @text_y_max = HeightCalculator.new(@items).result end