prawn 2.4.0 → 2.5.0

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 (203) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/prawn/document/bounding_box.rb +213 -141
  4. data/lib/prawn/document/column_box.rb +61 -26
  5. data/lib/prawn/document/internals.rb +25 -16
  6. data/lib/prawn/document/span.rb +20 -18
  7. data/lib/prawn/document.rb +257 -171
  8. data/lib/prawn/encoding.rb +2 -5
  9. data/lib/prawn/errors.rb +23 -34
  10. data/lib/prawn/font.rb +248 -135
  11. data/lib/prawn/font_metric_cache.rb +11 -10
  12. data/lib/prawn/fonts/afm.rb +85 -45
  13. data/lib/prawn/fonts/dfont.rb +7 -1
  14. data/lib/prawn/fonts/otf.rb +4 -1
  15. data/lib/prawn/fonts/to_unicode_cmap.rb +151 -0
  16. data/lib/prawn/fonts/ttc.rb +7 -2
  17. data/lib/prawn/fonts/ttf.rb +305 -93
  18. data/lib/prawn/fonts.rb +14 -0
  19. data/lib/prawn/graphics/blend_mode.rb +25 -28
  20. data/lib/prawn/graphics/cap_style.rb +9 -12
  21. data/lib/prawn/graphics/color.rb +57 -34
  22. data/lib/prawn/graphics/dash.rb +45 -42
  23. data/lib/prawn/graphics/join_style.rb +17 -11
  24. data/lib/prawn/graphics/patterns.rb +190 -69
  25. data/lib/prawn/graphics/transformation.rb +48 -41
  26. data/lib/prawn/graphics/transparency.rb +16 -40
  27. data/lib/prawn/graphics.rb +363 -253
  28. data/lib/prawn/grid.rb +184 -57
  29. data/lib/prawn/image_handler.rb +27 -10
  30. data/lib/prawn/images/image.rb +8 -10
  31. data/lib/prawn/images/jpg.rb +42 -19
  32. data/lib/prawn/images/png.rb +92 -41
  33. data/lib/prawn/images.rb +44 -57
  34. data/lib/prawn/measurement_extensions.rb +39 -8
  35. data/lib/prawn/measurements.rb +60 -5
  36. data/lib/prawn/outline.rb +114 -108
  37. data/lib/prawn/repeater.rb +51 -35
  38. data/lib/prawn/security/arcfour.rb +4 -4
  39. data/lib/prawn/security.rb +75 -70
  40. data/lib/prawn/soft_mask.rb +42 -30
  41. data/lib/prawn/stamp.rb +38 -42
  42. data/lib/prawn/text/box.rb +146 -96
  43. data/lib/prawn/text/formatted/arranger.rb +87 -26
  44. data/lib/prawn/text/formatted/box.rb +221 -150
  45. data/lib/prawn/text/formatted/fragment.rb +130 -14
  46. data/lib/prawn/text/formatted/line_wrap.rb +33 -24
  47. data/lib/prawn/text/formatted/parser.rb +112 -72
  48. data/lib/prawn/text/formatted/wrap.rb +12 -17
  49. data/lib/prawn/text/formatted.rb +75 -0
  50. data/lib/prawn/text.rb +441 -196
  51. data/lib/prawn/transformation_stack.rb +29 -10
  52. data/lib/prawn/utilities.rb +13 -13
  53. data/lib/prawn/version.rb +2 -1
  54. data/lib/prawn/view.rb +68 -53
  55. data/lib/prawn.rb +23 -18
  56. data.tar.gz.sig +0 -0
  57. metadata +54 -177
  58. metadata.gz.sig +0 -0
  59. data/.yardopts +0 -10
  60. data/Gemfile +0 -5
  61. data/Rakefile +0 -25
  62. data/manual/absolute_position.pdf +0 -0
  63. data/manual/basic_concepts/adding_pages.rb +0 -26
  64. data/manual/basic_concepts/basic_concepts.rb +0 -43
  65. data/manual/basic_concepts/creation.rb +0 -38
  66. data/manual/basic_concepts/cursor.rb +0 -32
  67. data/manual/basic_concepts/measurement.rb +0 -24
  68. data/manual/basic_concepts/origin.rb +0 -37
  69. data/manual/basic_concepts/other_cursor_helpers.rb +0 -39
  70. data/manual/basic_concepts/view.rb +0 -48
  71. data/manual/bounding_box/bounding_box.rb +0 -41
  72. data/manual/bounding_box/bounds.rb +0 -48
  73. data/manual/bounding_box/canvas.rb +0 -23
  74. data/manual/bounding_box/creation.rb +0 -22
  75. data/manual/bounding_box/indentation.rb +0 -45
  76. data/manual/bounding_box/nesting.rb +0 -52
  77. data/manual/bounding_box/russian_boxes.rb +0 -40
  78. data/manual/bounding_box/stretchy.rb +0 -29
  79. data/manual/contents.rb +0 -35
  80. data/manual/cover.rb +0 -43
  81. data/manual/document_and_page_options/background.rb +0 -29
  82. data/manual/document_and_page_options/document_and_page_options.rb +0 -34
  83. data/manual/document_and_page_options/metadata.rb +0 -25
  84. data/manual/document_and_page_options/page_margins.rb +0 -36
  85. data/manual/document_and_page_options/page_size.rb +0 -34
  86. data/manual/document_and_page_options/print_scaling.rb +0 -23
  87. data/manual/example_helper.rb +0 -8
  88. data/manual/graphics/blend_mode.rb +0 -52
  89. data/manual/graphics/circle_and_ellipse.rb +0 -21
  90. data/manual/graphics/color.rb +0 -22
  91. data/manual/graphics/common_lines.rb +0 -29
  92. data/manual/graphics/fill_and_stroke.rb +0 -41
  93. data/manual/graphics/fill_rules.rb +0 -38
  94. data/manual/graphics/gradients.rb +0 -43
  95. data/manual/graphics/graphics.rb +0 -64
  96. data/manual/graphics/helper.rb +0 -34
  97. data/manual/graphics/line_width.rb +0 -36
  98. data/manual/graphics/lines_and_curves.rb +0 -40
  99. data/manual/graphics/polygon.rb +0 -27
  100. data/manual/graphics/rectangle.rb +0 -20
  101. data/manual/graphics/rotate.rb +0 -25
  102. data/manual/graphics/scale.rb +0 -42
  103. data/manual/graphics/soft_masks.rb +0 -44
  104. data/manual/graphics/stroke_cap.rb +0 -30
  105. data/manual/graphics/stroke_dash.rb +0 -47
  106. data/manual/graphics/stroke_join.rb +0 -29
  107. data/manual/graphics/translate.rb +0 -29
  108. data/manual/graphics/transparency.rb +0 -33
  109. data/manual/how_to_read_this_manual.rb +0 -39
  110. data/manual/images/absolute_position.rb +0 -22
  111. data/manual/images/fit.rb +0 -20
  112. data/manual/images/horizontal.rb +0 -24
  113. data/manual/images/images.rb +0 -41
  114. data/manual/images/plain_image.rb +0 -17
  115. data/manual/images/scale.rb +0 -21
  116. data/manual/images/vertical.rb +0 -30
  117. data/manual/images/width_and_height.rb +0 -24
  118. data/manual/layout/boxes.rb +0 -26
  119. data/manual/layout/content.rb +0 -24
  120. data/manual/layout/layout.rb +0 -27
  121. data/manual/layout/simple_grid.rb +0 -22
  122. data/manual/outline/add_subsection_to.rb +0 -60
  123. data/manual/outline/insert_section_after.rb +0 -46
  124. data/manual/outline/outline.rb +0 -33
  125. data/manual/outline/sections_and_pages.rb +0 -66
  126. data/manual/repeatable_content/alternate_page_numbering.rb +0 -36
  127. data/manual/repeatable_content/page_numbering.rb +0 -55
  128. data/manual/repeatable_content/repeatable_content.rb +0 -35
  129. data/manual/repeatable_content/repeater.rb +0 -54
  130. data/manual/repeatable_content/stamp.rb +0 -40
  131. data/manual/security/encryption.rb +0 -28
  132. data/manual/security/permissions.rb +0 -43
  133. data/manual/security/security.rb +0 -28
  134. data/manual/table.rb +0 -16
  135. data/manual/text/alignment.rb +0 -43
  136. data/manual/text/color.rb +0 -24
  137. data/manual/text/column_box.rb +0 -30
  138. data/manual/text/fallback_fonts.rb +0 -41
  139. data/manual/text/font.rb +0 -40
  140. data/manual/text/font_size.rb +0 -44
  141. data/manual/text/font_style.rb +0 -25
  142. data/manual/text/formatted_callbacks.rb +0 -70
  143. data/manual/text/formatted_text.rb +0 -61
  144. data/manual/text/free_flowing_text.rb +0 -50
  145. data/manual/text/inline.rb +0 -40
  146. data/manual/text/kerning_and_character_spacing.rb +0 -38
  147. data/manual/text/leading.rb +0 -24
  148. data/manual/text/line_wrapping.rb +0 -60
  149. data/manual/text/paragraph_indentation.rb +0 -31
  150. data/manual/text/positioned_text.rb +0 -37
  151. data/manual/text/registering_families.rb +0 -51
  152. data/manual/text/rendering_and_color.rb +0 -36
  153. data/manual/text/right_to_left_text.rb +0 -54
  154. data/manual/text/rotation.rb +0 -52
  155. data/manual/text/single_usage.rb +0 -36
  156. data/manual/text/text.rb +0 -75
  157. data/manual/text/text_box_excess.rb +0 -35
  158. data/manual/text/text_box_extensions.rb +0 -48
  159. data/manual/text/text_box_overflow.rb +0 -51
  160. data/manual/text/utf8.rb +0 -27
  161. data/manual/text/win_ansi_charset.rb +0 -62
  162. data/prawn.gemspec +0 -51
  163. data/spec/data/curves.pdf +0 -66
  164. data/spec/extensions/encoding_helpers.rb +0 -11
  165. data/spec/prawn/document/bounding_box_spec.rb +0 -550
  166. data/spec/prawn/document/column_box_spec.rb +0 -75
  167. data/spec/prawn/document/security_spec.rb +0 -176
  168. data/spec/prawn/document_annotations_spec.rb +0 -76
  169. data/spec/prawn/document_destinations_spec.rb +0 -15
  170. data/spec/prawn/document_grid_spec.rb +0 -99
  171. data/spec/prawn/document_reference_spec.rb +0 -27
  172. data/spec/prawn/document_span_spec.rb +0 -44
  173. data/spec/prawn/document_spec.rb +0 -805
  174. data/spec/prawn/font_metric_cache_spec.rb +0 -54
  175. data/spec/prawn/font_spec.rb +0 -544
  176. data/spec/prawn/graphics/blend_mode_spec.rb +0 -63
  177. data/spec/prawn/graphics/transparency_spec.rb +0 -81
  178. data/spec/prawn/graphics_spec.rb +0 -872
  179. data/spec/prawn/graphics_stroke_styles_spec.rb +0 -229
  180. data/spec/prawn/image_handler_spec.rb +0 -53
  181. data/spec/prawn/images/jpg_spec.rb +0 -20
  182. data/spec/prawn/images/png_spec.rb +0 -283
  183. data/spec/prawn/images_spec.rb +0 -229
  184. data/spec/prawn/measurements_extensions_spec.rb +0 -24
  185. data/spec/prawn/outline_spec.rb +0 -512
  186. data/spec/prawn/repeater_spec.rb +0 -166
  187. data/spec/prawn/soft_mask_spec.rb +0 -74
  188. data/spec/prawn/stamp_spec.rb +0 -173
  189. data/spec/prawn/text/box_spec.rb +0 -1110
  190. data/spec/prawn/text/formatted/arranger_spec.rb +0 -466
  191. data/spec/prawn/text/formatted/box_spec.rb +0 -849
  192. data/spec/prawn/text/formatted/fragment_spec.rb +0 -343
  193. data/spec/prawn/text/formatted/line_wrap_spec.rb +0 -495
  194. data/spec/prawn/text/formatted/parser_spec.rb +0 -697
  195. data/spec/prawn/text_draw_text_spec.rb +0 -150
  196. data/spec/prawn/text_rendering_mode_spec.rb +0 -48
  197. data/spec/prawn/text_spacing_spec.rb +0 -95
  198. data/spec/prawn/text_spec.rb +0 -603
  199. data/spec/prawn/text_with_inline_formatting_spec.rb +0 -35
  200. data/spec/prawn/transformation_stack_spec.rb +0 -66
  201. data/spec/prawn/view_spec.rb +0 -63
  202. data/spec/prawn_manual_spec.rb +0 -35
  203. data/spec/spec_helper.rb +0 -48
