giga-fast-kit 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/giga-fast-kit.gemspec +12 -0
  3. data/prawn-2.5.0/COPYING +2 -0
  4. data/prawn-2.5.0/GPLv2 +339 -0
  5. data/prawn-2.5.0/GPLv3 +674 -0
  6. data/prawn-2.5.0/LICENSE +56 -0
  7. data/prawn-2.5.0/data/fonts/Courier-Bold.afm +342 -0
  8. data/prawn-2.5.0/data/fonts/Courier-BoldOblique.afm +342 -0
  9. data/prawn-2.5.0/data/fonts/Courier-Oblique.afm +342 -0
  10. data/prawn-2.5.0/data/fonts/Courier.afm +342 -0
  11. data/prawn-2.5.0/data/fonts/Helvetica-Bold.afm +2827 -0
  12. data/prawn-2.5.0/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  13. data/prawn-2.5.0/data/fonts/Helvetica-Oblique.afm +3051 -0
  14. data/prawn-2.5.0/data/fonts/Helvetica.afm +3051 -0
  15. data/prawn-2.5.0/data/fonts/MustRead.html +19 -0
  16. data/prawn-2.5.0/data/fonts/Symbol.afm +213 -0
  17. data/prawn-2.5.0/data/fonts/Times-Bold.afm +2588 -0
  18. data/prawn-2.5.0/data/fonts/Times-BoldItalic.afm +2384 -0
  19. data/prawn-2.5.0/data/fonts/Times-Italic.afm +2667 -0
  20. data/prawn-2.5.0/data/fonts/Times-Roman.afm +2419 -0
  21. data/prawn-2.5.0/data/fonts/ZapfDingbats.afm +225 -0
  22. data/prawn-2.5.0/lib/prawn/document/bounding_box.rb +625 -0
  23. data/prawn-2.5.0/lib/prawn/document/column_box.rb +177 -0
  24. data/prawn-2.5.0/lib/prawn/document/internals.rb +76 -0
  25. data/prawn-2.5.0/lib/prawn/document/span.rb +65 -0
  26. data/prawn-2.5.0/lib/prawn/document.rb +846 -0
  27. data/prawn-2.5.0/lib/prawn/encoding.rb +85 -0
  28. data/prawn-2.5.0/lib/prawn/errors.rb +73 -0
  29. data/prawn-2.5.0/lib/prawn/font.rb +567 -0
  30. data/prawn-2.5.0/lib/prawn/font_metric_cache.rb +50 -0
  31. data/prawn-2.5.0/lib/prawn/fonts/afm.rb +332 -0
  32. data/prawn-2.5.0/lib/prawn/fonts/dfont.rb +42 -0
  33. data/prawn-2.5.0/lib/prawn/fonts/otf.rb +14 -0
  34. data/prawn-2.5.0/lib/prawn/fonts/to_unicode_cmap.rb +151 -0
  35. data/prawn-2.5.0/lib/prawn/fonts/ttc.rb +41 -0
  36. data/prawn-2.5.0/lib/prawn/fonts/ttf.rb +615 -0
  37. data/prawn-2.5.0/lib/prawn/fonts.rb +14 -0
  38. data/prawn-2.5.0/lib/prawn/graphics/blend_mode.rb +62 -0
  39. data/prawn-2.5.0/lib/prawn/graphics/cap_style.rb +44 -0
  40. data/prawn-2.5.0/lib/prawn/graphics/color.rb +257 -0
  41. data/prawn-2.5.0/lib/prawn/graphics/dash.rb +123 -0
  42. data/prawn-2.5.0/lib/prawn/graphics/join_style.rb +60 -0
  43. data/prawn-2.5.0/lib/prawn/graphics/patterns.rb +389 -0
  44. data/prawn-2.5.0/lib/prawn/graphics/transformation.rb +172 -0
  45. data/prawn-2.5.0/lib/prawn/graphics/transparency.rb +79 -0
  46. data/prawn-2.5.0/lib/prawn/graphics.rb +791 -0
  47. data/prawn-2.5.0/lib/prawn/grid.rb +449 -0
  48. data/prawn-2.5.0/lib/prawn/image_handler.rb +61 -0
  49. data/prawn-2.5.0/lib/prawn/images/image.rb +48 -0
  50. data/prawn-2.5.0/lib/prawn/images/jpg.rb +121 -0
  51. data/prawn-2.5.0/lib/prawn/images/png.rb +383 -0
  52. data/prawn-2.5.0/lib/prawn/images.rb +195 -0
  53. data/prawn-2.5.0/lib/prawn/measurement_extensions.rb +82 -0
  54. data/prawn-2.5.0/lib/prawn/measurements.rb +129 -0
  55. data/prawn-2.5.0/lib/prawn/outline.rb +316 -0
  56. data/prawn-2.5.0/lib/prawn/repeater.rb +140 -0
  57. data/prawn-2.5.0/lib/prawn/security/arcfour.rb +54 -0
  58. data/prawn-2.5.0/lib/prawn/security.rb +308 -0
  59. data/prawn-2.5.0/lib/prawn/soft_mask.rb +105 -0
  60. data/prawn-2.5.0/lib/prawn/stamp.rb +147 -0
  61. data/prawn-2.5.0/lib/prawn/text/box.rb +192 -0
  62. data/prawn-2.5.0/lib/prawn/text/formatted/arranger.rb +396 -0
  63. data/prawn-2.5.0/lib/prawn/text/formatted/box.rb +725 -0
  64. data/prawn-2.5.0/lib/prawn/text/formatted/fragment.rb +373 -0
  65. data/prawn-2.5.0/lib/prawn/text/formatted/line_wrap.rb +335 -0
  66. data/prawn-2.5.0/lib/prawn/text/formatted/parser.rb +284 -0
  67. data/prawn-2.5.0/lib/prawn/text/formatted/wrap.rb +166 -0
  68. data/prawn-2.5.0/lib/prawn/text/formatted.rb +82 -0
  69. data/prawn-2.5.0/lib/prawn/text.rb +701 -0
  70. data/prawn-2.5.0/lib/prawn/transformation_stack.rb +63 -0
  71. data/prawn-2.5.0/lib/prawn/utilities.rb +34 -0
  72. data/prawn-2.5.0/lib/prawn/version.rb +6 -0
  73. data/prawn-2.5.0/lib/prawn/view.rb +116 -0
  74. data/prawn-2.5.0/lib/prawn.rb +83 -0
  75. metadata +114 -0
