class PREP::Core::Loop

ループ読み込みクラスの定義

Constants

DIRECTIONS

Attributes

allow_header_split[R]
at_least_one[R]
direction[R]
fixed_times[R]
gap[R]
header_group[R]
header_repeat[R]
height[R]
iterator_group[R]
page_break[R]
point[R]
width[R]

Public Class Methods

new(identifier, values = { }) click to toggle source
Calls superclass method PREP::Core::Drawable::new
# File lib/core/loop.rb, line 35
def initialize(identifier, values = { })
  values = @@default_values.merge(key_string_to_symbol(values))

  super(identifier, values[:layer])

  self.direction = values[:direction]
  @header_group = values[:header]
  @iterator_group = values[:iterator]
  @gap = values[:gap].mm2pixcel
  @footer_group = values[:footer]
  @point = Point.new(values[:x].mm2pixcel, values[:y].mm2pixcel)
  @page_break = values[:page_break]
  @allow_header_split = values[:allow_header_split]
  @fixed_times = values[:fixed_times]
  @at_least_one = !!values[:at_least_one]
  @header_repeat = !!values[:header_repeat]
  @header_rendering_count = 0
end

Public Instance Methods

calculate_region(prep, region, values, stop_on_drawable = nil) click to toggle source

データに依存してサイズが変化する可能性がある

# File lib/core/loop.rb, line 86
def calculate_region(prep, region, values, stop_on_drawable = nil)
  if self === stop_on_drawable
    puts "STOPPING on #{stop_on_drawable.identifier}" if ENV["DEBUG"]
    gets if ENV["DEBUG"]
    raise ReRenderJump.new(region)
  end
  puts "Calculate region for #{self.class}: #{self.identifier} region: #{region}" if ENV["DEBUG"]
  # リージョン補正
  current_region = fit_region(region)
  # 描画計算の初期位置を保持
  @initial_calculate_region = current_region.dup

  # ヘッダの領域サイズを計算
  current_region = calculate_header_region(prep, current_region, values, stop_on_drawable)
  puts "Header:\t#{current_region}" if ENV["DEBUG"]

  # 繰り返し部分の領域サイズを計算
  current_region = calculate_iterator_region(prep, current_region, values, stop_on_drawable)
  puts "Iterator:\t#{current_region}" if ENV["DEBUG"]

  # フッタの領域サイズを計算
  current_region = calculate_footer_region(prep, current_region, values, stop_on_drawable)
  puts "Footer:\t#{current_region}" if ENV["DEBUG"]

  begin
    # 最終的にリージョン補正位置から必要な領域を計算
    # 元のリージョン範囲から最終的に残っているリージョン範囲を減算すればよい
    ret_region = Region.new(0, 0,
                            region.width - current_region.width,
                            region.height - current_region.height)

    # 進行方向じゃない方向に対しての差分は別途取得
    if @direction == DIRECTIONS[:horizontal]
      ret_region.height = @height
    else # if @direction == DIRECTIONS[:vertical]
      ret_region.width = @width
    end

    return ret_region.width, ret_region.height
  rescue RegionWidthOverflowError, RegionHeightOverflowError => e
    # これは負の数を返却してやれば上位では単純な加算しか起きない
    w, h = region.width - current_region.width, region.height - current_region.height
    if @direction == DIRECTIONS[:horizontal]
      h = @height
    else # if @direction == DIRECTIONS[:vertical]
      w = @width
    end

    puts "Inner page break!! width: #{w}, height: #{h}" if ENV["DEBUG"]
    gets if ENV["DEBUG"]

    return w, h
  end
  # 計算を抜けるタイミングで @header_rendering_count をクリア
  @header_rendering_count = 0
end
direction=(d) click to toggle source
# File lib/core/loop.rb, line 54
def direction=(d)
  if DIRECTIONS.values.include?(d)
    @direction = d
  else
    raise "Unknown direction \"#{d}\"."
  end
end
draw(prep, region, values, stop_on_drawable = nil) click to toggle source

実際の描画を実施