data/lib/prawn/font.rb CHANGED
@@ -1,51 +1,59 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # font.rb : The Prawn font class
4
- #
5
- # Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- #
9
3
  require_relative 'font_metric_cache'
10
4
 
11
5
  module Prawn
12
- class Document
6
+ class Document # rubocop: disable Style/Documentation
13
7
  # @group Stable API
14
8
 
15
- # Without arguments, this returns the currently selected font. Otherwise,
16
- # it sets the current font. When a block is used, the font is applied
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
17
14
  # transactionally and is rolled back when the block exits.
18
15
  #
19
- # Prawn::Document.generate("font.pdf") do
20
- # text "Default font is Helvetica"
21
- #
22
- # font "Times-Roman"
23
- # text "Now using Times-Roman"
16
+ # ```ruby
17
+ # Prawn::Document.generate("font.pdf") do
18
+ # text "Default font is Helvetica"
24
19
  #
25
- # font("DejaVuSans.ttf") do
26
- # text "Using TTF font from file DejaVuSans.ttf"
27
- # font "Courier", :style => :bold
28
- # text "You see this in bold Courier"
29
- # end
20
+ # font "Times-Roman"
21
+ # text "Now using Times-Roman"
30
22
  #
31
- # text "Times-Roman, again"
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"
32
27
  # end
