# frozen_string_literal: true
# wrap.rb: Handles text wrapping for for formatted text
#
# Contributed by Daniel Nelson
#
# This is free software. Please see the LICENSE and COPYING files for details.
require_relative 'line_wrap'
require_relative 'arranger'
module Prawn
module Text
module Formatted #:nodoc:
# @private
module Wrap #:nodoc:
def initialize(_array, options)
@line_wrap = Prawn::Text::Formatted::LineWrap.new
@arranger = Prawn::Text::Formatted::Arranger.new(
@document,
kerning: options[:kerning]
)
@disable_wrap_by_char = options[:disable_wrap_by_char]
end
# See the developer documentation for PDF::Core::Text#wrap
#
# Formatted#wrap should set the following variables:
# @line_height::
# the height of the tallest fragment in the last printed line
# @descender::
# the descender height of the tallest fragment in the last
# printed line
# @ascender::
# the ascender heigth of the tallest fragment in the last
# printed line
# @baseline_y::
# the baseline of the current line
# @nothing_printed::
# set to true until something is printed, then false
# @everything_printed::
# set to false until everything printed, then true
#
# Returns any formatted text that was not printed
#
def wrap(array) #:nodoc:
initialize_wrap(array)
stop = false
until stop
# wrap before testing if enough height for this line because the
# height of the highest fragment on this line will be used to
# determine the line height
@line_wrap.wrap_line(
document: @document,
kerning: @kerning,
width: available_width,
arranger: @arranger,
disable_wrap_by_char: @disable_wrap_by_char
)
if enough_height_for_this_line?
move_baseline_down
print_line
else
stop = true
end
stop ||= @single_line || @arranger.finished?
end
@text = @printed_lines.join("\n")
@everything_printed = @arranger.finished?
@arranger.unconsumed
end
private
def print_line
@nothing_printed = false
printed_fragments = []
fragments_this_line = []
word_spacing = word_spacing_for_this_line
@arranger.fragments.each do |fragment|
fragment.word_spacing = word_spacing
if fragment.text == "\n"
printed_fragments << "\n" if @printed_lines.last == ''
break
end
printed_fragments << fragment.text
fragments_this_line << fragment
end
@arranger.fragments.replace []
accumulated_width = 0
fragments_this_line.reverse! if @direction == :rtl
fragments_this_line.each do |fragment_this_line|
fragment_this_line.default_direction = @direction
format_and_draw_fragment(
fragment_this_line,
accumulated_width,
@line_wrap.width,
word_spacing
)
accumulated_width += fragment_this_line.width
end
@printed_lines << printed_fragments.map do |s|
s.dup.force_encoding(::Encoding::UTF_8)
end.join
end
def word_spacing_for_this_line
if @align == :justify && @line_wrap.space_count.positive? &&
!@line_wrap.paragraph_finished?
(available_width - @line_wrap.width) / @line_wrap.space_count
else
0
end
end
def enough_height_for_this_line?
@line_height = @arranger.max_line_height
@descender = @arranger.max_descender
@ascender = @arranger.max_ascender
diff =
if @baseline_y.zero?
@ascender + @descender
else
@descender + @line_height + @leading
end
require_relatived_total_height = @baseline_y.abs + diff
if require_relatived_total_height > @height + 0.0001
# no room for the full height of this line
@arranger.repack_unretrieved
false
else
true
end
end
def initialize_wrap(array)
@text = nil
@arranger.format_array = array
# these values will depend on the maximum value within a given line
@line_height = 0
@descender = 0
@ascender = 0
@baseline_y = 0
@printed_lines = []
@nothing_printed = true
@everything_printed = false
end
def format_and_draw_fragment(fragment, accumulated_width, line_width, word_spacing)
@arranger.apply_color_and_font_settings(fragment) do
draw_fragment(
fragment,
accumulated_width,
line_width,
word_spacing
)
end
end
end
end
end
end