prawn 2.0.2 → 2.3.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 (277) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -9
  6. data/Rakefile +20 -23
  7. data/lib/prawn.rb +37 -49
  8. data/lib/prawn/document.rb +181 -133
  9. data/lib/prawn/document/bounding_box.rb +41 -29
  10. data/lib/prawn/document/column_box.rb +7 -7
  11. data/lib/prawn/document/internals.rb +18 -8
  12. data/lib/prawn/document/span.rb +21 -16
  13. data/lib/prawn/encoding.rb +69 -68
  14. data/lib/prawn/errors.rb +12 -7
  15. data/lib/prawn/font.rb +115 -69
  16. data/lib/prawn/font_metric_cache.rb +14 -8
  17. data/lib/prawn/{font → fonts}/afm.rb +102 -68
  18. data/lib/prawn/{font → fonts}/dfont.rb +5 -11
  19. data/lib/prawn/fonts/otf.rb +11 -0
  20. data/lib/prawn/fonts/ttc.rb +36 -0
  21. data/lib/prawn/{font → fonts}/ttf.rb +87 -68
  22. data/lib/prawn/graphics.rb +120 -80
  23. data/lib/prawn/graphics/blend_mode.rb +65 -0
  24. data/lib/prawn/graphics/cap_style.rb +3 -3
  25. data/lib/prawn/graphics/color.rb +27 -25
  26. data/lib/prawn/graphics/dash.rb +23 -11
  27. data/lib/prawn/graphics/join_style.rb +9 -3
  28. data/lib/prawn/graphics/patterns.rb +197 -67
  29. data/lib/prawn/graphics/transformation.rb +17 -8
  30. data/lib/prawn/graphics/transparency.rb +17 -13
  31. data/lib/prawn/grid.rb +48 -47
  32. data/lib/prawn/image_handler.rb +5 -5
  33. data/lib/prawn/images.rb +39 -30
  34. data/lib/prawn/images/image.rb +2 -1
  35. data/lib/prawn/images/jpg.rb +28 -22
  36. data/lib/prawn/images/png.rb +107 -66
  37. data/lib/prawn/measurement_extensions.rb +10 -9
  38. data/lib/prawn/measurements.rb +19 -15
  39. data/lib/prawn/outline.rb +97 -77
  40. data/lib/prawn/repeater.rb +14 -10
  41. data/lib/prawn/security.rb +81 -61
  42. data/lib/prawn/security/arcfour.rb +2 -2
  43. data/lib/prawn/soft_mask.rb +26 -26
  44. data/lib/prawn/stamp.rb +20 -13
  45. data/lib/prawn/text.rb +68 -52
  46. data/lib/prawn/text/box.rb +11 -8
  47. data/lib/prawn/text/formatted.rb +5 -5
  48. data/lib/prawn/text/formatted/arranger.rb +78 -49
  49. data/lib/prawn/text/formatted/box.rb +134 -100
  50. data/lib/prawn/text/formatted/fragment.rb +11 -14
  51. data/lib/prawn/text/formatted/line_wrap.rb +121 -63
  52. data/lib/prawn/text/formatted/parser.rb +139 -117
  53. data/lib/prawn/text/formatted/wrap.rb +43 -31
  54. data/lib/prawn/transformation_stack.rb +44 -0
  55. data/lib/prawn/utilities.rb +7 -22
  56. data/lib/prawn/version.rb +2 -2
  57. data/lib/prawn/view.rb +17 -7
  58. data/manual/basic_concepts/adding_pages.rb +6 -7
  59. data/manual/basic_concepts/basic_concepts.rb +31 -22
  60. data/manual/basic_concepts/creation.rb +10 -11
  61. data/manual/basic_concepts/cursor.rb +4 -5
  62. data/manual/basic_concepts/measurement.rb +6 -7
  63. data/manual/basic_concepts/origin.rb +5 -6
  64. data/manual/basic_concepts/other_cursor_helpers.rb +11 -12
  65. data/manual/basic_concepts/view.rb +22 -16
  66. data/manual/bounding_box/bounding_box.rb +29 -24
  67. data/manual/bounding_box/bounds.rb +11 -12
  68. data/manual/bounding_box/canvas.rb +4 -5
  69. data/manual/bounding_box/creation.rb +6 -7
  70. data/manual/bounding_box/indentation.rb +14 -15
  71. data/manual/bounding_box/nesting.rb +24 -17
  72. data/manual/bounding_box/russian_boxes.rb +14 -13
  73. data/manual/bounding_box/stretchy.rb +12 -13
  74. data/manual/contents.rb +28 -22
  75. data/manual/cover.rb +33 -28
  76. data/manual/document_and_page_options/background.rb +11 -13
  77. data/manual/document_and_page_options/document_and_page_options.rb +25 -20
  78. data/manual/document_and_page_options/metadata.rb +18 -16
  79. data/manual/document_and_page_options/page_margins.rb +18 -20
  80. data/manual/document_and_page_options/page_size.rb +13 -12
  81. data/manual/document_and_page_options/print_scaling.rb +17 -15
  82. data/manual/example_helper.rb +5 -4
  83. data/manual/graphics/blend_mode.rb +52 -0
  84. data/manual/graphics/circle_and_ellipse.rb +4 -5
  85. data/manual/graphics/color.rb +7 -9
  86. data/manual/graphics/common_lines.rb +7 -8
  87. data/manual/graphics/fill_and_stroke.rb +4 -5
  88. data/manual/graphics/fill_rules.rb +9 -10
  89. data/manual/graphics/gradients.rb +27 -21
  90. data/manual/graphics/graphics.rb +48 -39
  91. data/manual/graphics/helper.rb +12 -9
  92. data/manual/graphics/line_width.rb +8 -7
  93. data/manual/graphics/lines_and_curves.rb +7 -8
  94. data/manual/graphics/polygon.rb +6 -8
  95. data/manual/graphics/rectangle.rb +4 -5
  96. data/manual/graphics/rotate.rb +6 -7
  97. data/manual/graphics/scale.rb +14 -15
  98. data/manual/graphics/soft_masks.rb +4 -5
  99. data/manual/graphics/stroke_cap.rb +6 -7
  100. data/manual/graphics/stroke_dash.rb +11 -12
  101. data/manual/graphics/stroke_join.rb +5 -6
  102. data/manual/graphics/translate.rb +9 -10
  103. data/manual/graphics/transparency.rb +7 -8
  104. data/manual/how_to_read_this_manual.rb +6 -6
  105. data/manual/images/absolute_position.rb +6 -7
  106. data/manual/images/fit.rb +7 -8
  107. data/manual/images/horizontal.rb +9 -10
  108. data/manual/images/images.rb +28 -24
  109. data/manual/images/plain_image.rb +5 -6
  110. data/manual/images/scale.rb +9 -10
  111. data/manual/images/vertical.rb +13 -14
  112. data/manual/images/width_and_height.rb +10 -11
  113. data/manual/layout/boxes.rb +5 -6
  114. data/manual/layout/content.rb +7 -8
  115. data/manual/layout/layout.rb +18 -16
  116. data/manual/layout/simple_grid.rb +6 -7
  117. data/manual/outline/add_subsection_to.rb +20 -21
  118. data/manual/outline/insert_section_after.rb +15 -16
  119. data/manual/outline/outline.rb +21 -17
  120. data/manual/outline/sections_and_pages.rb +17 -18
  121. data/manual/repeatable_content/alternate_page_numbering.rb +21 -17
  122. data/manual/repeatable_content/page_numbering.rb +17 -16
  123. data/manual/repeatable_content/repeatable_content.rb +25 -19
  124. data/manual/repeatable_content/repeater.rb +14 -15
  125. data/manual/repeatable_content/stamp.rb +14 -15
  126. data/manual/security/encryption.rb +9 -10
  127. data/manual/security/permissions.rb +19 -14
  128. data/manual/security/security.rb +19 -16
  129. data/manual/table.rb +3 -3
  130. data/manual/text/alignment.rb +16 -17
  131. data/manual/text/color.rb +12 -11
  132. data/manual/text/column_box.rb +9 -10
  133. data/manual/text/fallback_fonts.rb +25 -21
  134. data/manual/text/font.rb +11 -12
  135. data/manual/text/font_size.rb +13 -14
  136. data/manual/text/font_style.rb +7 -8
  137. data/manual/text/formatted_callbacks.rb +25 -21
  138. data/manual/text/formatted_text.rb +33 -25
  139. data/manual/text/free_flowing_text.rb +20 -21
  140. data/manual/text/inline.rb +18 -19
  141. data/manual/text/kerning_and_character_spacing.rb +14 -15
  142. data/manual/text/leading.rb +7 -8
  143. data/manual/text/line_wrapping.rb +37 -18
  144. data/manual/text/paragraph_indentation.rb +13 -14
  145. data/manual/text/positioned_text.rb +15 -16
  146. data/manual/text/registering_families.rb +20 -21
  147. data/manual/text/rendering_and_color.rb +9 -10
  148. data/manual/text/right_to_left_text.rb +26 -19
  149. data/manual/text/rotation.rb +28 -23
  150. data/manual/text/single_usage.rb +8 -9
  151. data/manual/text/text.rb +57 -52
  152. data/manual/text/text_box_excess.rb +20 -17
  153. data/manual/text/text_box_extensions.rb +18 -15
  154. data/manual/text/text_box_overflow.rb +18 -19
  155. data/manual/text/utf8.rb +11 -12
  156. data/manual/text/win_ansi_charset.rb +21 -19
  157. data/prawn.gemspec +45 -33
  158. data/spec/extensions/encoding_helpers.rb +3 -3
  159. data/spec/prawn/document/bounding_box_spec.rb +546 -0
  160. data/spec/prawn/document/column_box_spec.rb +75 -0
  161. data/spec/prawn/document/security_spec.rb +176 -0
  162. data/spec/prawn/document_annotations_spec.rb +76 -0
  163. data/spec/prawn/document_destinations_spec.rb +15 -0
  164. data/spec/prawn/document_grid_spec.rb +99 -0
  165. data/spec/prawn/document_reference_spec.rb +27 -0
  166. data/spec/prawn/document_span_spec.rb +36 -0
  167. data/spec/prawn/document_spec.rb +802 -0
  168. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  169. data/spec/prawn/font_spec.rb +542 -0
  170. data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
  171. data/spec/prawn/graphics/transparency_spec.rb +81 -0
  172. data/spec/prawn/graphics_spec.rb +837 -0
  173. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  174. data/spec/prawn/image_handler_spec.rb +53 -0
  175. data/spec/prawn/images/jpg_spec.rb +20 -0
  176. data/spec/prawn/images/png_spec.rb +283 -0
  177. data/spec/prawn/images_spec.rb +224 -0
  178. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  179. data/spec/prawn/outline_spec.rb +412 -0
  180. data/spec/prawn/repeater_spec.rb +165 -0
  181. data/spec/prawn/soft_mask_spec.rb +74 -0
  182. data/spec/prawn/stamp_spec.rb +172 -0
  183. data/spec/prawn/text/box_spec.rb +1112 -0
  184. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  185. data/spec/prawn/text/formatted/box_spec.rb +846 -0
  186. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  187. data/spec/prawn/text/formatted/line_wrap_spec.rb +494 -0
  188. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  189. data/spec/prawn/text_draw_text_spec.rb +149 -0
  190. data/spec/prawn/text_rendering_mode_spec.rb +48 -0
  191. data/spec/prawn/text_spacing_spec.rb +95 -0
  192. data/spec/prawn/text_spec.rb +603 -0
  193. data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
  194. data/spec/prawn/transformation_stack_spec.rb +66 -0
  195. data/spec/prawn/view_spec.rb +63 -0
  196. data/spec/prawn_manual_spec.rb +35 -0
  197. data/spec/spec_helper.rb +19 -23
  198. metadata +145 -185
  199. metadata.gz.sig +4 -0
  200. data/data/images/16bit.alpha +0 -0
  201. data/data/images/16bit.color +0 -0
  202. data/data/images/16bit.png +0 -0
  203. data/data/images/arrow.png +0 -0
  204. data/data/images/arrow2.png +0 -0
  205. data/data/images/dice.alpha +0 -0
  206. data/data/images/dice.color +0 -0
  207. data/data/images/dice.png +0 -0
  208. data/data/images/dice_interlaced.png +0 -0
  209. data/data/images/fractal.jpg +0 -0
  210. data/data/images/indexed_color.dat +0 -0
  211. data/data/images/indexed_color.png +0 -0
  212. data/data/images/letterhead.jpg +0 -0
  213. data/data/images/license.md +0 -8
  214. data/data/images/page_white_text.alpha +0 -0
  215. data/data/images/page_white_text.color +0 -0
  216. data/data/images/page_white_text.png +0 -0
  217. data/data/images/pal_bk.png +0 -0
  218. data/data/images/pigs.jpg +0 -0
  219. data/data/images/prawn.png +0 -0
  220. data/data/images/ruport.png +0 -0
  221. data/data/images/ruport_data.dat +0 -0
  222. data/data/images/ruport_transparent.png +0 -0
  223. data/data/images/ruport_type0.png +0 -0
  224. data/data/images/stef.jpg +0 -0
  225. data/data/images/tru256.bmp +0 -0
  226. data/data/images/web-links.dat +0 -1
  227. data/data/images/web-links.png +0 -0
  228. data/data/pdfs/complex_template.pdf +0 -0
  229. data/data/pdfs/contains_ttf_font.pdf +0 -0
  230. data/data/pdfs/encrypted.pdf +0 -0
  231. data/data/pdfs/form.pdf +1 -819
  232. data/data/pdfs/hexagon.pdf +0 -61
  233. data/data/pdfs/indirect_reference.pdf +0 -86
  234. data/data/pdfs/multipage_template.pdf +0 -127
  235. data/data/pdfs/nested_pages.pdf +0 -118
  236. data/data/pdfs/page_without_mediabox.pdf +0 -193
  237. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  238. data/data/pdfs/two_hexagons.pdf +0 -90
  239. data/data/pdfs/version_1_6.pdf +0 -61
  240. data/data/shift_jis_text.txt +0 -1
  241. data/spec/acceptance/png.rb +0 -24
  242. data/spec/annotations_spec.rb +0 -67
  243. data/spec/bounding_box_spec.rb +0 -501
  244. data/spec/column_box_spec.rb +0 -59
  245. data/spec/destinations_spec.rb +0 -13
  246. data/spec/document_spec.rb +0 -742
  247. data/spec/extensions/mocha.rb +0 -45
  248. data/spec/font_metric_cache_spec.rb +0 -52
  249. data/spec/font_spec.rb +0 -475
  250. data/spec/formatted_text_arranger_spec.rb +0 -423
  251. data/spec/formatted_text_box_spec.rb +0 -716
  252. data/spec/formatted_text_fragment_spec.rb +0 -299
  253. data/spec/graphics_spec.rb +0 -666
  254. data/spec/grid_spec.rb +0 -95
  255. data/spec/image_handler_spec.rb +0 -53
  256. data/spec/images_spec.rb +0 -167
  257. data/spec/inline_formatted_text_parser_spec.rb +0 -568
  258. data/spec/jpg_spec.rb +0 -23
  259. data/spec/line_wrap_spec.rb +0 -366
  260. data/spec/measurement_units_spec.rb +0 -22
  261. data/spec/outline_spec.rb +0 -409
  262. data/spec/png_spec.rb +0 -235
  263. data/spec/reference_spec.rb +0 -25
  264. data/spec/repeater_spec.rb +0 -154
  265. data/spec/security_spec.rb +0 -151
  266. data/spec/soft_mask_spec.rb +0 -78
  267. data/spec/span_spec.rb +0 -43
  268. data/spec/stamp_spec.rb +0 -179
  269. data/spec/stroke_styles_spec.rb +0 -208
  270. data/spec/text_at_spec.rb +0 -142
  271. data/spec/text_box_spec.rb +0 -1038
  272. data/spec/text_rendering_mode_spec.rb +0 -45
  273. data/spec/text_spacing_spec.rb +0 -93
  274. data/spec/text_spec.rb +0 -549
  275. data/spec/text_with_inline_formatting_spec.rb +0 -35
  276. data/spec/transparency_spec.rb +0 -91
  277. data/spec/view_spec.rb +0 -42
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # errors.rb : Implements custom error classes for Prawn
4
4
  #