33
28
  #
34
- # The :name parameter must be a string. It can be one of the 14 built-in
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
35
34
  # fonts supported by PDF, or the location of a TTF file. The
36
- # Fonts::AFM::BUILT_INS array specifies the valid built in font values.
35
+ # {Fonts::AFM::BUILT_INS} array specifies the valid built in font names.
37
36
  #
38
- # If a ttf font is specified, the glyphs necessary to render your document
39
- # will be embedded in the rendered PDF. This should be your preferred option
40
- # in most cases. It will increase the size of the resulting file, but also
41
- # make it more portable.
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.
42
41
  #
43
42
  # The options parameter is an optional hash providing size and style. To use
44
43
  # the :style option you need to map those font styles to their respective
45
44
  # font files.
46
- # See font_families for more information.
47
45
  #
48
- def font(name = nil, options = {})
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)
49
57
  return((defined?(@font) && @font) || font('Helvetica')) if name.nil?
50
58
 
51
59
  if state.pages.empty? && !state.page.in_stamp_stream?
@@ -66,32 +74,35 @@ module Prawn
66
74
  @font
67
75
  end
68
76
 
69
- # @method font_size(points=nil)
70
- #
71
77
  # When called with no argument, returns the current font size.
72
78
  #
73
79
  # When called with a single argument but no block, sets the current font
