File: column_box.rb

package info (click to toggle)
ruby-prawn 2.5.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,528 kB
  • sloc: ruby: 17,688; sh: 43; makefile: 20
file content (177 lines) | stat: -rw-r--r-- 5,477 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# frozen_string_literal: true

require_relative 'bounding_box'

module Prawn
  class Document # rubocop: disable Style/Documentation
    # @group Experimental API

    # A column box is a bounding box with the additional property that when
    # text flows past the bottom, it will wrap first to another column on the
    # same page, and only flow to the next page when all the columns are
    # filled.
    #
    # `column_box` accepts the same parameters as {bounding_box}, as well as the
    # number of `:columns` and a `:spacer` (in points) between columns. If
    # resetting the top margin is desired on a new page (e.g. to allow for
    # initial page wide column titles) the option `reflow_margins: true` can be
    # set.
    #
    # @overload column_box(point, options = {})
    #   @param point [Array(Number, Number)]
    #   @param options [Hash{Symbol => any}]
    #   @option options :width [Number]
    #     width of the new column box, must be specified.
    #   @option options :height [Number]
    #     height of the new column box, stretchy box if omitted.
    #   @option options :hold_position [Boolean]
    #     whether to put cursor at the bottom of the column box.
    #   @option options :columns [Integer] (3)
    #   @option options :spacer [Number] (font_size)
    #   @option options :reflow_margins [Boolean] (false)
    #   @yieldparam parent_box [BoundingBox] parent bounding box
    #   @return [void]
    def column_box(*args, &block)
      init_column_box(block) do |parent_box|
        map_to_absolute!(args[0])
        @bounding_box = ColumnBox.new(self, parent_box, *args)
      end
    end

    private

    def init_column_box(user_block, options = {})
      parent_box = @bounding_box

      yield(parent_box)

      self.y = @bounding_box.absolute_top
      user_block.call
      self.y = @bounding_box.absolute_bottom unless options[:hold_position]

      @bounding_box = parent_box
    end

    # Implements the necessary functionality to allow {Document#column_box} to
    # work.
    #
    class ColumnBox < BoundingBox
      # @param point [Array(Number, Number)]
      # @param options [Hash{Symbol => any}]
      # @option options :width [Number]
      #   width of the new column box, must be specified.
      # @option options :height [Number]
      #   height of the new column box, stretchy box if omitted.
      # @option options :columns [Integer] (3)
      # @option options :spacer [Number] (font_size)
      # @option options :reflow_margins [Boolean] (false)
      def initialize(document, parent, point, options = {})
        super
        @columns = options[:columns] || 3
        @spacer = options[:spacer] || @document.font_size
        @current_column = 0
        @reflow_margins = options[:reflow_margins]
      end

      # The column width, not the width of the whole box, before left and/or
      # right padding.
      #
      # @return [Number]
      def bare_column_width
        (@width - (@spacer * (@columns - 1))) / @columns
      end

      # The column width after padding. Used to calculate how long a line of
      # text can be.
      #
      # @return [Number]
      def width
        bare_column_width - (@total_left_padding + @total_right_padding)
      end

      # Column width including the spacer.
      #
      # @return [Number]
      def width_of_column
        bare_column_width + @spacer
      end

      # x coordinate of the left edge of the current column.
      #
      # @return [Number]
      def left_side
        absolute_left + (width_of_column * @current_column)
      end

      # Relative position of the left edge of the current column.
      #
      # @return [Number]
      def left
        width_of_column * @current_column
      end

      # x coordinate of the right edge of the current column.
      #
      # @return [Number]
      def right_side
        columns_from_right = @columns - (1 + @current_column)
        absolute_right - (width_of_column * columns_from_right)
      end

      # Relative position of the right edge of the current column.
      #
      # @return [Number]
      def right
        left + width
      end

      # Moves to the next column or starts a new page if currently positioned at
      # the rightmost column.
      #
      # @return [void]
      def move_past_bottom
        @current_column = (@current_column + 1) % @columns
        @document.y = @y
        if @current_column.zero?
          if @reflow_margins
            @y = @parent.absolute_top
          end
          @document.start_new_page
        end
      end

      # Override the padding functions so as not to split the padding amount
      # between all columns on the page.

      # @private
      # @param left_padding [Number]
      # @return [void]
      def add_left_padding(left_padding)
        @total_left_padding += left_padding
        @x += left_padding
      end

      # @private
      # @param left_padding [Number]
      # @return [void]
      def subtract_left_padding(left_padding)
        @total_left_padding -= left_padding
        @x -= left_padding
      end

      # @private
      # @param right_padding [Number]
      # @return [void]
      def add_right_padding(right_padding)
        @total_right_padding += right_padding
      end

      # @private
      # @param right_padding [Number]
      # @return [void]
      def subtract_right_padding(right_padding)
        @total_right_padding -= right_padding
      end
    end
  end
end