5
5
  # Copyright April 2008, Gregory Brown. All Rights Reserved.
@@ -43,9 +43,9 @@ module Prawn
43
43
  #
44
44
  UnknownOption = Class.new(StandardError)
45
45
 
46
- # this error is raised when a user attempts to embed an image of an unsupported
47
- # type. This can either a completely unsupported format, or a dialect of a
48
- # supported format (ie. some types of PNG)
46
+ # this error is raised when a user attempts to embed an image of an
47
+ # unsupported type. This can either a completely unsupported format, or
48
+ # a dialect of a supported format (ie. some types of PNG)
49
49
  UnsupportedImageType = Class.new(StandardError)
50
50
 
51
51
  # This error is raised when a named element has alredy been
@@ -63,17 +63,22 @@ module Prawn
63
63
  # This error is raised when a required option has not been set
64
64
  RequiredOption = Class.new(StandardError)
65
65
 
66
- # This error is raised when a requested outline item with a given title does not exist
66
+ # This error is raised when a requested outline item with a given title does
67
+ # not exist
67
68
  UnknownOutlineTitle = Class.new(StandardError)
68
69
 
69
70
  # This error is raised when a block is required, but not provided
70
71
  BlockRequired = Class.new(StandardError)
71
72
 
72
- # This error is rased when a graphics method is called with improper arguments
73
+ # This error is rased when a graphics method is called with improper
74
+ # arguments
73
75
  InvalidGraphicsPath = Class.new(StandardError)