74
- # size. When a block is used, the font size is applied transactionally and
75
- # is rolled back when the block exits. You may still change the font size
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
76
82
  # within a transactional block for individual text segments, or nested calls
77
- # to font_size.
83
+ # to `font_size`.
78
84
  #
85
+ # @example
79
86
  # Prawn::Document.generate("font_size.pdf") do
80
87
  # font_size 16
81
88
  # text "At size 16"
82
89
  #
83
90
  # font_size(10) do
84
91
  # text "At size 10"
85
- # text "At size 6", :size => 6
92
+ # text "At size 6", size: 6
86
93
  # text "At size 10"
87
94
  # end
88
95
  #
89
96
  # text "At size 16"
90
97
  # end
91
98
  #
92
- # When called without an argument, this method returns the current font
93
- # size.
94
- #
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]
95
106
  def font_size(points = nil)
96
107
  return @font_size unless points
97
108
 
@@ -101,33 +112,43 @@ module Prawn
101
112
  @font_size = size_before_yield
102
113
  end
103
114
 
104
- # Sets the font size
115
+ # Sets the font size.
116
+ #
117
+ # @param size [Number]
118
+ # @return [Number]
105
119
  def font_size=(size)
106
120
  font_size(size)
107
121
  end
108
122
 
109
- # Returns the width of the given string using the given font. If :size is
123
+ # Returns the width of the given string using the given font. If `:size` is
110
124
  # not specified as one of the options, the string is measured using the