@@ -0,0 +1,725 @@
1
+ # frozen_string_literal: true
2
+
3
+ # text/formatted/rectangle.rb : Implements text boxes with formatted text
4
+ #
5
+ # Copyright February 2010, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+ module Text
12
+ module Formatted
13
+ # Formatted text box.
14
+ #
15
+ # Generally, one would use the {Prawn::Text::Formatted#formatted_text_box}
16
+ # convenience method. However, using `Text::Formatted::Box.new` in
17
+ # conjunction with `#render(dry_run: true)` enables one to do calculations
18
+ # prior to placing text on the page, or to determine how much vertical
19
+ # space was consumed by the printed text
20
+ class Box
21
+ include Prawn::Text::Formatted::Wrap
22
+
23
+ # @group Experimental API
24
+
25
+ # The text that was successfully printed (or, if `:dry_run` was
26
+ # used, the text that would have been successfully printed).
27
+ # @return [Array<Hash>]
28
+ attr_reader :text
29
+
30
+ # True if nothing printed (or, if `:dry_run` was used, nothing would
31
+ # have been successfully printed).
32
+ #
33
+ # @return [Boolean]
34
+ def nothing_printed?
35
+ @nothing_printed
36
+ end
37
+
38
+ # True if everything printed (or, if `:dry_run` was used, everything
39
+ # would have been successfully printed).
40
+ #
41
+ # @return [Boolean]
42
+ def everything_printed?
43
+ @everything_printed
44
+ end
45
+
46
+ # The upper left corner of the text box.
47
+ # @return [Array(Number, Number)]
48
+ attr_reader :at
49
+
50
+ # The line height of the last line printed.
51
+ # @return [Number]
52
+ attr_reader :line_height
53
+
54
+ # The height of the ascender of the last line printed.
55
+ # @return [Number]
56
+ attr_reader :ascender
57
+
58
+ # The height of the descender of the last line printed.
59
+ # @return [Number]
60
+ attr_reader :descender
61
+
62
+ # The leading used during printing.
63
+ # @return [Number]
64
+ attr_reader :leading
65
+
66
+ # Gap between adjacent lines of text.
67
+ #
68
+ # @return [Number]
69
+ def line_gap
70
+ line_height - (ascender + descender)
71
+ end
72
+
73
+ # See Prawn::Text#text_box for valid options
74
+ #
75
+ # @param formatted_text [Array<Hash{Symbol => any}>]
76
+ # Formatted text is an array of hashes, where each hash defines text
77
+ # and format information. The following hash options are supported:
78
+ #
79
+ # - `:text` --- the text to format according to the other hash
80
+ # options.
81
+ # - `:styles` --- an array of styles to apply to this text. Available
82
+ # styles include `:bold`, `:italic`, `:underline`, `:strikethrough`,
83
+ # `:subscript`, and `:superscript`.
84
+ # - `:size` ---a number denoting the font size to apply to this text.
85
+ # - `:character_spacing` --- a number denoting how much to increase or
86
+ # decrease the default spacing between characters.
87
+ # - `:font` --- the name of a font. The name must be an AFM font with
88
+ # the desired faces or must be a font that is already registered
89
+ # using {Prawn::Document#font_families}.
90
+ # - `:color` --- anything compatible with
91
+ # {Prawn::Graphics::Color#fill_color} and
92
+ # {Prawn::Graphics::Color#stroke_color}.
93
+ # - :link` --- a URL to which to create a link. A clickable link will
94
+ # be created to that URL. Note that you must explicitly underline
95
+ # and color using the appropriate tags if you which to draw
96
+ # attention to the link.
97
+ # - `:anchor` --- a destination that has already been or will be
98
+ # registered using `PDF::Core::Destinations#add_dest`. A clickable
99
+ # link will be created to that destination. Note that you must
100
+ # explicitly underline and color using the appropriate tags if you
101
+ # which to draw attention to the link.
102
+ # - `:local` --- a file or application to be opened locally.
103
+ # A clickable link will be created to the provided local file or
104
+ # application. If the file is another PDF, it will be opened in
105
+ # a new window. Note that you must explicitly underline and color
106
+ # using the appropriate options if you which to draw attention to
107
+ # the link.
108
+ # - `:draw_text_callback` --- if provided, this Proc will be called
109
+ # instead of {Prawn::Document#draw_text!} once per fragment for
110
+ # every low-level addition of text to the page.
111
+ # - `:callback` --- an object (or array of such objects) with two
112
+ # methods: `render_behind` and `render_in_front`, which are called
113
+ # immediately prior to and immediately after rendering the text
114
+ # fragment and which are passed the fragment as an argument.
115
+ # @param options [Hash{Symbol => any}]
116
+ # @option options :document [Prawn::Document] Owning document.
117
+ # @option options :kerning [Boolean]
118
+ # (value of document.default_kerning?)
119
+ # Whether or not to use kerning (if it is available with the current
120
+ # font).
121
+ # @option options :size [Number] (current font size)
122
+ # The font size to use.
123
+ # @option options :character_spacing [Number] (0)
124
+ # The amount of space to add to or remove from the default character
125
+ # spacing.
126
+ # @option options :disable_wrap_by_char [Boolean] (false)
127
+ # Whether or not to prevent mid-word breaks when text does not fit in
128
+ # box.
129
+ # @option options :mode [Symbol] (:fill)
130
+ # The text rendering mode. See documentation for
131
+ # {Prawn::Document#text_rendering_mode} for a list of valid options.
132
+ # @option option :style [Symbol] (current style)
133
+ # The style to use. The requested style must be part of the current
134
+ # font family.
135
+ # @option option :at [Array(Number, Number)] (bounds top left corner)
136
+ # The upper left corner of the box.
137
+ # @option options :width [Number] (bounds.right - at[0])
138
+ # The width of the box.
139
+ # @option options :height [Number] (default_height())
140
+ # The height of the box.
141
+ # @option options :direction [:ltr, :rtl]
142
+ # (value of document.text_direction)
143
+ # Direction of the text (left-to-right or right-to-left).
144
+ # @option options :fallback_fonts [Array<String>]
145
+ # An array of font names. Each name must be the name of an AFM font or
146
+ # the name that was used to register a family of external fonts (see
147
+ # {Prawn::Document#font_families}). If present, then each glyph will
148
+ # be rendered using the first font that includes the glyph, starting
149
+ # with the current font and then moving through `:fallback_fonts`.
150
+ # @option options :align [:left, :center, :right, :justify]
151
+ # (:left if direction is :ltr, :right if direction is :rtl)
152
+ # Alignment within the bounding box.
153
+ # @option options :valign [:top, :center, :bottom] (:top)
154
+ # Vertical alignment within the bounding box.
155
+ # @option options :rotate [Number]
156
+ # The angle to rotate the text.
157
+ # @option options :rotate_around
158
+ # [:center, :upper_left, :upper_right, :lower_right, :lower_left]
159
+ # (:upper_left)
160
+ # The point around which to rotate the text.
161
+ # @option options :leading [Number] (value of document.default_leading)
162
+ # Additional space between lines.
163
+ # @option options :single_line [Boolean] (false)
164
+ # If true, then only the first line will be drawn.
165
+ # @option options :overflow [:truncate, :shrink_to_fit, :expand]
166
+ # (:truncate)
167
+ # This controls the behavior when the amount of text exceeds the
168
+ # available space.
169
+ # @option options :min_font_size [Number] (5)
170
+ # The minimum font size to use when `:overflow` is set to
171
+ # `:shrink_to_fit` (that is the font size will not be reduced to less
172
+ # than this value, even if it means that some text will be cut off).
173
+ def initialize(formatted_text, options = {})
174
+ @inked = false
175
+ Prawn.verify_options(valid_options, options)
176
+ options = options.dup
177
+
178
+ self.class.extensions.reverse_each { |e| extend(e) }
179
+
180
+ @overflow = options[:overflow] || :truncate
181
+ @disable_wrap_by_char = options[:disable_wrap_by_char]
182
+
183
+ self.original_text = formatted_text
184
+ @text = nil
185
+
186
+ @document = options[:document]
187
+ @direction = options[:direction] || @document.text_direction
188
+ @fallback_fonts = options[:fallback_fonts] ||
189
+ @document.fallback_fonts
190
+ @at = (
191
+ options[:at] || [@document.bounds.left, @document.bounds.top]
192
+ ).dup
193
+ @width = options[:width] ||
194
+ (@document.bounds.right - @at[0])
195
+ @height = options[:height] || default_height
196
+ @align = options[:align] ||
197
+ (@direction == :rtl ? :right : :left)
198
+ @vertical_align = options[:valign] || :top
199
+ @leading = options[:leading] || @document.default_leading
200
+ @character_spacing = options[:character_spacing] ||
201
+ @document.character_spacing
202
+ @mode = options[:mode] || @document.text_rendering_mode
203
+ @rotate = options[:rotate] || 0
204
+ @rotate_around = options[:rotate_around] || :upper_left
205
+ @single_line = options[:single_line]
206
+ @draw_text_callback = options[:draw_text_callback]
207
+
208
+ # if the text rendering mode is :unknown, force it back to :fill
209
+ if @mode == :unknown
210
+ @mode = :fill
211
+ end
212
+
213
+ if @overflow == :expand
214
+ # if set to expand, then we simply set the bottom
215
+ # as the bottom of the document bounds, since that
216
+ # is the maximum we should expand to
217
+ @height = default_height
218
+ @overflow = :truncate
219
+ end
220
+ @min_font_size = options[:min_font_size] || 5
221
+ if options[:kerning].nil?
222
+ options[:kerning] = @document.default_kerning?
223
+ end
224
+ @options = {
225
+ kerning: options[:kerning],
226
+ size: options[:size],
227
+ style: options[:style],
228
+ }
229
+
230
+ super(formatted_text, options)
231
+ end
232
+
233
+ # Render text to the document based on the settings defined in
234
+ # constructor.
235
+ #
236
+ # In order to facilitate look-ahead calculations, this method accepts
237
+ # a `dry_run: true` option. If provided, then everything is executed as
238
+ # if rendering, with the exception that nothing is drawn on the page.
239
+ # Useful for look-ahead computations of height, unprinted text, etc.
240
+ #
241
+ # @param flags [Hash{Symbol => any}]
242
+ # @option flags :dry_run [Boolean] (false)
243
+ # Do not draw the text. Everything else is done.
244
+ # @return [Array<Hash>]
245
+ # A formatted text array representing any text that did not print
246
+ # under the current settings.
247
+ # @raise [Prawn::Text::Formatted::Arranger::BadFontFamily]
248
+ # If no font family is defined for the current font.
249
+ # @raise [Prawn::Errors::CannotFit]
250
+ # If not wide enough to print any text.
251
+ def render(flags = {})
252
+ unprinted_text = []
253
+
254
+ @document.save_font do
255
+ @document.character_spacing(@character_spacing) do
256
+ @document.text_rendering_mode(@mode) do
257
+ process_options
258
+
259
+ text = normalized_text(flags)
260
+
261
+ @document.font_size(@font_size) do
262
+ shrink_to_fit(text) if @overflow == :shrink_to_fit
263
+ process_vertical_alignment(text)
264
+ @inked = true unless flags[:dry_run]
265
+ unprinted_text =
266
+ if @rotate != 0 && @inked
267
+ render_rotated(text)
268
+ else
269
+ wrap(text)
270
+ end
271
+ @inked = false
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ unprinted_text.map do |e|
278
+ e.merge(text: @document.font.to_utf8(e[:text]))
279
+ end
280
+ end
281
+
282
+ # The width available at this point in the box.
283
+ #
284
+ # @return [Number]
285
+ def available_width
286
+ @width
287
+ end
288
+
289
+ # The height actually used during the previous {render}.
290
+ #
291
+ # @return [Number]
292
+ def height
293
+ return 0 if @baseline_y.nil? || @descender.nil?
294
+
295
+ (@baseline_y - @descender).abs
296
+ end
297
+
298
+ # @private
299
+ # @param fragment [Prawn::Text::Formatted::Fragment]
300
+ # @param accumulated_width [Number]
301
+ # @param line_width [Number]
302
+ # @param word_spacing [Number]
303
+ # @return [void]
304
+ def draw_fragment(
305
+ fragment, accumulated_width = 0, line_width = 0, word_spacing = 0
306
+ )
307
+ case @align
308
+ when :left
309
+ x = @at[0]
310
+ when :center
311
+ x = @at[0] + (@width * 0.5) - (line_width * 0.5)
312
+ when :right
313
+ x = @at[0] + @width - line_width
314
+ when :justify
315
+ x =
316
+ if @direction == :ltr
317
+ @at[0]
318
+ else
319
+ @at[0] + @width - line_width
320
+ end
321
+ else
322
+ raise ArgumentError,
323
+ 'align must be one of :left, :right, :center or :justify symbols'
324
+ end
325
+
326
+ x += accumulated_width
327
+
328
+ y = @at[1] + @baseline_y
329
+
330
+ y += fragment.y_offset
331
+
332
+ fragment.left = x
333
+ fragment.baseline = y
334
+
335
+ if @inked
336
+ draw_fragment_underlays(fragment)
337
+
338
+ @document.word_spacing(word_spacing) do
339
+ if @draw_text_callback
340
+ @draw_text_callback.call(
341
+ fragment.text,
342
+ at: [x, y],
343
+ kerning: @kerning,
344
+ )
345
+ else
346
+ @document.draw_text!(
347
+ fragment.text,
348
+ at: [x, y],
349
+ kerning: @kerning,
350
+ )
351
+ end
352
+ end
353
+
354
+ draw_fragment_overlays(fragment)
355
+ end
356
+ end
357
+
358
+ # @group Extension API
359
+
360
+ # Text box extensions.
361
+ #
362
+ # Example:
363
+ #
364
+ # ```ruby
365
+ # module MyWrap
366
+ # def wrap(array)
367
+ # initialize_wrap([{ text: 'all your base are belong to us' }])
368
+ # @line_wrap.wrap_line(
369
+ # document: @document,
370
+ # kerning: @kerning,
371
+ # width: 10000,
372
+ # arranger: @arranger
373
+ # )
374
+ # fragment = @arranger.retrieve_fragment
375
+ # format_and_draw_fragment(fragment, 0, @line_wrap.width, 0)
376
+ # []
377
+ # end
378
+ # end
379
+ #
380
+ # Prawn::Text::Formatted::Box.extensions << MyWrap
381
+ #
382
+ # box = Prawn::Text::Formatted::Box.new('hello world')
383
+ # box.render("why can't I print anything other than" +
384
+ # '"all your base are belong to us"?')
385
+ # ```
386
+ #
387
+ # See {Prawn::Text::Formatted::Wrap} for what is required of the
388
+ # wrap method if you want to override the default wrapping algorithm.
389
+ #
390
+ # @return [Array<Module>]
391
+ def self.extensions
392
+ @extensions ||= []
393
+ end
394
+
395
+ # @private
396
+ def self.inherited(base)
397
+ super
398
+ extensions.each { |e| base.extensions << e }
399
+ end
400
+
401
+ # @private
402
+ def valid_options
403
+ PDF::Core::Text::VALID_OPTIONS + %i[
404
+ at
405
+ height width
406
+ align valign
407
+ rotate rotate_around
408
+ overflow min_font_size
409
+ disable_wrap_by_char
410
+ leading character_spacing
411
+ mode single_line
412
+ document
413
+ direction
414
+ fallback_fonts
415
+ draw_text_callback
416
+ ]
417
+ end
418
+
419
+ private
420
+
421
+ def normalized_text(flags)
422
+ text = normalize_encoding
423
+
424
+ text.each { |t| t.delete(:color) } if flags[:dry_run]
425
+
426
+ text
427
+ end
428
+
429
+ def original_text
430
+ @original_array.map(&:dup)
431
+ end
432
+
433
+ def original_text=(formatted_text)
434
+ @original_array = formatted_text
435
+ end
436
+
437
+ def normalize_encoding
438
+ formatted_text = original_text
439
+
440
+ unless @fallback_fonts.empty?
441
+ formatted_text = process_fallback_fonts(formatted_text)
442
+ end
443
+
444
+ formatted_text.each do |hash|
445
+ if hash[:font]
446
+ @document.font(hash[:font]) do
447
+ hash[:text] = @document.font.normalize_encoding(hash[:text])
448
+ end
449
+ else
450
+ hash[:text] = @document.font.normalize_encoding(hash[:text])
451
+ end
452
+ end
453
+
454
+ formatted_text
455
+ end
456
+
457
+ def process_fallback_fonts(formatted_text)
458
+ modified_formatted_text = []
459
+
460
+ formatted_text.each do |hash|
461
+ fragments = analyze_glyphs_for_fallback_font_support(hash)
462
+ modified_formatted_text.concat(fragments)
463
+ end
464
+
465
+ modified_formatted_text
466
+ end
467
+
468
+ def analyze_glyphs_for_fallback_font_support(hash)
469
+ font_glyph_pairs = []
470
+
471
+ original_font = @document.font.family
472
+ fragment_font = hash[:font] || original_font
473
+ fragment_font_options =
474
+ (fragment_font_style = font_style(hash[:styles])) == :normal ? {} : { style: fragment_font_style }
475
+
476
+ fallback_fonts = @fallback_fonts.dup
477
+ # always default back to the current font if the glyph is missing from
478
+ # all fonts
479
+ fallback_fonts << fragment_font
480
+
481
+ @document.save_font do
482
+ hash[:text].each_char do |char|
483
+ font_glyph_pairs << [
484
+ find_font_for_this_glyph(
485
+ char,
486
+ fragment_font,
487
+ fallback_fonts.dup,
488
+ fragment_font_options,
489
+ ),
490
+ char,
491
+ ]
492
+ end
493
+ end
494
+
495
+ # Don't add a :font to fragments if it wasn't there originally
496
+ if hash[:font].nil?
497
+ font_glyph_pairs.each do |pair|
498
+ pair[0] = nil if pair[0] == original_font
499
+ end
500
+ end
501
+
502
+ form_fragments_from_like_font_glyph_pairs(font_glyph_pairs, hash)
503
+ end
504
+
505
+ def font_style(styles)
506
+ if styles
507
+ if styles.include?(:bold)
508
+ styles.include?(:italic) ? :bold_italic : :bold
509
+ elsif styles.include?(:italic)
510
+ :italic
511
+ else
512
+ :normal
513
+ end
514
+ else
515
+ :normal
516
+ end
517
+ end
518
+
519
+ def find_font_for_this_glyph(char, current_font, fallback_fonts, current_font_options = {})
520
+ @document.font(current_font, current_font_options)
521
+ if fallback_fonts.empty? || @document.font.glyph_present?(char)
522
+ current_font
523
+ else
524
+ find_font_for_this_glyph(char, fallback_fonts.shift, fallback_fonts, current_font_options)
525
+ end
526
+ end
527
+
528
+ def form_fragments_from_like_font_glyph_pairs(font_glyph_pairs, hash)
529
+ fragments = []
530
+ fragment = nil
531
+ current_font = nil
532
+
533
+ font_glyph_pairs.each do |font, char|
534
+ if font != current_font || fragments.count.zero?
535
+ current_font = font
536
+ fragment = hash.dup
537
+ fragment[:text] = char
538
+ fragment[:font] = font unless font.nil?
539
+ fragments << fragment
540
+ else
541
+ fragment[:text] += char
542
+ end
543
+ end
544
+
545
+ fragments
546
+ end
547
+
548
+ def move_baseline_down
549
+ if @baseline_y.zero?
550
+ @baseline_y = -@ascender
551
+ else
552
+ @baseline_y -= (@line_height + @leading)
553
+ end
554
+ end
555
+
556
+ # Returns the default height to be used if none is provided or if the
557
+ # overflow option is set to :expand. If we are in a stretchy bounding
558
+ # box, assume we can stretch to the bottom of the innermost non-stretchy
559
+ # box.
560
+ #
561
+ def default_height
562
+ # Find the "frame", the innermost non-stretchy bbox.
563
+ frame = @document.bounds
564
+ frame = frame.parent while frame.stretchy? && frame.parent
565
+
566
+ @at[1] + @document.bounds.absolute_bottom - frame.absolute_bottom
567
+ end
568
+
569
+ def process_vertical_alignment(text)
570
+ # The vertical alignment must only be done once per text box, but
571
+ # we need to wait until render() is called so that the fonts are set
572
+ # up properly for wrapping. So guard with a boolean to ensure this is
573
+ # only run once.
574
+ if defined?(@vertical_alignment_processed) &&
575
+ @vertical_alignment_processed
576
+ return
577
+ end
578
+
579
+ @vertical_alignment_processed = true
580
+
581
+ return if @vertical_align == :top
582
+
583
+ wrap(text)
584
+
585
+ case @vertical_align
586
+ when :center
587
+ @at[1] -= (@height - height + @descender) * 0.5
588
+ when :bottom
589
+ @at[1] -= (@height - height)
590
+ else
591
+ raise ArgumentError,
592
+ 'valign must be one of :left, :right or :center symbols'
593
+ end
594
+
595
+ @height = height
596
+ end
597
+
598
+ # Decrease the font size until the text fits or the min font
599
+ # size is reached
600
+ def shrink_to_fit(text)
601
+ loop do
602
+ if @disable_wrap_by_char && @font_size > @min_font_size
603
+ begin
604
+ wrap(text)
605
+ rescue Errors::CannotFit
606
+ # Ignore errors while we can still attempt smaller
607
+ # font sizes.
608
+ end
609
+ else
610
+ wrap(text)
611
+ end
612
+
613
+ break if @everything_printed || @font_size <= @min_font_size
614
+
615
+ @font_size = [@font_size - 0.5, @min_font_size].max
616
+ @document.font_size = @font_size
617
+ end
618
+ end
619
+
620
+ def process_options
621
+ # must be performed within a save_font block because
622
+ # document.process_text_options sets the font
623
+ @document.process_text_options(@options)
624
+ @font_size = @options[:size]
625
+ @kerning = @options[:kerning]
626
+ end
627
+
628
+ def render_rotated(text)
629
+ unprinted_text = ''
630
+
631
+ case @rotate_around
632
+ when :center
633
+ x = @at[0] + (@width * 0.5)
634
+ y = @at[1] - (@height * 0.5)
635
+ when :upper_right
636
+ x = @at[0] + @width
637
+ y = @at[1]
638
+ when :lower_right
639
+ x = @at[0] + @width
640
+ y = @at[1] - @height
641
+ when :lower_left
642
+ x = @at[0]
643
+ y = @at[1] - @height
644
+ else
645
+ x = @at[0]
646
+ y = @at[1]
647
+ end
648
+
649
+ @document.rotate(@rotate, origin: [x, y]) do
650
+ unprinted_text = wrap(text)
651
+ end
652
+ unprinted_text
653
+ end
654
+
655
+ def draw_fragment_underlays(fragment)
656
+ fragment.callback_objects.each do |obj|
657
+ obj.render_behind(fragment) if obj.respond_to?(:render_behind)
658
+ end
659
+ end
660
+
661
+ def draw_fragment_overlays(fragment)
662
+ draw_fragment_overlay_styles(fragment)
663
+ draw_fragment_overlay_link(fragment)
664
+ draw_fragment_overlay_anchor(fragment)
665
+ draw_fragment_overlay_local(fragment)
666
+ fragment.callback_objects.each do |obj|
667
+ obj.render_in_front(fragment) if obj.respond_to?(:render_in_front)
668
+ end
669
+ end
670
+
671
+ def draw_fragment_overlay_link(fragment)
672
+ return unless fragment.link
673
+
674
+ box = fragment.absolute_bounding_box
675
+ @document.link_annotation(
676
+ box,
677
+ Border: [0, 0, 0],
678
+ A: {
679
+ Type: :Action,
680
+ S: :URI,
681
+ URI: PDF::Core::LiteralString.new(fragment.link),
682
+ },
683
+ )
684
+ end
685
+
686
+ def draw_fragment_overlay_anchor(fragment)
687
+ return unless fragment.anchor
688
+
689
+ box = fragment.absolute_bounding_box
690
+ @document.link_annotation(
691
+ box,
692
+ Border: [0, 0, 0],
693
+ Dest: fragment.anchor,
694
+ )
695
+ end
696
+
697
+ def draw_fragment_overlay_local(fragment)
698
+ return unless fragment.local
699
+
700
+ box = fragment.absolute_bounding_box
701
+ @document.link_annotation(
702
+ box,
703
+ Border: [0, 0, 0],
704
+ A: {
705
+ Type: :Action,
706
+ S: :Launch,
707
+ F: PDF::Core::LiteralString.new(fragment.local),
708
+ NewWindow: true,
709
+ },
710
+ )
711
+ end
712
+
713
+ def draw_fragment_overlay_styles(fragment)
714
+ if fragment.styles.include?(:underline)
715
+ @document.stroke_line(fragment.underline_points)
716
+ end
717
+
718
+ if fragment.styles.include?(:strikethrough)
719
+ @document.stroke_line(fragment.strikethrough_points)
720
+ end
721
+ end
722
+ end
723
+ end
724
+ end
725
+ end