# File lib/core/loop.rb, line 157
def draw(prep, region, values, stop_on_drawable = nil)
  if self === stop_on_drawable
    puts "STOPPING on #{stop_on_drawable.identifier}" if ENV["DEBUG"]
    gets if ENV["DEBUG"]
    raise ReRenderJump.new(region)
  end
  STDERR.puts("Draw on #{self.class} #{self.identifier}") if ENV['DEBUG']
  # リージョン補正
  current_region = fit_region(region)
  # 描画計算の初期位置を保持
  @initial_draw_region = current_region.dup

  # 領域に関する情報を取得
  expand_setting = { }
  if !@page_break
    # 描画に先立ち領域拡張設定を実施
    # ループ要素の描画領域を算出(事前計算)
    rewind_current_page(prep) do
      calculate_region(prep, region.dup, values)
    end
    if @direction == DIRECTIONS[:horizontal]
      expand_setting[:height] = @height
    else # if @direction == DIRECTIONS[:vertical]
      expand_setting[:width] = @width
    end
  end
  # ヘッダおよびフッタの子に関してワンタイム設定を実施
  unless @header_group.nil?
    header = prep.group(@header_group)
    header.drawable_items.each do |drawable|
      if Label === drawable || Rectangle === drawable
        if drawable.expand
          drawable.expand_region(expand_setting)
        end
      end
    end
  end
  unless @footer_group.nil?
    footer = prep.group(@footer_group)
    footer.drawable_items.each do |drawable|
      if Label === drawable || Rectangle === drawable
        if drawable.expand
          drawable.expand_region(expand_setting)
        end
      end
    end
  end
  # ヘッダブロック描画
  current_region = draw_header(prep, current_region, values, stop_on_drawable)

  # 繰り返しブロック描画
  current_region = draw_iterator(prep, current_region, values, stop_on_drawable)

  # フッタブロック描画
  current_region = draw_footer(prep, current_region, values, stop_on_drawable)

  # ヘッダの描画回数をクリア
  @header_rendering_count = 0
end
fit_region(region) click to toggle source

リージョン補正

相対位置情報を元にリージョンを補正して返却

# File lib/core/loop.rb, line 146
def fit_region(region)
  current_region = region.dup
  current_region.x += point.x
  current_region.y += point.y
  current_region.width -= point.x
  current_region.height -= point.y

  return current_region
end
generate_sample_dataset(prep) click to toggle source

データセット雛形を生成

# File lib/core/loop.rb, line 63
def generate_sample_dataset(prep)
  dataset = { }

  unless @header_group.nil?
    header = prep.group(@header_group)
    dataset[:header] = header.generate_sample_dataset(prep)
  end
  iterator = prep.group(@iterator_group)
  dataset[:values] =
    [
     iterator.generate_sample_dataset(prep),
     iterator.generate_sample_dataset(prep),
     iterator.generate_sample_dataset(prep),
    ]
  unless @footer_group.nil?
    footer = prep.group(@footer_group)
    dataset[:footer] = footer.generate_sample_dataset(prep)
  end

  return dataset
end

Private Instance Methods

at_least_one_time_iteration?(prep, region, values) click to toggle source

最低一回はイテーレーションループが描画可能であることを確認

# File lib/core/loop.rb, line 304
def at_least_one_time_iteration?(prep, region, values)
  # 現在のページ番号を確保
  page_pos_x, page_pos_y = prep.page_pos_x, prep.page_pos_y
  # 一回分のデータでイテーレーションの領域計算を実施
  calculate_iterator_region(prep, region.dup, { :values => [values[:values].first] })
  # 進行方向へのページ切り替えが発生したかを判定
  if @direction == DIRECTIONS[:horizontal]
    # X 方向へのページ切り替えが発生しているのでイテーレーション一回の描画不可
    return false if page_pos_x != prep.page_pos_x
  else # if @direction == DIRECTIONS[:vertical]
    # Y 方向へのページ切り替えが発生しているのでイテーレーション一回の描画不可
    return false if page_pos_y != prep.page_pos_y
  end
  # ページ切り替えが無いので描画可能
  return true
end
calculate_header_region(prep, region, values, stop_on_drawable = nil) click to toggle source

ヘッダ構成要素の描画領域を計算するためのメソッド

# File lib/core/loop.rb, line 256
def calculate_header_region(prep, region, values, stop_on_drawable = nil)
  # ヘッダー描画回数をインクリメント
  @header_rendering_count += 1
  if !@header_repeat && @header_rendering_count > 1
    # ヘッダー繰り返しなしかつヘッダの描画回数が2回目以降の場合は何もせずに region を返却
    return region
  end
  unless @header_group.nil?
    header = prep.group(@header_group)
    w, h = header.calculate_region(prep, region, values[:header], stop_on_drawable)
    if @direction == DIRECTIONS[:horizontal] # 右方向
      if (has_iterator_group?(prep) && !values[:values].nil? && !values[:values].size.zero?) || has_footer_group?(prep)
        w += @gap
      end
      region.x += w
      region.width -= w
      @height ||= h
      @height = h if @height < h
    else # if @direction == DIRECTIONS[:vertical] # 下方向
      if (has_iterator_group?(prep) && !values[:values].nil? && !values[:values].size.zero?) || has_footer_group?(prep)
        h += @gap
      end
      region.y += h
      region.height -= h
      @width ||= w
      @width = w if @width < w
    end
    # ヘッダ取り残しを許可するかどうかを判定
    unless @allow_header_split
      # イテーレーションが一度も描画できない場合は進行方向に対するページ切り替え例外
      drawabled = rewind_current_page(prep) do # ページ設定巻き戻し用ブロック付きメソッド
        at_least_one_time_iteration?(prep, region, values)
      end
      # 描画不可の場合は強制ページ切り替え
      unless drawabled
        if @direction == DIRECTIONS[:horizontal]
          raise RegionWidthOverflowError.new
        else # if @direction == DIRECTIONS[:vertical]
          raise RegionHeightOverflowError.new
        end
      end
    end
  end

  return region