111
- # current font size. You can also pass :kerning as an option to indicate
125
+ # current font size. You can also pass `:kerning` as an option to indicate
112
126
  # whether kerning should be used when measuring the width (defaults to
113
- # +false+).
127
+ # `false`).
114
128
  #
115
129
  # Note that the string _must_ be encoded properly for the font being used.
116
- # For AFM fonts, this is WinAnsi. For TTF, make sure the font is encoded as
117
- # UTF-8. You can use the Font#normalize_encoding method to make sure strings
118
- # are in an encoding appropriate for the current font.
119
- #--
120
- # For the record, this method used to be a method of Font (and still
121
- # delegates to width computations on Font). However, having the primary
122
- # interface for calculating string widths exist on Font made it tricky to
123
- # write extensions for Prawn in which widths are computed differently (e.g.,
124
- # taking formatting tags into account, or the like).
125
- #
126
- # By putting width_of here, on Document itself, extensions may easily
127
- # override it and redefine the width calculation behavior.
128
- #++
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]
129
150
  def width_of(string, options = {})
130
- if options.key? :inline_format
151
+ if options.key?(:inline_format)
131
152
  p = options[:inline_format]
132
153
  p = [] unless p.is_a?(Array)
133
154
 
@@ -143,69 +164,94 @@ module Prawn
143
164
  end
144
165
  end
145
166
 
146
- # Hash that maps font family names to their styled individual font names.
167
+ # Hash that maps font family names to their styled individual font
168
+ # definitions.
147
169
  #
148
170
  # To add support for another font family, append to this hash, e.g:
149
171
  #
150
- # pdf.font_families.update(
151
- # "MyTrueTypeFamily" => { :bold => "foo-bold.ttf",
152
- # :italic => "foo-italic.ttf",
153
- # :bold_italic => "foo-bold-italic.ttf",
154
- # :normal => "foo.ttf" })
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
+ # ```
155
182
  #
156
183
  # This will then allow you to use the fonts like so:
157
184
  #
158
- # pdf.font("MyTrueTypeFamily", :style => :bold)
159
- # pdf.text "Some bold text"
160
- # pdf.font("MyTrueTypeFamily")
161
- # pdf.text "Some normal text"
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
+ # ```
162
191
  #
163
- # This assumes that you have appropriate TTF fonts for each style you
192
+ # This assumes that you have appropriate TTF/OTF fonts for each style you
164
193
  # wish to support.
165
194
  #
166
- # By default the styles :bold, :italic, :bold_italic, and :normal are
167
- # defined for fonts "Courier", "Times-Roman" and "Helvetica". When
168
- # defining your own font families, you can map any or all of these
169
- # styles to whatever font files you'd like.
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:
170
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}}}]
171
212
  def font_families
