lib/prawn/text/formatted/arranger.rb in prawn-2.4.0 vs lib/prawn/text/formatted/arranger.rb in prawn-2.5.0

- old
+ new

@@ -1,33 +1,32 @@ # frozen_string_literal: true -# core/text/formatted/arranger.rb : Implements a data structure for 2-stage -# processing of lines of formatted text -# -# Copyright February 2010, Daniel Nelson. All Rights Reserved. -# -# This is free software. Please see the LICENSE and COPYING files for details. - module Prawn module Text - module Formatted #:nodoc: + module Formatted + # D data structure for 2-stage processing of lines of formatted text. # @private - - class Arranger #:nodoc: + class Arranger + # You're getting this because you're trying to get some information from + # the arranger before it finished processing text. class NotFinalized < StandardError + # @private DEFAULT_MESSAGE = 'Lines must be finalized' + + # @private MESSAGE_WITH_METHOD = 'Lines must be finalized before calling #%<method>s' def initialize(message = DEFAULT_MESSAGE, method: nil) if method && message == DEFAULT_MESSAGE - super format(MESSAGE_WITH_METHOD, method: method) + super(format(MESSAGE_WITH_METHOD, method: method)) else - super message + super(message) end end end + # You're getting this because a font doesn't have a family name. class BadFontFamily < StandardError def initialize(message = 'Bad font family') super end end @@ -48,43 +47,59 @@ @fragments = [] @unconsumed = [] @kerning = options[:kerning] end + # Number of spaces in the text. + # + # @return [Integer] + # @raise [NotFinalized] def space_count unless finalized raise NotFinalized.new(method: 'space_count') end @fragments.reduce(0) do |sum, fragment| sum + fragment.space_count end end + # Line width. + # + # @return [Number] + # @raise [NotFinalized] def line_width unless finalized raise raise NotFinalized.new(method: 'line_width') end @fragments.reduce(0) do |sum, fragment| sum + fragment.width end end + # Line text. + # + # @return [String] + # @raise [NotFinalized] def line unless finalized raise NotFinalized.new(method: 'line') end - @fragments.map do |fragment| - fragment.text.dup.encode(::Encoding::UTF_8) - rescue ::Encoding::InvalidByteSequenceError, - ::Encoding::UndefinedConversionError - fragment.text.dup.force_encoding(::Encoding::UTF_8) - end.join + @fragments.map { |fragment| + begin + fragment.text.dup.encode(::Encoding::UTF_8) + rescue ::Encoding::InvalidByteSequenceError, ::Encoding::UndefinedConversionError + fragment.text.dup.force_encoding(::Encoding::UTF_8) + end + }.join end + # Finish laying out current line. + # + # @return [void] def finalize_line @finalized = true omit_trailing_whitespace_from_line_width @fragments = [] @@ -93,42 +108,56 @@ format_state = hash.dup format_state.delete(:text) fragment = Prawn::Text::Formatted::Fragment.new( text, format_state, - @document + @document, ) @fragments << fragment self.fragment_measurements = fragment self.line_measurement_maximums = fragment end end + # Set new fragment array. + # + # @param array [Array<Hash>] + # @return [void] def format_array=(array) initialize_line @unconsumed = [] array.each do |hash| hash[:text].scan(/[^\n]+|\n/) do |line| @unconsumed << hash.merge(text: line) end end end + # Prepare for new line layout. + # + # @return [void] def initialize_line @finalized = false @max_line_height = 0 @max_descender = 0 @max_ascender = 0 @consumed = [] @fragments = [] end + # Were all fragments processed? + # + # @return [Boolean] def finished? @unconsumed.empty? end + # Get the next unprocessed string. + # + # @return [String, nil] + # @raise [NotFinalized] def next_string if finalized raise NotFinalized.new(method: 'next_string') end @@ -141,18 +170,26 @@ next_unconsumed_hash[:text] end end + # Get the next unprocessed string keeping it in the queue. + # + # @return [String, nil] def preview_next_string next_unconsumed_hash = @unconsumed.first if next_unconsumed_hash next_unconsumed_hash[:text] end end + # Apply color and font settings. + # + # @param fragment [Prawn::Text::Formatted::Fragment] + # @yield + # @return [void] def apply_color_and_font_settings(fragment, &block) if fragment.color original_fill_color = @document.fill_color original_stroke_color = @document.stroke_color @document.fill_color(*fragment.color) @@ -163,10 +200,15 @@ else apply_font_settings(fragment, &block) end end + # Apply font settings. + # + # @param fragment [Prawn::Text::Formatted::Fragment] + # @yield + # @return [void] def apply_font_settings(fragment = nil, &block) if fragment.nil? font = current_format_state[:font] size = current_format_state[:size] character_spacing = current_format_state[:character_spacing] || @@ -183,20 +225,26 @@ @document.character_spacing(character_spacing) do if font || font_style != :normal raise BadFontFamily unless @document.font.family @document.font( - font || @document.font.family, style: font_style + font || @document.font.family, style: font_style, ) do apply_font_size(size, styles, &block) end else apply_font_size(size, styles, &block) end end end + # Update last fragment's text. + # + # @param printed [String] + # @param unprinted [String] + # @param normalized_soft_hyphen [Boolean] + # @return [void] def update_last_string(printed, unprinted, normalized_soft_hyphen = nil) return if printed.nil? if printed.empty? @consumed.pop @@ -212,18 +260,25 @@ end load_previous_format_state if printed.empty? end + # Get the next fragment. + # + # @return [Prawn::Text::Formatted::Fragment] + # @raise [NotFinalized] def retrieve_fragment unless finalized raise NotFinalized, 'Lines must be finalized before fragments can be retrieved' end @fragments.shift end + # Repack remaining fragments. + # + # @return [void] def repack_unretrieved new_unconsumed = [] # rubocop: disable Lint/AssignmentInCondition while fragment = retrieve_fragment # rubocop: enable Lint/AssignmentInCondition @@ -231,10 +286,14 @@ new_unconsumed << fragment.format_state.merge(text: fragment.text) end @unconsumed = new_unconsumed.concat(@unconsumed) end + # Get font variant from fragment styles. + # + # @param styles [Array<Symbol>] + # @return [Symbol] def font_style(styles) styles = Array(styles) if styles.include?(:bold) && styles.include?(:italic) :bold_italic elsif styles.include?(:bold) @@ -275,17 +334,19 @@ end end def subscript?(styles) if styles.nil? then false - else styles.include?(:subscript) + else + styles.include?(:subscript) end end def superscript?(styles) if styles.nil? then false - else styles.include?(:superscript) + else + styles.include?(:superscript) end end def omit_trailing_whitespace_from_line_width @consumed.reverse_each do |hash| @@ -305,29 +366,29 @@ def fragment_measurements=(fragment) apply_font_settings(fragment) do fragment.width = @document.width_of( fragment.text, - kerning: @kerning + kerning: @kerning, ) fragment.line_height = @document.font.height fragment.descender = @document.font.descender fragment.ascender = @document.font.ascender end end def line_measurement_maximums=(fragment) @max_line_height = [ defined?(@max_line_height) && @max_line_height, - fragment.line_height + fragment.line_height, ].compact.max @max_descender = [ defined?(@max_descender) && @max_descender, - fragment.descender + fragment.descender, ].compact.max @max_ascender = [ defined?(@max_ascender) && @max_ascender, - fragment.ascender + fragment.ascender, ].compact.max end end end end