74
76
 
75
77
  # Raised when unrecognized content is provided for a table cell.
76
78
  #
77
79
  UnrecognizedTableContent = Class.new(StandardError)
80
+
81
+ # This error is raised when an incompatible join style is specified
82
+ InvalidJoinStyle = Class.new(StandardError)
78
83
  end
79
84
  end
@@ -1,15 +1,12 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # font.rb : The Prawn font class
4
4
  #
5
5
  # Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
  #
9
- require_relative "font/afm"
10
- require_relative "font/ttf"
11
- require_relative "font/dfont"
12
- require_relative "font_metric_cache"
9
+ require_relative 'font_metric_cache'
13
10
 
14
11
  module Prawn
15
12
  class Document
@@ -35,8 +32,8 @@ module Prawn
35
32
  # end
36
33
  #
37
34
  # The :name parameter must be a string. It can be one of the 14 built-in
38
- # fonts supported by PDF, or the location of a TTF file. The Font::AFM::BUILT_INS
39
- # array specifies the valid built in font values.
35
+ # 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.
40
37
  #
41
38
  # If a ttf font is specified, the glyphs necessary to render your document
42
39
  # will be embedded in the rendered PDF. This should be your preferred option
@@ -44,14 +41,15 @@ module Prawn
44
41
  # make it more portable.