172
213
  @font_families ||= {}.merge!(
173
214
  'Courier' => {
174
215
  bold: 'Courier-Bold',
175
216
  italic: 'Courier-Oblique',
176
217
  bold_italic: 'Courier-BoldOblique',
177
- normal: 'Courier'
218
+ normal: 'Courier',
178
219
  },
179
220
 
180
221
  'Times-Roman' => {
181
222
  bold: 'Times-Bold',
182
223
  italic: 'Times-Italic',
183
224
  bold_italic: 'Times-BoldItalic',
184
- normal: 'Times-Roman'
225
+ normal: 'Times-Roman',
185
226
  },
186
227
 
187
228
  'Helvetica' => {
188
229
  bold: 'Helvetica-Bold',
189
230
  italic: 'Helvetica-Oblique',
190
231
  bold_italic: 'Helvetica-BoldOblique',
191
- normal: 'Helvetica'
192
- }
232
+ normal: 'Helvetica',
233
+ },
193
234
  )
194
235
  end
195
236
 
196
237
  # @group Experimental API
197
238
 
198
- # Sets the font directly, given an actual Font object
199
- # and size.
239
+ # Sets the font directly, given an actual {Font} object and size.
200
240
  #
201
- def set_font(font, size = nil) # :nodoc:
241
+ # @private
242
+ # @param font [Font]
243
+ # @param size [Number]
244
+ # @return [void]
245
+ def set_font(font, size = nil)
202
246
  @font = font
203
247
  @font_size = size if size
204
248
  end
205
249
 
206
- # Saves the current font, and then yields. When the block
207
- # finishes, the original font is restored.
250
+ # Saves the current font, and then yields. When the block finishes, the
251
+ # original font is restored.
208
252
  #
253
+ # @yield
254
+ # @return [void]
209
255
  def save_font
210
256
  @font ||= find_font('Helvetica')
211
257
  original_font = @font
@@ -219,22 +265,30 @@ module Prawn
219
265
  # Looks up the given font using the given criteria. Once a font has been
220
266
  # found by that matches the criteria, it will be cached to subsequent
221
267
  # lookups for that font will return the same object.
222
- # --
223
- # Challenges involved: the name alone is not sufficient to uniquely identify
224
- # a font (think dfont suitcases that can hold multiple different fonts in a
225
- # single file). Thus, the :name key is included in the cache key.
226
- #
227
- # It is further complicated, however, since fonts in some formats (like the
228
- # dfont suitcases) can be identified either by numeric index, OR by their
229
- # name within the suitcase, and both should hash to the same font object (to
230
- # avoid the font being embedded multiple times). This is not yet
231
- # implemented, which means if someone selects a font both by name, and by
232
- # index, the font will be embedded twice. Since we do font subsetting, this
233
- # double embedding won't be catastrophic, just annoying.
234
- # ++
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.
235
282
  #
236
283
  # @private
237
- def find_font(name, options = {}) #:nodoc:
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:
238
292
  if font_families.key?(name)
239
293
  family = name
240
294
  name = font_families[name][options[:style] || :normal]
@@ -243,9 +297,9 @@ module Prawn
243
297
  name = options[:file]
244
298
  end
245
299
  end
246
- key = "#{name}:#{options[:font] || 0}"
300
+ key = "#{family}:#{name}:#{options[:font] || 0}"
247
301
 
248
- if name.is_a? Prawn::Font
302
+ if name.is_a?(Prawn::Font)
249
303
  font_registry[key] = name
250
304
  else
251
305
  font_registry[key] ||=
@@ -253,9 +307,11 @@ module Prawn
253
307
  end
254
308
  end
255
309
 
256
- # Hash of Font objects keyed by names
310
+ # Hash of Font objects keyed by names.
257
311
  #
258
- def font_registry #:nodoc:
312
+ # @private
313
+ # @return [Hash{String => Font}]
314
+ def font_registry
259
315
  @font_registry ||= {}
260
316
  end
261
317
 
@@ -278,32 +334,46 @@ module Prawn
278
334
 
279
335
  # Provides font information and helper functions.
280
336
  #
337
+ # @abstract
281
338
  class Font
282
- require_relative 'fonts/afm'
283
- require_relative 'fonts/ttf'
284
- require_relative 'fonts/dfont'
285
- require_relative 'fonts/otf'
286
- require_relative 'fonts/ttc'
339
+ require_relative 'fonts'
340
+
341
+ # @deprecated
342
+ AFM = Prawn::Fonts::AFM
287
343
 
