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,567 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'font_metric_cache'
4
+
5
+ module Prawn
6
+ class Document # rubocop: disable Style/Documentation
7
+ # @group Stable API
8
+
9
+ # Default empty options.
10
+ DEFAULT_OPTS = {}.freeze
11
+
12
+ # Without arguments, this returns the currently selected font. Otherwise, it
13
+ # sets the current font. When a block is used, the font is applied
14
+ # transactionally and is rolled back when the block exits.
15
+ #
16
+ # ```ruby
17
+ # Prawn::Document.generate("font.pdf") do
18
+ # text "Default font is Helvetica"
19
+ #
20
+ # font "Times-Roman"
21
+ # text "Now using Times-Roman"
22
+ #
23
+ # font("DejaVuSans.ttf") do
24
+ # text "Using TTF font from file DejaVuSans.ttf"
25
+ # font "Courier", style: :bold
26
+ # text "You see this in bold Courier"
27
+ # end
28
+ #
29
+ # text "Times-Roman, again"
30
+ # end
31
+ # ```
32
+ #
33
+ # The `name` parameter must be a string. It can be one of the 14 built-in
34
+ # fonts supported by PDF, or the location of a TTF file. The
35
+ # {Fonts::AFM::BUILT_INS} array specifies the valid built in font names.
36
+ #
37
+ # If a TTF/OTF font is specified, the glyphs necessary to render your
38
+ # document will be embedded in the rendered PDF. This should be your
39
+ # preferred option in most cases. It will increase the size of the resulting
40
+ # file, but also make it more portable.
41
+ #
42
+ # The options parameter is an optional hash providing size and style. To use
43
+ # the :style option you need to map those font styles to their respective
44
+ # font files.
45
+ #
46
+ # @param name [String] font name. It can be:
47
+ # - One of 14 PDF built-in fonts.
48
+ # - A font file path.
49
+ # - A font name defined in {font_families}
50
+ # @param options [Hash{Symbol => any}]
51
+ # @option options :style [Symbol] font style
52
+ # @yield
53
+ # @return [Font]
54
+ # @see #font_families
55
+ # @see Font::AFM::BUILT_INS
56
+ def font(name = nil, options = DEFAULT_OPTS)
57
+ return((defined?(@font) && @font) || font('Helvetica')) if name.nil?
58
+
59
+ if state.pages.empty? && !state.page.in_stamp_stream?
60
+ raise Prawn::Errors::NotOnPage
61
+ end
62
+
63
+ new_font = find_font(name.to_s, options)
64
+
65
+ if block_given?
66
+ save_font do
67
+ set_font(new_font, options[:size])
68
+ yield
69
+ end
70
+ else
71
+ set_font(new_font, options[:size])
72
+ end
73
+
74
+ @font
75
+ end
76
+
77
+ # When called with no argument, returns the current font size.
78
+ #
79
+ # When called with a single argument but no block, sets the current font
80
+ # size. When a block is used, the font size is applied transactionally and
81
+ # is rolled back when the block exits. You may still change the font size
82
+ # within a transactional block for individual text segments, or nested calls
83
+ # to `font_size`.
84
+ #
85
+ # @example
86
+ # Prawn::Document.generate("font_size.pdf") do
87
+ # font_size 16
88
+ # text "At size 16"
89
+ #
90
+ # font_size(10) do
91
+ # text "At size 10"
92
+ # text "At size 6", size: 6
93
+ # text "At size 10"
94
+ # end
95
+ #
96
+ # text "At size 16"
97
+ # end
98
+ #
99
+ # @overload font_size()
100
+ # @return [Number] vurrent font size
101
+ # @overload font_size(points)
102
+ # @param points [Number] new font size
103
+ # @yield if block is provided font size is set only for the duration of
104
+ # the block
105
+ # @return [void]
106
+ def font_size(points = nil)
107
+ return @font_size unless points
108
+
109
+ size_before_yield = @font_size
110
+ @font_size = points
111
+ block_given? ? yield : return
112
+ @font_size = size_before_yield
113
+ end
114
+
115
+ # Sets the font size.
116
+ #
117
+ # @param size [Number]
118
+ # @return [Number]
119
+ def font_size=(size)
120
+ font_size(size)
121
+ end
122
+
123
+ # Returns the width of the given string using the given font. If `:size` is
124
+ # not specified as one of the options, the string is measured using the
125
+ # current font size. You can also pass `:kerning` as an option to indicate
126
+ # whether kerning should be used when measuring the width (defaults to
127
+ # `false`).
128
+ #
129
+ # Note that the string _must_ be encoded properly for the font being used.
130
+ # For AFM fonts, this is WinAnsi. For TTF/OTF, make sure the font is encoded
131
+ # as UTF-8. You can use the Font#normalize_encoding method to make sure
132
+ # strings are in an encoding appropriate for the current font.
133
+ #
134
+ # @devnote
135
+ # For the record, this method used to be a method of Font (and still
136
+ # delegates to width computations on Font). However, having the primary
137
+ # interface for calculating string widths exist on Font made it tricky to
138
+ # write extensions for Prawn in which widths are computed differently
139
+ # (e.g., taking formatting tags into account, or the like).
140
+ #
141
+ # By putting width_of here, on Document itself, extensions may easily
142
+ # override it and redefine the width calculation behavior.
143
+ #
144
+ # @param string [String]
145
+ # @param options [Hash{Symbol => any}]
146
+ # @option options :inline_format [Boolean] (false)
147
+ # @option options :kerning [Boolean] (false)
148
+ # @option options :style [Symbol]
149
+ # @return [Number]
150
+ def width_of(string, options = {})
151
+ if options.key?(:inline_format)
152
+ p = options[:inline_format]
153
+ p = [] unless p.is_a?(Array)
154
+
155
+ # Build up an Arranger with the entire string on one line, finalize it,
156
+ # and find its width.
157
+ arranger = Prawn::Text::Formatted::Arranger.new(self, options)
158
+ arranger.consumed = text_formatter.format(string, *p)
159
+ arranger.finalize_line
160
+
161
+ arranger.line_width
162
+ else
163
+ width_of_string(string, options)
164
+ end
165
+ end
166
+
167
+ # Hash that maps font family names to their styled individual font
168
+ # definitions.
169
+ #
170
+ # To add support for another font family, append to this hash, e.g:
171
+ #
172
+ # ```ruby
173
+ # pdf.font_families.update(
174
+ # "MyTrueTypeFamily" => {
175
+ # bold: "foo-bold.ttf",
176
+ # italic: "foo-italic.ttf",
177
+ # bold_italic: "foo-bold-italic.ttf",
178
+ # normal: "foo.ttf",
179
+ # }
180
+ # )
181
+ # ```
182
+ #
183
+ # This will then allow you to use the fonts like so:
184
+ #
185
+ # ```ruby
186
+ # pdf.font("MyTrueTypeFamily", style: :bold)
187
+ # pdf.text "Some bold text"
188
+ # pdf.font("MyTrueTypeFamily")
189
+ # pdf.text "Some normal text"
190
+ # ```
191
+ #
192
+ # This assumes that you have appropriate TTF/OTF fonts for each style you
193
+ # wish to support.
194
+ #
195
+ # By default the styles `:bold`, `:italic`, `:bold_italic`, and `:normal`
196
+ # are defined for fonts "Courier", "Times-Roman" and "Helvetica". When
197
+ # defining your own font families, you can map any or all of these styles to
198
+ # whatever font files you'd like.
199
+ #
200
+ # Font definition can be either a hash or just a string.
201
+ #
202
+ # A hash font definition can specify a number of options:
203
+ #
204
+ # - `:file` -- path to the font file (required)
205
+ # - `:subset` -- whether to subset the font (default false). Only
206
+ # applicable to TrueType and OpenType fonts (includnig DFont and TTC).
207
+ #
208
+ # A string font definition is equivalent to hash definition with only
209
+ # `:file` being specified.
210
+ #
211
+ # @return [Hash{String => Hash{Symbol => String, Hash{Symbol => String}}}]
212
+ def font_families
213
+ @font_families ||= {}.merge!(
214
+ 'Courier' => {
215
+ bold: 'Courier-Bold',
216
+ italic: 'Courier-Oblique',
217
+ bold_italic: 'Courier-BoldOblique',
218
+ normal: 'Courier',
219
+ },
220
+
221
+ 'Times-Roman' => {
222
+ bold: 'Times-Bold',
223
+ italic: 'Times-Italic',
224
+ bold_italic: 'Times-BoldItalic',
225
+ normal: 'Times-Roman',
226
+ },
227
+
228
+ 'Helvetica' => {
229
+ bold: 'Helvetica-Bold',
230
+ italic: 'Helvetica-Oblique',
231
+ bold_italic: 'Helvetica-BoldOblique',
232
+ normal: 'Helvetica',
233
+ },
234
+ )
235
+ end
236
+
237
+ # @group Experimental API
238
+
239
+ # Sets the font directly, given an actual {Font} object and size.
240
+ #
241
+ # @private
242
+ # @param font [Font]
243
+ # @param size [Number]
244
+ # @return [void]
245
+ def set_font(font, size = nil)
246
+ @font = font
247
+ @font_size = size if size
248
+ end
249
+
250
+ # Saves the current font, and then yields. When the block finishes, the
251
+ # original font is restored.
252
+ #
253
+ # @yield
254
+ # @return [void]
255
+ def save_font
256
+ @font ||= find_font('Helvetica')
257
+ original_font = @font
258
+ original_size = @font_size
259
+
260
+ yield
261
+ ensure
262
+ set_font(original_font, original_size) if original_font
263
+ end
264
+
265
+ # Looks up the given font using the given criteria. Once a font has been
266
+ # found by that matches the criteria, it will be cached to subsequent
267
+ # lookups for that font will return the same object.
268
+ #
269
+ # @devnote
270
+ # Challenges involved: the name alone is not sufficient to uniquely
271
+ # identify a font (think dfont suitcases that can hold multiple different
272
+ # fonts in a single file). Thus, the `:name` key is included in the cache
273
+ # key.
274
+ #
275
+ # It is further complicated, however, since fonts in some formats (like
276
+ # the dfont suitcases) can be identified either by numeric index, OR by
277
+ # their name within the suitcase, and both should hash to the same font
278
+ # object (to avoid the font being embedded multiple times). This is not
279
+ # yet implemented, which means if someone selects a font both by name, and
280
+ # by index, the font will be embedded twice. Since we do font subsetting,
281
+ # this double embedding won't be catastrophic, just annoying.
282
+ #
283
+ # @private
284
+ # @param name [String]
285
+ # @param options [Hash]
286
+ # @option options :style [Symbol]
287
+ # @option options :file [String]
288
+ # @option options :font [Integer, String] index or name of the font in
289
+ # a font suitcase/collection
290
+ # @return [Font]
291
+ def find_font(name, options = {}) # :nodoc:
292
+ if font_families.key?(name)
293
+ family = name
294
+ name = font_families[name][options[:style] || :normal]
295
+ if name.is_a?(::Hash)
296
+ options = options.merge(name)
297
+ name = options[:file]
298
+ end
299
+ end
300
+ key = "#{family}:#{name}:#{options[:font] || 0}"
301
+
302
+ if name.is_a?(Prawn::Font)
303
+ font_registry[key] = name
304
+ else
305
+ font_registry[key] ||=
306
+ Font.load(self, name, options.merge(family: family))
307
+ end
308
+ end
309
+
310
+ # Hash of Font objects keyed by names.
311
+ #
312
+ # @private
313
+ # @return [Hash{String => Font}]
314
+ def font_registry
315
+ @font_registry ||= {}
316
+ end
317
+
318
+ private
319
+
320
+ def width_of_inline_formatted_string(string, options = {})
321
+ # Build up an Arranger with the entire string on one line, finalize it,
322
+ # and find its width.
323
+ arranger = Prawn::Text::Formatted::Arranger.new(self, options)
324
+ arranger.consumed = Text::Formatted::Parser.format(string)
325
+ arranger.finalize_line
326
+
327
+ arranger.line_width
328
+ end
329
+
330
+ def width_of_string(string, options = {})
331
+ font_metric_cache.width_of(string, options)
332
+ end
333
+ end
334
+
335
+ # Provides font information and helper functions.
336
+ #
337
+ # @abstract
338
+ class Font
339
+ require_relative 'fonts'
340
+
341
+ # @deprecated
342
+ AFM = Prawn::Fonts::AFM
343
+
344
+ # @deprecated
345
+ TTF = Fonts::TTF
346
+
347
+ # @deprecated
348
+ DFont = Fonts::DFont
349
+
350
+ # @deprecated
351
+ TTC = Fonts::TTC
352
+
353
+ # The font name.
354
+ # @return [String]
355
+ attr_reader :name
356
+
357
+ # The font family.
358
+ # @return [String]
359
+ attr_reader :family
360
+
361
+ # The options hash used to initialize the font.
362
+ # @return [Hash]
363
+ attr_reader :options
364
+
365
+ # Shortcut interface for constructing a font object. Filenames of the form
366
+ # `*.ttf` will call {Fonts::TTF#initialize TTF.new}, `*.otf` calls
367
+ # {Fonts::OTF#initialize OTF.new}, `*.dfont` calls {Fonts::DFont#initialize
368
+ # DFont.new}, `*.ttc` goes to {Fonts::TTC#initialize TTC.new}, and anything
369
+ # else will be passed through to {Prawn::Fonts::AFM#initialize AFM.new}.
370
+ #
371
+ # @param document [Prawn::Document] owning document
372
+ # @param src [String] font file path
373
+ # @param options [Hash]
374
+ # @option options :family [String]
375
+ # @option options :style [Symbol]
376
+ # @return [Prawn::Fonts::Font]
377
+ def self.load(document, src, options = {})
378
+ case font_format(src, options)
379
+ when 'ttf' then TTF.new(document, src, options)
380
+ when 'otf' then Fonts::OTF.new(document, src, options)
381
+ when 'dfont' then DFont.new(document, src, options)
382
+ when 'ttc' then TTC.new(document, src, options)
383
+ else AFM.new(document, src, options)
384
+ end
385
+ end
386
+
387
+ # Guesses font format.
388
+ #
389
+ # @private
390
+ # @param src [String, IO]
391
+ # @param options [Hash]
392
+ # @option options :format [String]
393
+ # @return [String]
394
+ def self.font_format(src, options)
395
+ return options.fetch(:format, 'ttf') if src.respond_to?(:read)
396
+
397
+ case src.to_s
398
+ when /\.ttf$/i then 'ttf'
399
+ when /\.otf$/i then 'otf'
400
+ when /\.dfont$/i then 'dfont'
401
+ when /\.ttc$/i then 'ttc'
402
+ else 'afm'
403
+ end
404
+ end
405
+
406
+ # @private
407
+ # @param document [Prawn::Document]
408
+ # @param name [String]
409
+ # @param options [Hash{Symbol => any}]
410
+ # @option options :family [String]
411
+ # @option options :subset [Boolean] (true)
412
+ def initialize(document, name, options = {})
413
+ @document = document
414
+ @name = name
415
+ @options = options
416
+
417
+ @family = options[:family]
418
+
419
+ @identifier = generate_unique_id
420
+
421
+ @references = {}
422
+ @subset_name_cache = {}
423
+
424
+ @full_font_embedding = options.key?(:subset) && !options[:subset]
425
+ end
426
+
427
+ # The size of the font ascender in PDF points.
428
+ #
429
+ # @return [Number]
430
+ def ascender
431
+ @ascender / 1000.0 * size
432
+ end
433
+
434
+ # The size of the font descender in PDF points.
435
+ #
436
+ # @return [Number]
437
+ def descender
438
+ -@descender / 1000.0 * size
439
+ end
440
+
441
+ # The size of the recommended gap between lines of text in PDF points
442
+ #
443
+ # @return [Number]
444
+ def line_gap
445
+ @line_gap / 1000.0 * size
446
+ end
447
+
448
+ # Normalizes the encoding of the string to an encoding supported by the
449
+ # font. The string is expected to be UTF-8 going in. It will be re-encoded
450
+ # and the new string will be returned.
451
+ #
452
+ # @abstract
453
+ # @!parse def normalize_encoding(string); end
454
+ # @param string [String]
455
+ # @return [String]
456
+ def normalize_encoding(_string)
457
+ raise NotImplementedError,
458
+ 'subclasses of Prawn::Font must implement #normalize_encoding'
459
+ end
460
+
461
+ # Destructive version of {normalize_encoding}; normalizes the encoding of a
462
+ # string in place.
463
+ #
464
+ # @note This method doesn't mutate its argument any more.
465
+ #
466
+ # @deprecated
467
+ # @param str [String]
468
+ # @return [String]
469
+ def normalize_encoding!(str)
470
+ warn('Font#normalize_encoding! is deprecated. Please use non-mutating version Font#normalize_encoding instead.')
471
+ str.dup.replace(normalize_encoding(str))
472
+ end
473
+
474
+ # Gets height of font in PDF points at the given font size.
475
+ #
476
+ # @param size [Number]
477
+ # @return [Number]
478
+ def height_at(size)
479
+ @normalized_height ||= (@ascender - @descender + @line_gap) / 1000.0
480
+ @normalized_height * size
481
+ end
482
+
483
+ # Gets height of current font in PDF points at current font size.
484
+ #
485
+ # @return [Number]
486
+ def height
487
+ height_at(size)
488
+ end
489
+
490
+ # Registers the given subset of the current font with the current PDF
491
+ # page. This is safe to call multiple times for a given font and subset,
492
+ # as it will only add the font the first time it is called.
493
+ #
494
+ # @param subset [Integer]
495
+ # @return [void]
496
+ def add_to_current_page(subset)
497
+ @references[subset] ||= register(subset)
498
+ @document.state.page.fonts[identifier_for(subset)] = @references[subset]
499
+ end
500
+
501
+ # @private
502
+ # @param subset [Integer]
503
+ # @return [Symbol]
504
+ def identifier_for(subset)
505
+ @subset_name_cache[subset] ||=
506
+ if full_font_embedding
507
+ @identifier.to_sym
508
+ else
509
+ :"#{@identifier}.#{subset}"
510
+ end
511
+ end
512
+
513
+ # Returns a string containing a human-readable representation of this font.
514
+ #
515
+ # @return [String]
516
+ def inspect
517
+ "#{self.class.name}< #{name}: #{size} >"
518
+ end
519
+
520
+ # Return a hash (as in `Object#hash`) for the font. This is required since
521
+ # font objects are used as keys in hashes that cache certain values.
522
+ #
523
+ # @return [Integer]
524
+ def hash
525
+ [self.class, name, family].hash
526
+ end
527
+
528
+ # Compliments the {#hash} implementation.
529
+ #
530
+ # @param other [Object]
531
+ # @return [Boolean]
532
+ def eql?(other)
533
+ self.class == other.class && name == other.name &&
534
+ family == other.family && size == other.size
535
+ end
536
+
537
+ private
538
+
539
+ attr_reader :full_font_embedding
540
+
541
+ # generate a font identifier that hasn't been used on the current page yet
542
+ #
543
+ def generate_unique_id
544
+ key = nil
545
+ font_count = @document.font_registry.size + 1
546
+ loop do
547
+ key = :"F#{font_count}"
548
+ break if key_is_unique?(key)
549
+
550
+ font_count += 1
551
+ end
552
+ key
553
+ end
554
+
555
+ def key_is_unique?(test_key)
556
+ @document.state.page.fonts.keys.none? do |key|
557
+ key.to_s.start_with?("#{test_key}.")
558
+ end
559
+ end
560
+
561
+ protected
562
+
563
+ def size
564
+ @document.font_size
565
+ end
566
+ end
567
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prawn
4
+ # Cache used internally by {Prawn::Document} instances to calculate the width
5
+ # of various strings for layout purposes.
6
+ #
7
+ # @private
8
+ class FontMetricCache
9
+ CacheEntry = Struct.new(:font, :font_size, :options, :string)
10
+
11
+ def initialize(document)
12
+ @document = document
13
+
14
+ @cache = {}
15
+ end
16
+
17
+ # Get width of string.
18
+ #
19
+ # @param string [String]
20
+ # @param options [Hash{Symbol => any}]
21
+ # @option options :style [Symbol]
22
+ # @option options :size [Number]
23
+ # @option options :kerning [Boolean] (false)
24
+ # @return [Number]
25
+ def width_of(string, options)
26
+ f =
27
+ if options[:style]
28
+ # override style with :style => :bold
29
+ @document.find_font(@document.font.family, style: options[:style])
30
+ else
31
+ @document.font
32
+ end
33
+
34
+ encoded_string = f.normalize_encoding(string)
35
+
36
+ key = CacheEntry.new(f, @document.font_size, options, encoded_string)
37
+
38
+ @cache[key] ||= f.compute_width_of(encoded_string, options)
39
+
40
+ length = @cache[key]
41
+
42
+ character_count = @document.font.character_count(encoded_string)
43
+ if character_count.positive?
44
+ length += @document.character_spacing * (character_count - 1)
45
+ end
46
+
47
+ length
48
+ end
49
+ end
50
+ end