45
42
  #
46
43
  # The options parameter is an optional hash providing size and style. To use
47
- # the :style option you need to map those font styles to their respective font files.
44
+ # the :style option you need to map those font styles to their respective
45
+ # font files.
48
46
  # See font_families for more information.
49
47
  #
50
48
  def font(name = nil, options = {})
51
- return((defined?(@font) && @font) || font("Helvetica")) if name.nil?
49
+ return((defined?(@font) && @font) || font('Helvetica')) if name.nil?
52
50
 
53
51
  if state.pages.empty? && !state.page.in_stamp_stream?
54
- fail Prawn::Errors::NotOnPage
52
+ raise Prawn::Errors::NotOnPage
55
53
  end
56
54
 
57
55
  new_font = find_font(name.to_s, options)
@@ -96,6 +94,7 @@ module Prawn
96
94
  #
97
95
  def font_size(points = nil)
98
96
  return @font_size unless points
97
+
99
98
  size_before_yield = @font_size
100
99
  @font_size = points
101
100
  block_given? ? yield : return
@@ -107,33 +106,35 @@ module Prawn
107
106
  font_size(size)
108
107
  end
109
108
 
110
- # Returns the width of the given string using the given font. If :size is not
111
- # specified as one of the options, the string is measured using the current
112
- # font size. You can also pass :kerning as an option to indicate whether
113
- # kerning should be used when measuring the width (defaults to +false+).
109
+ # Returns the width of the given string using the given font. If :size is
110
+ # 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
112
+ # whether kerning should be used when measuring the width (defaults to
113
+ # +false+).
114
114
  #
115
115
  # Note that the string _must_ be encoded properly for the font being used.
116
116
  # For AFM fonts, this is WinAnsi. For TTF, make sure the font is encoded as
117
117
  # UTF-8. You can use the Font#normalize_encoding method to make sure strings
118
118
  # are in an encoding appropriate for the current font.
119
119
  #--
120
- # For the record, this method used to be a method of Font (and still delegates
121
- # to width computations on Font). However, having the primary interface for
122
- # calculating string widths exist on Font made it tricky to write extensions
123
- # for Prawn in which widths are computed differently (e.g., taking formatting
124
- # tags into account, or the like).
125
- #
126
- # By putting width_of here, on Document itself, extensions may easily override
127
- # it and redefine the width calculation behavior.
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
128
  #++
129
129
  def width_of(string, options = {})
130
- if p = options[:inline_format]
130
+ if options.key? :inline_format
131
+ p = options[:inline_format]
131
132
  p = [] unless p.is_a?(Array)
132
133
 
133
134
  # Build up an Arranger with the entire string on one line, finalize it,
134
135
  # and find its width.
135
136
  arranger = Prawn::Text::Formatted::Arranger.new(self, options)
136
- arranger.consumed = self.text_formatter.format(string, *p)
137
+ arranger.consumed = text_formatter.format(string, *p)
137
138
  arranger.finalize_line
138
139
 
139
140
  arranger.line_width
@@ -169,20 +170,26 @@ module Prawn
169
170
  #
170
171
  def font_families