288
344
  # @deprecated
289
- AFM = Fonts::AFM
290
345
  TTF = Fonts::TTF
346
+
347
+ # @deprecated
291
348
  DFont = Fonts::DFont
349
+
350
+ # @deprecated
292
351
  TTC = Fonts::TTC
293
352
 
294
- # The current font name
353
+ # The font name.
354
+ # @return [String]
295
355
  attr_reader :name
296
356
 
297
- # The current font family
357
+ # The font family.
358
+ # @return [String]
298
359
  attr_reader :family
299
360
 
300
- # The options hash used to initialize the font
361
+ # The options hash used to initialize the font.
362
+ # @return [Hash]
301
363
  attr_reader :options
302
364
 
303
- # Shortcut interface for constructing a font object. Filenames of the form
304
- # *.ttf will call Fonts::TTF.new, *.dfont Fonts::DFont.new, *.ttc goes to
305
- # Fonts::TTC.new, and anything else will be passed through to
306
- # Fonts::AFM.new()
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]
307
377
  def self.load(document, src, options = {})
308
378
  case font_format(src, options)
309
379
  when 'ttf' then TTF.new(document, src, options)
@@ -314,8 +384,15 @@ module Prawn
314
384
  end
315
385
  end
316
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]
317
394
  def self.font_format(src, options)
318
- return options.fetch(:format, 'ttf') if src.respond_to? :read
395
+ return options.fetch(:format, 'ttf') if src.respond_to?(:read)
319
396
 
320
397
  case src.to_s
321
398
  when /\.ttf$/i then 'ttf'
@@ -326,7 +403,13 @@ module Prawn
326
403
  end
327
404
  end
328
405
 
329
- def initialize(document, name, options = {}) #:nodoc:
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 = {})
330
413
  @document = document
331
414
  @name = name
332
415
  @options = options
@@ -336,54 +419,70 @@ module Prawn
336
419
  @identifier = generate_unique_id
337
420
 
338
421
  @references = {}
422
+ @subset_name_cache = {}
423
+
424
+ @full_font_embedding = options.key?(:subset) && !options[:subset]
339
425
  end
340
426
 
341
- # The size of the font ascender in PDF points
427
+ # The size of the font ascender in PDF points.
342
428
  #
429
+ # @return [Number]
343
430
  def ascender
344
431
  @ascender / 1000.0 * size
345
432
  end
346
433
 
347
- # The size of the font descender in PDF points
434
+ # The size of the font descender in PDF points.
348
435
  #
436
+ # @return [Number]
349
437
  def descender
350
438
  -@descender / 1000.0 * size
351
439
  end
352
440
 
353
441
  # The size of the recommended gap between lines of text in PDF points
354
442
  #
443
+ # @return [Number]
355
444
  def line_gap
356
445
  @line_gap / 1000.0 * size
357
446
  end
358
447
 
359
448
  # Normalizes the encoding of the string to an encoding supported by the
360
449
  # font. The string is expected to be UTF-8 going in. It will be re-encoded
361
- # and the new string will be returned. For an in-place (destructive)
362
- # version, see normalize_encoding!.
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]
363
456
  def normalize_encoding(_string)
364
457
  raise NotImplementedError,
365
458
  'subclasses of Prawn::Font must implement #normalize_encoding'
366
459
  end
367
460
 
368
- # Destructive version of normalize_encoding; normalizes the encoding of a
461
+ # Destructive version of {normalize_encoding}; normalizes the encoding of a
369
462
  # string in place.
370
463
  #
464
+ # @note This method doesn't mutate its argument any more.
465
+ #
371
466
  # @deprecated
467
+ # @param str [String]
468
+ # @return [String]
372
469
  def normalize_encoding!(str)
373
- warn 'Font#normalize_encoding! is deprecated. ' \
374
- 'Please use non-mutating version Font#normalize_encoding instead.'
470
+ warn('Font#normalize_encoding! is deprecated. Please use non-mutating version Font#normalize_encoding instead.')
375
471
  str.dup.replace(normalize_encoding(str))