end
calculate_iterator_region(prep, region, values, stop_on_drawable = nil) click to toggle source

繰返し構成要素の描画領域を計算するためのメソッド

# File lib/core/loop.rb, line 432
def calculate_iterator_region(prep, region, values, stop_on_drawable = nil)
  iterator = prep.group(@iterator_group)
  # 固定繰り返し時のエラー回避用
  if @at_least_one
    values[:values] ||= [nil]
  else
    values[:values] ||= []
  end
  if !@fixed_times.nil? && !(values[:values].size % @fixed_times).zero?
    iterator_times = ((values[:values].size / @fixed_times) + 1) * @fixed_times
  else
    iterator_times = values[:values].size
  end
  iterator_times.times do |index|
    add_gap = false
    iterator_values = values[:values][index]
    iterator_values ||= { }
    begin
      w, h = iterator.calculate_region(prep, region, iterator_values, stop_on_drawable)
      # 描画したので、方向に応じてリージョン補正
      if @direction == DIRECTIONS[:horizontal] # 右方向
        region.x += w
        region.width -= w
        if has_footer_group?(prep) || (values[:values].size > index + 1)
          add_gap = true
          region.x += @gap
          region.width -= @gap
        end
        @height ||= h
        @height = h if @height < h
      else # if @direction == DIRECTIONS[:vertical] # 下方向
        region.y += h
        region.height -= h
        if has_footer_group?(prep) || (values[:values].size > index + 1)
          add_gap = true
          region.y += @gap
          region.height -= @gap
        end
        @width ||= w
        @width = w if @width < w
      end
    rescue RegionWidthOverflowError
      # 幅オーバーフロー時のページ切り替え対応
      if @page_break && @direction == DIRECTIONS[:horizontal]
        next_page_exist = prep.exists_move_to_page?(1, 0)

        page = prep.move_page_to(1, 0)
        if next_page_exist
          region = @initial_calculate_region.dup
        else
          region = prep.page_content_region
          # 過去を再計算
          begin
            prep.group(:content).calculate_region(prep, region, prep.values[:content], self)
          rescue ReRenderJump => e
            region = fit_region(e.region)
          else
            raise "ReRendering Error!!"
          end
        end
        # ヘッダを再計算
        region = calculate_header_region(prep, region, values, stop_on_drawable)
        # リトライ
        if add_gap
          next
        else
          retry
        end
      end
      raise
    rescue RegionHeightOverflowError
      # 高さオーバーフロー時のページ切り替え対応
      if @page_break && @direction == DIRECTIONS[:vertical]
        next_page_exist = prep.exists_move_to_page?(0, 1)

        page = prep.move_page_to(0, 1)
        if next_page_exist
          region = @initial_calculate_region.dup
        else
          region = prep.page_content_region
          # 過去を再計算
          begin
            prep.group(:content).calculate_region(prep, region, prep.values[:content], self)
          rescue ReRenderJump => e
            region = fit_region(e.region)
          else
            raise "ReRendering Error!!"
          end
        end
        # ヘッダを再計算
        region = calculate_header_region(prep, region, values, stop_on_drawable)
        # リトライ
        if add_gap
          next
        else
          retry
        end
      end
      raise
    end
  end

  return region
end
draw_header(prep, region, values, stop_on_drawable = nil) click to toggle source

ヘッダ構成要素を描画するためのメソッド

描画後に新しい region を返却