171
172
  @font_families ||= {}.merge!(
172
- "Courier" => { :bold => "Courier-Bold",
173
- :italic => "Courier-Oblique",
174
- :bold_italic => "Courier-BoldOblique",
175
- :normal => "Courier" },
176
-
177
- "Times-Roman" => { :bold => "Times-Bold",
178
- :italic => "Times-Italic",
179
- :bold_italic => "Times-BoldItalic",
180
- :normal => "Times-Roman" },
181
-
182
- "Helvetica" => { :bold => "Helvetica-Bold",
183
- :italic => "Helvetica-Oblique",
184
- :bold_italic => "Helvetica-BoldOblique",
185
- :normal => "Helvetica" }
173
+ 'Courier' => {
174
+ bold: 'Courier-Bold',
175
+ italic: 'Courier-Oblique',
176
+ bold_italic: 'Courier-BoldOblique',
177
+ normal: 'Courier'
178
+ },
179
+
180
+ 'Times-Roman' => {
181
+ bold: 'Times-Bold',
182
+ italic: 'Times-Italic',
183
+ bold_italic: 'Times-BoldItalic',
184
+ normal: 'Times-Roman'
185
+ },
186
+
187
+ 'Helvetica' => {
188
+ bold: 'Helvetica-Bold',
189
+ italic: 'Helvetica-Oblique',
190
+ bold_italic: 'Helvetica-BoldOblique',
191
+ normal: 'Helvetica'
192
+ }
186
193
  )
187
194
  end
188
195
 
@@ -200,7 +207,7 @@ module Prawn
200
207
  # finishes, the original font is restored.
201
208
  #
202
209
  def save_font
203
- @font ||= find_font("Helvetica")
210
+ @font ||= find_font('Helvetica')
204
211
  original_font = @font
205
212
  original_size = @font_size
206
213
 
@@ -210,26 +217,27 @@ module Prawn
210
217
  end
211
218
 
212
219
  # Looks up the given font using the given criteria. Once a font has been
213
- # found by that matches the criteria, it will be cached to subsequent lookups
214
- # for that font will return the same object.
215
- #--
220
+ # found by that matches the criteria, it will be cached to subsequent
221
+ # lookups for that font will return the same object.
222
+ # --
216
223
  # Challenges involved: the name alone is not sufficient to uniquely identify
217
224
  # a font (think dfont suitcases that can hold multiple different fonts in a
218
225
  # single file). Thus, the :name key is included in the cache key.
219
226
  #
220
227
  # It is further complicated, however, since fonts in some formats (like the
221
228
  # dfont suitcases) can be identified either by numeric index, OR by their
222
- # name within the suitcase, and both should hash to the same font object
223
- # (to avoid the font being embedded multiple times). This is not yet implemented,
224
- # which means if someone selects a font both by name, and by index, the
225
- # font will be embedded twice. Since we do font subsetting, this double
226
- # embedding won't be catastrophic, just annoying.
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.
227
234
  # ++
228
235
  #
229
236
  # @private
230
237
  def find_font(name, options = {}) #:nodoc:
231
238
  if font_families.key?(name)
232
- family, name = name, font_families[name][options[:style] || :normal]
239
+ family = name
240
+ name = font_families[name][options[:style] || :normal]
233
241
  if name.is_a?(::Hash)
234
242
  options = options.merge(name)
235
243
  name = options[:file]
@@ -240,7 +248,8 @@ module Prawn
240
248
  if name.is_a? Prawn::Font
241
249
  font_registry[key] = name
242
250
  else
243
- font_registry[key] ||= Font.load(self, name, options.merge(family: family))
251
+ font_registry[key] ||=
252
+ Font.load(self, name, options.merge(family: family))
244
253
  end
245
254
  end
246
255
 
@@ -270,6 +279,18 @@ module Prawn
270
279
  # Provides font information and helper functions.
271
280
  #
272
281
  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'
287
+
288
+ # @deprecated
289
+ AFM = Fonts::AFM
290
+ TTF = Fonts::TTF
291
+ DFont = Fonts::DFont
292
+ TTC = Fonts::TTC
293
+
273
294
  # The current font name
274
295
  attr_reader :name
275
296
 
@@ -280,12 +301,15 @@ module Prawn
280
301
  attr_reader :options
281
302
 
282
303
  # Shortcut interface for constructing a font object. Filenames of the form
283
- # *.ttf will call Font::TTF.new, *.dfont Font::DFont.new, and anything else
284
- # will be passed through to Font::AFM.new()
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()
285
307
  def self.load(document, src, options = {})
286
308
  case font_format(src, options)
287
- when 'ttf' then TTF.new(document, src, options)
309
+ when 'ttf' then TTF.new(document, src, options)
310
+ when 'otf' then Fonts::OTF.new(document, src, options)
288
311
  when 'dfont' then DFont.new(document, src, options)
312
+ when 'ttc' then TTC.new(document, src, options)
289
313
  else AFM.new(document, src, options)
290
314
  end
291
315
  end
@@ -294,18 +318,20 @@ module Prawn
294
318
  return options.fetch(:format, 'ttf') if src.respond_to? :read
295
319
 
296
320
  case src.to_s
297
- when /\.ttf$/i then return 'ttf'
298
- when /\.dfont$/i then return 'dfont'
299
- else return 'afm'
321
+ when /\.ttf$/i then 'ttf'
322
+ when /\.otf$/i then 'otf'
323
+ when /\.dfont$/i then 'dfont'
324
+ when /\.ttc$/i then 'ttc'
325
+ else 'afm'
300
326
  end
301
327
  end
302
328
 
303
329
  def initialize(document, name, options = {}) #:nodoc:
304
- @document = document
305
- @name = name
306
- @options = options
330
+ @document = document
331
+ @name = name
332
+ @options = options
307
333
 
308
- @family = options[:family]
334
+ @family = options[:family]
309
335
 
310
336
  @identifier = generate_unique_id
311
337
 
@@ -334,15 +360,19 @@ module Prawn
334
360
  # font. The string is expected to be UTF-8 going in. It will be re-encoded