376
472
  end
377
473
 
378
- # Gets height of current font in PDF points at the given font size
474
+ # Gets height of font in PDF points at the given font size.
379
475
  #
476
+ # @param size [Number]
477
+ # @return [Number]
380
478
  def height_at(size)
381
479
  @normalized_height ||= (@ascender - @descender + @line_gap) / 1000.0
382
480
  @normalized_height * size
383
481
  end
384
482
 
385
- # Gets height of current font in PDF points at current font size
483
+ # Gets height of current font in PDF points at current font size.
386
484
  #
485
+ # @return [Number]
387
486
  def height
388
487
  height_at(size)
389
488
  end
@@ -392,39 +491,53 @@ module Prawn
392
491
  # page. This is safe to call multiple times for a given font and subset,
393
492
  # as it will only add the font the first time it is called.
394
493
  #
494
+ # @param subset [Integer]
495
+ # @return [void]
395
496
  def add_to_current_page(subset)
396
497
  @references[subset] ||= register(subset)
397
- @document.state.page.fonts.merge!(
398
- identifier_for(subset) => @references[subset]
399
- )
498
+ @document.state.page.fonts[identifier_for(subset)] = @references[subset]
400
499
  end
401
500
 
402
- def identifier_for(subset) #:nodoc:
403
- "#{@identifier}.#{subset}".to_sym
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
404
511
  end
405
512
 
406
- def inspect #:nodoc:
513
+ # Returns a string containing a human-readable representation of this font.
514
+ #
515
+ # @return [String]
516
+ def inspect
407
517
  "#{self.class.name}< #{name}: #{size} >"
408
518
  end
409
519
 
410
- # Return a hash (as in Object#hash) for the font based on the output of
411
- # #inspect. This is required since font objects are used as keys in hashes
412
- # that cache certain values (See
413
- # Prawn::Table::Text#styled_with_of_single_character)
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.
414
522
  #
415
- def hash #:nodoc:
416
- [self.class, name, family, size].hash
523
+ # @return [Integer]
524
+ def hash
525
+ [self.class, name, family].hash
417
526
  end
418
527
 
419
- # Compliments the #hash implementation above
528
+ # Compliments the {#hash} implementation.
420
529
  #
421
- def eql?(other) #:nodoc:
530
+ # @param other [Object]
531
+ # @return [Boolean]
532
+ def eql?(other)
422
533
  self.class == other.class && name == other.name &&
423
534
  family == other.family && size == other.size
424
535
  end
425
536
 
426
537
  private
427
538
 
539
+ attr_reader :full_font_embedding
540
+
428
541
  # generate a font identifier that hasn't been used on the current page yet
429
542
  #
430
543
  def generate_unique_id
@@ -1,19 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # font_metric_cache.rb : The Prawn font class
4
- #
5
- # Copyright Dec 2012, Kenneth Kalmer. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- #
9
-
10
3
  module Prawn
11
- # Cache used internally by Prawn::Document instances to calculate the width
4
+ # Cache used internally by {Prawn::Document} instances to calculate the width
12
5
  # of various strings for layout purposes.
13
6
  #
14
7
  # @private
15
8
  class FontMetricCache
16
- CacheEntry = Struct.new(:font, :options, :string)
9
+ CacheEntry = Struct.new(:font, :font_size, :options, :string)
17
10
 
18
11
  def initialize(document)
19
12
  @document = document
@@ -21,6 +14,14 @@ module Prawn
21
14
  @cache = {}
22
15
  end
23
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]
24
25
  def width_of(string, options)
25
26
  f =
26
27
  if options[:style]
@@ -32,7 +33,7 @@ module Prawn
32
33
 
33
34
  encoded_string = f.normalize_encoding(string)
34
35
 
35
- key = CacheEntry.new(f, options, encoded_string)
36
+ key = CacheEntry.new(f, @document.font_size, options, encoded_string)
36
37
 
37
38
  @cache[key] ||= f.compute_width_of(encoded_string, options)
38
39