# File lib/core/loop.rb, line 222
def draw_header(prep, region, values, stop_on_drawable = nil)
  # ヘッダー描画回数をインクリメント
  @header_rendering_count += 1
  if !@header_repeat && @header_rendering_count > 1
    # ヘッダー繰り返しなしかつヘッダの描画回数が2回目以降の場合は何もせずに region を返却
    return region
  end
  unless @header_group.nil?
    header = prep.group(@header_group)
    w, h = rewind_current_page(prep) do
      header.calculate_region(prep, region, values[:header], stop_on_drawable)
    end
    rewind_current_page(prep, self.direction) do
      header.draw(prep, region, values[:header], stop_on_drawable)
    end
    if @direction == DIRECTIONS[:horizontal] # 右方向
      if (has_iterator_group?(prep) && !values[:values].nil? && !values[:values].size.zero?) || has_footer_group?(prep)
        w += @gap
      end
      region.x += w
      region.width -= w
    else # if @direction == DIRECTIONS[:vertical] # 下方向
      if (has_iterator_group?(prep) && !values[:values].nil? && !values[:values].size.zero?) || has_footer_group?(prep)
        h += @gap
      end
      region.y += h
      region.height -= h
    end
  end

  return region
end
draw_iterator(prep, region, values, stop_on_drawable = nil) click to toggle source

繰返し構成要素を描画するためのメソッド

# File lib/core/loop.rb, line 322
def draw_iterator(prep, region, values, stop_on_drawable = nil)
  iterator = prep.group(@iterator_group)
  # 固定繰り返し時のエラー回避用
  if @at_least_one
    values[:values] ||= [nil]
  else
    values[:values] ||= []
  end
  if !@fixed_times.nil? && !(values[:values].size % @fixed_times).zero?
    iterator_times = ((values[:values].size / @fixed_times) + 1) * @fixed_times
  else
    iterator_times = values[:values].size
  end
  iterator_times.times do |index|
    add_gap = false
    iterator_values = values[:values][index]
    iterator_values ||= { }
    begin
      w, h = rewind_current_page(prep) do
        iterator.calculate_region(prep, region, iterator_values, stop_on_drawable)
      end
      rewind_current_page(prep, self.direction) do
        iterator.draw(prep, region, iterator_values, stop_on_drawable)
      end
      # 描画したので、方向に応じてリージョン補正
      if @direction == DIRECTIONS[:horizontal] # 右方向
        region.x += w
        region.width -= w
        if has_footer_group?(prep) || (values[:values].size > index + 1)
          add_gap = true
          region.x += @gap
          region.width -= @gap
        end
        @height ||= h
        @height = h if @height < h
      else # if @direction == DIRECTIONS[:vertical] # 下方向
        region.y += h
        region.height -= h
        if has_footer_group?(prep) || (values[:values].size > index + 1)
          add_gap = true
          region.y += @gap
          region.height -= @gap
        end
        @width ||= w
        @width = w if @width < w
      end
    rescue RegionWidthOverflowError
      # 幅オーバーフロー時のページ切り替え対応
      if @page_break && @direction == DIRECTIONS[:horizontal]
        next_page_exist = prep.exists_move_to_page?(1, 0)

        page = prep.move_page_to(1, 0)
        if next_page_exist
          region = @initial_draw_region.dup
        else
          region = prep.page_content_region
          # 過去を再描画
          begin
            prep.group(:content).draw(prep, region, prep.values[:content], self)
          rescue ReRenderJump => e
            region = fit_region(e.region)
          else
            raise "ReRedering Error!!"
          end
        end
        region = draw_header(prep, region, values, stop_on_drawable)
        # リトライ
        if add_gap
          next
        else
          retry
        end
      end
      raise
    rescue RegionHeightOverflowError
      # 高さオーバーフロー時のページ切り替え対応
      if @page_break && @direction == DIRECTIONS[:vertical]
        next_page_exist = prep.exists_move_to_page?(0, 1)

        page = prep.move_page_to(0, 1)
        if next_page_exist
          region = @initial_draw_region.dup
        else
          region = prep.page_content_region
          # 過去を再描画
          begin
            prep.group(:content).draw(prep, region, prep.values[:content], self)
          rescue ReRenderJump => e
            region = fit_region(e.region)
          else
            raise "ReRendering Error!!"
          end
        end
        # ヘッダを再描画
        region = draw_header(prep, region, values, stop_on_drawable)
        # リトライ
        if add_gap
          next
        else
          retry
        end
      end
      raise
    end
  end

  return region
end
has_header_group?(prep) click to toggle source

ヘッダグループの設定有無を確認

# File lib/core/loop.rb, line 677
def has_header_group?(prep)
  if @header_group.nil?
    return false
  else
    return prep.has_group?(@header_group)
  end
end
has_iterator_group?(prep) click to toggle source

繰返しグループの設定有無を確認

# File lib/core/loop.rb, line 686
def has_iterator_group?(prep)
  if @iterator_group.nil?
    return false
  else
    return prep.has_group?(@iterator_group)
  end
end