335
361
  # and the new string will be returned. For an in-place (destructive)
336
362
  # version, see normalize_encoding!.
337
- def normalize_encoding(string)
338
- fail NotImplementedError, "subclasses of Prawn::Font must implement #normalize_encoding"
363
+ def normalize_encoding(_string)
364
+ raise NotImplementedError,
365
+ 'subclasses of Prawn::Font must implement #normalize_encoding'
339
366
  end
340
367
 
341
368
  # Destructive version of normalize_encoding; normalizes the encoding of a
342
369
  # string in place.
343
370
  #
371
+ # @deprecated
344
372
  def normalize_encoding!(str)
345
- str.replace(normalize_encoding(str))
373
+ warn 'Font#normalize_encoding! is deprecated. ' \
374
+ 'Please use non-mutating version Font#normalize_encoding instead.'
375
+ str.dup.replace(normalize_encoding(str))
346
376
  end
347
377
 
348
378
  # Gets height of current font in PDF points at the given font size
@@ -364,11 +394,13 @@ module Prawn
364
394
  #
365
395
  def add_to_current_page(subset)
366
396
  @references[subset] ||= register(subset)
367
- @document.state.page.fonts.merge!(identifier_for(subset) => @references[subset])
397
+ @document.state.page.fonts.merge!(
398
+ identifier_for(subset) => @references[subset]
399
+ )
368
400
  end
369
401
 
370
402
  def identifier_for(subset) #:nodoc:
371
- "#{@identifier}.#{subset}"
403
+ "#{@identifier}.#{subset}".to_sym
372
404
  end
373
405
 
374
406
  def inspect #:nodoc:
@@ -381,14 +413,14 @@ module Prawn
381
413
  # Prawn::Table::Text#styled_with_of_single_character)
382
414
  #
383
415
  def hash #:nodoc:
384
- [ self.class, self.name, self.family, size ].hash
416
+ [self.class, name, family, size].hash
385
417
  end
386
418
 
387
419
  # Compliments the #hash implementation above
388
420
  #
389
421
  def eql?(other) #:nodoc:
390
- self.class == other.class && self.name == other.name &&
391
- self.family == other.family && size == other.send(:size)
422
+ self.class == other.class && name == other.name &&
423
+ family == other.family && size == other.send(:size)
392
424
  end
393
425
 
394
426
  private
@@ -396,7 +428,21 @@ module Prawn
396
428
  # generate a font identifier that hasn't been used on the current page yet
397
429
  #
398
430
  def generate_unique_id
399
- :"F#{@document.font_registry.size + 1}"
431
+ key = nil
432
+ font_count = @document.font_registry.size + 1
433
+ loop do
434
+ key = :"F#{font_count}"
435
+ break if key_is_unique?(key)
436
+
437
+ font_count += 1
438
+ end
439
+ key
440
+ end
441
+
442
+ def key_is_unique?(test_key)
443
+ @document.state.page.fonts.keys.none? do |key|
444
+ key.to_s.start_with?("#{test_key}.")
445
+ end
400
446
  end
401
447
 
402
448
  def size
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # font_metric_cache.rb : The Prawn font class
4
4
  #
5
5
  # Copyright Dec 2012, Kenneth Kalmer. All Rights Reserved.
@@ -24,19 +24,25 @@ module Prawn
24
24
  def width_of(string, options)
25
25
  f = if options[:style]
26
26
  # override style with :style => :bold
27
- @document.find_font(@document.font.family, :style => options[:style])
27
+ @document.find_font(@document.font.family, style: options[:style])
28
28
  else
29
29
  @document.font
30
30
  end
31
31
 
32
- key = CacheEntry.new(f, options, string)
32
+ encoded_string = f.normalize_encoding(string)
33
+
34
+ key = CacheEntry.new(f, options, encoded_string)
35
+
36
+ @cache[key] ||= f.compute_width_of(encoded_string, options)
37
+
38
+ length = @cache[key]
33
39
 
34
- unless length = @cache[ key ]
35
- length = @cache[ key ] = f.compute_width_of(string, options)
40
+ character_count = @document.font.character_count(encoded_string)
41
+ if character_count.positive?
42
+ length += @document.character_spacing * (character_count - 1)
36
43
  end
37
44
 
38
- length +
39
- (@document.character_spacing * @document.font.character_count(string))
45
+ length
40
46
  end
41
47
  end
42
48
  end
@@ -1,15 +1,15 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- # prawn/font/afm.rb : Implements AFM font support for Prawn
3
+ # Implements AFM font support for Prawn
4
4
  #
5
5
  # Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
- require_relative "../encoding"
9
+ require_relative '../encoding'
10
10
 
11
11
  module Prawn
12
- class Font
12
+ module Fonts
13
13
  # @private
14
14
 
15
15
  class AFM < Font
@@ -19,54 +19,61 @@ module Prawn
19
19
 
20
20
  self.hide_m17n_warning = false
21
21
 
22
- BUILT_INS = %w[ Courier Helvetica Times-Roman Symbol ZapfDingbats
23
- Courier-Bold Courier-Oblique Courier-BoldOblique
24
- Times-Bold Times-Italic Times-BoldItalic
25
- Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique ]
22
+ BUILT_INS = %w[
23
+ Courier Helvetica Times-Roman Symbol ZapfDingbats
24
+ Courier-Bold Courier-Oblique Courier-BoldOblique
25
+ Times-Bold Times-Italic Times-BoldItalic
26
+ Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique
27
+ ].freeze
26
28
 
27
29
  def unicode?
28
30
  false
29
31
  end
30
32
 
31
33
  def self.metrics_path
