File: fragment.rb

package info (click to toggle)
ruby-prawn 2.3.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 4,380 kB
  • sloc: ruby: 15,820; sh: 43; makefile: 20
file content (257 lines) | stat: -rw-r--r-- 5,897 bytes parent folder | download | duplicates (2)
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# frozen_string_literal: true

# text/formatted/fragment.rb : Implements information about a formatted fragment
#
# Copyright March 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
      # Prawn::Text::Formatted::Fragment is a state store for a formatted text
      # fragment. It does not render anything.
      #
      # @private
      class Fragment
        attr_reader :format_state, :text
        attr_writer :width
        attr_accessor :line_height, :descender, :ascender
        attr_accessor :word_spacing, :left, :baseline

        def initialize(text, format_state, document)
          @format_state = format_state
          @document = document
          @word_spacing = 0

          # keep the original value of "text", so we can reinitialize @text if
          # formatting parameters like text direction are changed
          @original_text = text
          @text = process_text(@original_text)
        end

        def width
          if @word_spacing.zero? then @width
          else @width + @word_spacing * space_count
          end
        end

        def height
          top - bottom
        end

        def subscript?
          styles.include?(:subscript)
        end

        def superscript?
          styles.include?(:superscript)
        end

        def y_offset
          if subscript? then -descender
          elsif superscript? then 0.85 * ascender
          else 0
          end
        end

        def bounding_box
          [left, bottom, right, top]
        end

        def absolute_bounding_box
          box = bounding_box
          box[0] += @document.bounds.absolute_left
          box[2] += @document.bounds.absolute_left
          box[1] += @document.bounds.absolute_bottom
          box[3] += @document.bounds.absolute_bottom
          box
        end

        def underline_points
          y = baseline - 1.25
          [[left, y], [right, y]]
        end

        def strikethrough_points
          y = baseline + ascender * 0.3
          [[left, y], [right, y]]
        end

        def styles
          @format_state[:styles] || []
        end

        def link
          @format_state[:link]
        end

        def anchor
          @format_state[:anchor]
        end

        def local
          @format_state[:local]
        end

        def color
          @format_state[:color]
        end

        def font
          @format_state[:font]
        end

        def size
          @format_state[:size]
        end

        def character_spacing
          @format_state[:character_spacing] ||
            @document.character_spacing
        end

        def direction
          @format_state[:direction]
        end

        def default_direction=(direction)
          unless @format_state[:direction]
            @format_state[:direction] = direction
            @text = process_text(@original_text)
          end
        end

        def include_trailing_white_space!
          @format_state.delete(:exclude_trailing_white_space)
          @text = process_text(@original_text)
        end

        def space_count
          @text.count(' ')
        end

        def callback_objects
          callback = @format_state[:callback]
          if callback.nil?
            []
          elsif callback.is_a?(Array)
            callback
          else
            [callback]
          end
        end

        def right
          left + width
        end

        def top
          baseline + ascender
        end

        def bottom
          baseline - descender
        end

        def top_left
          [left, top]
        end

        def top_right
          [right, top]
        end

        def bottom_right
          [right, bottom]
        end

        def bottom_left
          [left, bottom]
        end

        def absolute_left
          absolute_bounding_box[0]
        end

        def absolute_right
          absolute_bounding_box[2]
        end

        def absolute_top
          absolute_bounding_box[3]
        end

        def absolute_bottom
          absolute_bounding_box[1]
        end

        def absolute_top_left
          [absolute_left, absolute_top]
        end

        def absolute_top_right
          [absolute_right, absolute_top]
        end

        def absolute_bottom_left
          [absolute_left, absolute_bottom]
        end

        def absolute_bottom_right
          [absolute_right, absolute_bottom]
        end

        private

        def process_text(text)
          string = strip_zero_width_spaces(text)

          if exclude_trailing_white_space?
            string = string.rstrip

            if soft_hyphens_need_processing?(string)
              string = process_soft_hyphens(string[0..-2]) + string[-1..-1]
            end
          elsif soft_hyphens_need_processing?(string)
            string = process_soft_hyphens(string)
          end

          if direction == :rtl
            string.reverse
          else
            string
          end
        end

        def exclude_trailing_white_space?
          @format_state[:exclude_trailing_white_space]
        end

        def soft_hyphens_need_processing?(string)
          !string.empty? && normalized_soft_hyphen
        end

        def normalized_soft_hyphen
          @format_state[:normalized_soft_hyphen]
        end

        def process_soft_hyphens(string)
          if string.encoding != normalized_soft_hyphen.encoding
            string.force_encoding(normalized_soft_hyphen.encoding)
          end

          string.gsub(normalized_soft_hyphen, '')
        end

        def strip_zero_width_spaces(string)
          if string.encoding == ::Encoding::UTF_8
            string.gsub(Prawn::Text::ZWSP, '')
          else
            string
          end
        end
      end
    end
  end
end