# encoding: utf-8 # 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]) 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 while !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) 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 while fragment = @arranger.retrieve_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 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 { |s| s.force_encoding(::Encoding::UTF_8) }.join end def word_spacing_for_this_line if @align == :justify && @line_wrap.space_count > 0 && !@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 if @baseline_y == 0 diff = @ascender + @descender else diff = @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