32
- if m = ENV['METRICS']
33
- @metrics_path ||= m.split(':')
34
- else
35
- @metrics_path ||= [
36
- ".", "/usr/lib/afm",
37
- "/usr/local/lib/afm",
38
- "/usr/openwin/lib/fonts/afm",
39
- Prawn::DATADIR + '/fonts'
40
- ]
41
- end
34
+ @metrics_path ||= if ENV['METRICS']
35
+ ENV['METRICS'].split(':')
36
+ else
37
+ [
38
+ '.', '/usr/lib/afm',
39
+ '/usr/local/lib/afm',
40
+ '/usr/openwin/lib/fonts/afm',
41
+ Prawn::DATADIR + '/fonts'
42
+ ]
43
+ end
42
44
  end
43
45
 
44
46
  attr_reader :attributes #:nodoc:
45
47
 
48
+ # parse each ATM font file once only
49
+ def self.font_data
50
+ @font_data ||= SynchronizedCache.new
51
+ end
52
+
46
53
  def initialize(document, name, options = {}) #:nodoc:
54
+ name ||= options[:family]
47
55
  unless BUILT_INS.include?(name)
48
- fail Prawn::Errors::UnknownFont, "#{name} is not a known font."
56
+ raise Prawn::Errors::UnknownFont,
57
+ "#{name} (#{options[:style] || 'normal'}) is not a known font."
49
58
  end
50
59
 
51
60
  super
52
61
 
53
- @@font_data ||= SynchronizedCache.new # parse each ATM font file once only
54
-
55
62
  file_name = @name.dup
56
- file_name << ".afm" unless file_name =~ /\.afm$/
63
+ file_name << '.afm' unless /\.afm$/.match?(file_name)
57
64
  file_name = file_name[0] == '/' ? file_name : find_font(file_name)
58
65
 
59
- font_data = @@font_data[file_name] ||= parse_afm(file_name)
60
- @glyph_widths = font_data[:glyph_widths]
61
- @glyph_table = font_data[:glyph_table]
62
- @bounding_boxes = font_data[:bounding_boxes]
63
- @kern_pairs = font_data[:kern_pairs]
66
+ font_data = self.class.font_data[file_name] ||= parse_afm(file_name)
67
+ @glyph_widths = font_data[:glyph_widths]
68
+ @glyph_table = font_data[:glyph_table]
69
+ @bounding_boxes = font_data[:bounding_boxes]
70
+ @kern_pairs = font_data[:kern_pairs]
64
71
  @kern_pair_table = font_data[:kern_pair_table]
65
- @attributes = font_data[:attributes]
72
+ @attributes = font_data[:attributes]
66
73
 
67
- @ascender = @attributes["ascender"].to_i
68
- @descender = @attributes["descender"].to_i
69
- @line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
74
+ @ascender = @attributes['ascender'].to_i
75
+ @descender = @attributes['descender'].to_i
76
+ @line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
70
77
  end
71
78
 
72
79
  # The font bbox, as an array of integers
@@ -81,7 +88,7 @@ module Prawn
81
88
 
82
89
  if options[:kerning]
83
90
  strings, numbers = kern(string).partition { |e| e.is_a?(String) }
84
- total_kerning_offset = numbers.inject(0.0) { |s, r| s + r }
91
+ total_kerning_offset = numbers.inject(0.0) { |a, e| a + e }
85
92
  (unscaled_width_of(strings.join) - total_kerning_offset) * scale
86
93
  else
87
94
  unscaled_width_of(string) * scale
@@ -90,26 +97,30 @@ module Prawn
90
97
 
91
98
  # Returns true if the font has kerning data, false otherwise
92
99
  #
100
+ # rubocop: disable Naming/PredicateName
93
101
  def has_kerning_data?
94
102
  @kern_pairs.any?
95
103
  end
104
+ # rubocop: enable Naming/PredicateName
96
105
 
97
106
  # built-in fonts only work with winansi encoding, so translate the
98
107
  # string. Changes the encoding in-place, so the argument itself
99
108
  # is replaced with a string in WinAnsi encoding.
100
109
  #
101
110
  def normalize_encoding(text)
102
- text.encode("windows-1252")
111
+ text.encode('windows-1252')
103
112
  rescue ::Encoding::InvalidByteSequenceError,
104
113
  ::Encoding::UndefinedConversionError
105
114
 
106
115
  raise Prawn::Errors::IncompatibleStringEncoding,
107
- "Your document includes text that's not compatible with the Windows-1252 character set.\n" \
108
- "If you need full UTF-8 support, use TTF fonts instead of PDF's built-in fonts\n."
116
+ "Your document includes text that's not compatible with the " \
117
+ "Windows-1252 character set.\n" \
118
+ 'If you need full UTF-8 support, use external fonts instead of ' \
119
+ "PDF's built-in fonts.\n"
109
120
  end
110
121
 
111
122
  def to_utf8(text)
112
- text.encode("UTF-8")
123
+ text.encode('UTF-8')
113
124
  end
114
125
 
115
126
  # Returns the number of characters in +str+ (a WinAnsi-encoded string).
@@ -135,45 +146,52 @@ module Prawn
135
146
  end
136
147
 
137
148
  def glyph_present?(char)
138
- !!normalize_encoding(char)
149
+ !normalize_encoding(char).nil?
139
150
  rescue Prawn::Errors::IncompatibleStringEncoding
140
151
  false
141
152
  end
142
153
 
143
154
  private
144
155
 
145
- def register(subset)
156
+ def register(_subset)
146
157
  font_dict = {
147
- :Type => :Font,
148
- :Subtype => :Type1,
149
- :BaseFont => name.to_sym
158
+ Type: :Font,
159
+ Subtype: :Type1,
160
+ BaseFont: name.to_sym
150
161
  }
151
162
 
152
163
  # Symbolic AFM fonts (Symbol, ZapfDingbats) have their own encodings
153
- font_dict.merge!(:Encoding => :WinAnsiEncoding) unless symbolic?
164
+ font_dict[:Encoding] = :WinAnsiEncoding unless symbolic?
154
165
 
155
166
  @document.ref!(font_dict)
156
167
  end
157
168
 
158
169
  def symbolic?
159
- attributes["characterset"] == "Special"
170
+ attributes['characterset'] == 'Special'
160
171
  end
161
172
 
162
173
  def find_font(file)
163
- self.class.metrics_path.find { |f| File.exist? "#{f}/#{file}" } + "/#{file}"
174
+ self.class.metrics_path.find { |f| File.exist? "#{f}/#{file}" } +
175
+ "/#{file}"
164
176
  rescue NoMethodError
165
177
  raise Prawn::Errors::UnknownFont,
166
- "Couldn't find the font: #{file} in any of:\n" + self.class.metrics_path.join("\n")
178
+ "Couldn't find the font: #{file} in any of:\n" +
179
+ self.class.metrics_path.join("\n")
167
180
  end
168
181
 
169
182
  def parse_afm(file_name)
170
- data = { :glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {} }
183
+ data = {
184
+ glyph_widths: {},
185
+ bounding_boxes: {},
186
+ kern_pairs: {},
187
+ attributes: {}
188
+ }
171
189
  section = []
172
190
 
173
191
  File.foreach(file_name) do |line|
174
192
  case line
175
193
  when /^Start(\w+)/
176
- section.push $1
194
+ section.push Regexp.last_match(1)
177
195
  next
178
196
  when /^End(\w+)/
179
197
  section.pop
@@ -181,17 +199,19 @@ module Prawn
181
199
  end
182
200
 
183
201
  case section
184
- when ["FontMetrics", "CharMetrics"]
185
- next unless line =~ /^CH?\s/
202
+ when %w[FontMetrics CharMetrics]
203
+ next unless /^CH?\s/.match?(line)
186
204
 
187
- name = line[/\bN\s+(\.?\w+)\s*;/, 1]
188
- data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
205
+ name = line[/\bN\s+(\.?\w+)\s*;/, 1]
206
+ data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
189
207
  data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
190
- when ["FontMetrics", "KernData", "KernPairs"]
208
+ when %w[FontMetrics KernData KernPairs]
191
209
  next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
192
- data[:kern_pairs][[$1, $2]] = $3.to_i
193
- when ["FontMetrics", "KernData", "TrackKern"],
194
- ["FontMetrics", "Composites"]
210
+
211
+ data[:kern_pairs][[Regexp.last_match(1), Regexp.last_match(2)]] =
212
+ Regexp.last_match(3).to_i
213
+ when %w[FontMetrics KernData TrackKern],
214
+ %w[FontMetrics Composites]
195
215
  next
196
216
  else
197
217
  parse_generic_afm_attribute(line, data)
@@ -204,10 +224,15 @@ module Prawn
204
224
  data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
205
225
  end
206
226
 
207
- character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
208
- data[:kern_pair_table] = data[:kern_pairs].each_with_object({}) do |p, h|
209
- h[p[0].map { |n| character_hash[n] }] = p[1]
210
- end
227
+ character_hash = Hash[
228
+ Encoding::WinAnsi::CHARACTERS.zip(
229
+ (0..Encoding::WinAnsi::CHARACTERS.size).to_a
230
+ )
231
+ ]
232
+ data[:kern_pair_table] =
233
+ data[:kern_pairs].each_with_object({}) do |p, h|
234
+ h[p[0].map { |n| character_hash[n] }] = p[1]
235
+ end
211
236
 
212
237
  data.each_value(&:freeze)
213
238
  data.freeze
@@ -215,9 +240,15 @@ module Prawn
215
240
 
216
241
  def parse_generic_afm_attribute(line, hash)
217
242
  line =~ /(^\w+)\s+(.*)/
218
- key, value = $1.to_s.downcase, $2
243
+ key = Regexp.last_match(1).to_s.downcase
244
+ value = Regexp.last_match(2)
219
245
 
220
- hash[:attributes][key] = hash[:attributes][key] ? Array(hash[:attributes][key]) << value : value
246
+ hash[:attributes][key] =
247
+ if hash[:attributes][key]
248
+ Array(hash[:attributes][key]) << value
249
+ else
250
+ value
251
+ end
221
252
  end
222
253
 
223
254
  # converts a string into an array with spacing offsets
@@ -230,7 +261,8 @@ module Prawn
230
261
  last_byte = nil
231
262
 
232
263
  string.each_byte do |byte|
233
- if k = last_byte && @kern_pair_table[[last_byte, byte]]
264
+ k = last_byte && @kern_pair_table[[last_byte, byte]]
265
+ if k
234
266
  kerned << -k << [byte]
235
267
  else
236
268
  kerned.last << byte
@@ -238,14 +270,16 @@ module Prawn
238
270
  last_byte = byte
239
271
  end
240
272
 
241
- kerned.map { |e|
242
- e = (Array === e ? e.pack("C*") : e)
243
- e.respond_to?(:force_encoding) ? e.force_encoding(::Encoding::Windows_1252) : e
244
- }
273
+ kerned.map do |e|
274
+ e = e.is_a?(Array) ? e.pack('C*') : e
275
+ if e.respond_to?(:force_encoding)
276
+ e.force_encoding(::Encoding::Windows_1252)
277
+ else
278
+ e
279
+ end
280
+ end
245
281
  end
246
282
 
247
- private
248
-
249
283
  def unscaled_width_of(string)
250
284
  string.bytes.inject(0) do |s, r|
251
285
  s + @glyph_table[r]