prawn 2.1.0 → 2.4.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 (280) 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 +9 -41
  7. data/lib/prawn.rb +37 -49
  8. data/lib/prawn/document.rb +193 -141
  9. data/lib/prawn/document/bounding_box.rb +50 -30
  10. data/lib/prawn/document/column_box.rb +7 -7
  11. data/lib/prawn/document/internals.rb +8 -6
  12. data/lib/prawn/document/span.rb +22 -16
  13. data/lib/prawn/encoding.rb +69 -68
  14. data/lib/prawn/errors.rb +12 -7
  15. data/lib/prawn/font.rb +104 -69
  16. data/lib/prawn/font_metric_cache.rb +20 -13
  17. data/lib/prawn/{font → fonts}/afm.rb +108 -72
  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 +126 -81
  22. data/lib/prawn/graphics.rb +119 -81
  23. data/lib/prawn/graphics/blend_mode.rb +9 -8
  24. data/lib/prawn/graphics/cap_style.rb +3 -3
  25. data/lib/prawn/graphics/color.rb +43 -39
  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 +204 -102
  29. data/lib/prawn/graphics/transformation.rb +15 -9
  30. data/lib/prawn/graphics/transparency.rb +17 -13
  31. data/lib/prawn/grid.rb +84 -48
  32. data/lib/prawn/image_handler.rb +5 -5
  33. data/lib/prawn/images.rb +60 -49
  34. data/lib/prawn/images/image.rb +2 -1
  35. data/lib/prawn/images/jpg.rb +31 -22
  36. data/lib/prawn/images/png.rb +67 -63
  37. data/lib/prawn/measurement_extensions.rb +10 -9
  38. data/lib/prawn/measurements.rb +19 -15
  39. data/lib/prawn/outline.rb +98 -77
  40. data/lib/prawn/repeater.rb +15 -11
  41. data/lib/prawn/security.rb +93 -70
  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 +76 -60
  46. data/lib/prawn/text/box.rb +18 -14
  47. data/lib/prawn/text/formatted.rb +5 -5
  48. data/lib/prawn/text/formatted/arranger.rb +80 -40
  49. data/lib/prawn/text/formatted/box.rb +140 -101
  50. data/lib/prawn/text/formatted/fragment.rb +11 -14
  51. data/lib/prawn/text/formatted/line_wrap.rb +128 -67
  52. data/lib/prawn/text/formatted/parser.rb +147 -123
  53. data/lib/prawn/text/formatted/wrap.rb +48 -32
  54. data/lib/prawn/transformation_stack.rb +7 -5
  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 +7 -8
  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 +7 -8
  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 +25 -18
  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 +15 -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 +18 -15
  82. data/manual/example_helper.rb +5 -4
  83. data/manual/graphics/blend_mode.rb +12 -9
  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 +5 -6
  88. data/manual/graphics/fill_rules.rb +10 -10
  89. data/manual/graphics/gradients.rb +27 -21
  90. data/manual/graphics/graphics.rb +48 -40
  91. data/manual/graphics/helper.rb +19 -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 +3 -4
  99. data/manual/graphics/stroke_cap.rb +6 -7
  100. data/manual/graphics/stroke_dash.rb +15 -16
  101. data/manual/graphics/stroke_join.rb +5 -6
  102. data/manual/graphics/translate.rb +10 -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 +10 -11
  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 +16 -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 +21 -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 +10 -8
  137. data/manual/text/formatted_callbacks.rb +33 -24
  138. data/manual/text/formatted_text.rb +36 -25
  139. data/manual/text/free_flowing_text.rb +22 -23
  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 +12 -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 +33 -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 +20 -19
  155. data/manual/text/utf8.rb +11 -12
  156. data/manual/text/win_ansi_charset.rb +27 -25
  157. data/prawn.gemspec +41 -34
  158. data/spec/extensions/encoding_helpers.rb +3 -3
  159. data/spec/prawn/document/bounding_box_spec.rb +550 -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 +44 -0
  167. data/spec/prawn/document_spec.rb +805 -0
  168. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  169. data/spec/prawn/font_spec.rb +544 -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 +872 -0
  173. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  174. data/spec/{image_handler_spec.rb → prawn/image_handler_spec.rb} +14 -14
  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 +229 -0
  178. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  179. data/spec/prawn/outline_spec.rb +512 -0
  180. data/spec/prawn/repeater_spec.rb +166 -0
  181. data/spec/prawn/soft_mask_spec.rb +74 -0
  182. data/spec/prawn/stamp_spec.rb +173 -0
  183. data/spec/prawn/text/box_spec.rb +1110 -0
  184. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  185. data/spec/prawn/text/formatted/box_spec.rb +849 -0
  186. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  187. data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
  188. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  189. data/spec/prawn/text_draw_text_spec.rb +150 -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/{transformation_stack_spec.rb → prawn/transformation_stack_spec.rb} +22 -19
  195. data/spec/prawn/view_spec.rb +63 -0
  196. data/spec/prawn_manual_spec.rb +35 -0
  197. data/spec/spec_helper.rb +18 -19
  198. metadata +102 -222
  199. metadata.gz.sig +0 -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/blend_modes_bottom_layer.jpg +0 -0
  206. data/data/images/blend_modes_top_layer.jpg +0 -0
  207. data/data/images/dice.alpha +0 -0
  208. data/data/images/dice.color +0 -0
  209. data/data/images/dice.png +0 -0
  210. data/data/images/dice_interlaced.png +0 -0
  211. data/data/images/fractal.jpg +0 -0
  212. data/data/images/indexed_color.dat +0 -0
  213. data/data/images/indexed_color.png +0 -0
  214. data/data/images/indexed_transparency.png +0 -0
  215. data/data/images/indexed_transparency_alpha.dat +0 -0
  216. data/data/images/indexed_transparency_color.dat +0 -0
  217. data/data/images/letterhead.jpg +0 -0
  218. data/data/images/license.md +0 -8
  219. data/data/images/page_white_text.alpha +0 -0
  220. data/data/images/page_white_text.color +0 -0
  221. data/data/images/page_white_text.png +0 -0
  222. data/data/images/pigs.jpg +0 -0
  223. data/data/images/prawn.png +0 -0
  224. data/data/images/ruport.png +0 -0
  225. data/data/images/ruport_data.dat +0 -0
  226. data/data/images/ruport_transparent.png +0 -0
  227. data/data/images/ruport_type0.png +0 -0
  228. data/data/images/stef.jpg +0 -0
  229. data/data/images/tru256.bmp +0 -0
  230. data/data/images/web-links.dat +0 -1
  231. data/data/images/web-links.png +0 -0
  232. data/data/pdfs/complex_template.pdf +0 -0
  233. data/data/pdfs/contains_ttf_font.pdf +0 -0
  234. data/data/pdfs/encrypted.pdf +0 -0
  235. data/data/pdfs/form.pdf +1 -819
  236. data/data/pdfs/hexagon.pdf +0 -61
  237. data/data/pdfs/indirect_reference.pdf +0 -86
  238. data/data/pdfs/multipage_template.pdf +0 -127
  239. data/data/pdfs/nested_pages.pdf +0 -118
  240. data/data/pdfs/page_without_mediabox.pdf +0 -193
  241. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  242. data/data/pdfs/two_hexagons.pdf +0 -90
  243. data/data/pdfs/version_1_6.pdf +0 -61
  244. data/data/shift_jis_text.txt +0 -1
  245. data/spec/acceptance/png_spec.rb +0 -35
  246. data/spec/annotations_spec.rb +0 -67
  247. data/spec/blend_mode_spec.rb +0 -71
  248. data/spec/bounding_box_spec.rb +0 -501
  249. data/spec/column_box_spec.rb +0 -59
  250. data/spec/destinations_spec.rb +0 -13
  251. data/spec/document_spec.rb +0 -738
  252. data/spec/font_metric_cache_spec.rb +0 -52
  253. data/spec/font_spec.rb +0 -475
  254. data/spec/formatted_text_arranger_spec.rb +0 -452
  255. data/spec/formatted_text_box_spec.rb +0 -716
  256. data/spec/formatted_text_fragment_spec.rb +0 -299
  257. data/spec/graphics_spec.rb +0 -705
  258. data/spec/grid_spec.rb +0 -95
  259. data/spec/images_spec.rb +0 -167
  260. data/spec/inline_formatted_text_parser_spec.rb +0 -568
  261. data/spec/jpg_spec.rb +0 -23
  262. data/spec/line_wrap_spec.rb +0 -366
  263. data/spec/measurement_units_spec.rb +0 -22
  264. data/spec/outline_spec.rb +0 -409
  265. data/spec/png_spec.rb +0 -257
  266. data/spec/reference_spec.rb +0 -25
  267. data/spec/repeater_spec.rb +0 -154
  268. data/spec/security_spec.rb +0 -151
  269. data/spec/soft_mask_spec.rb +0 -78
  270. data/spec/span_spec.rb +0 -43
  271. data/spec/stamp_spec.rb +0 -179
  272. data/spec/stroke_styles_spec.rb +0 -208
  273. data/spec/text_at_spec.rb +0 -142
  274. data/spec/text_box_spec.rb +0 -1042
  275. data/spec/text_rendering_mode_spec.rb +0 -45
  276. data/spec/text_spacing_spec.rb +0 -93
  277. data/spec/text_spec.rb +0 -543
  278. data/spec/text_with_inline_formatting_spec.rb +0 -35
  279. data/spec/transparency_spec.rb +0 -91
  280. 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.size
392
424
  end
393
425
 
394
426
  private
@@ -401,17 +433,20 @@ module Prawn
401
433
  loop do
402
434
  key = :"F#{font_count}"
403
435
  break if key_is_unique?(key)
436
+
404
437
  font_count += 1
405
438
  end
406
439
  key
407
440
  end
408
441
 
409
442
  def key_is_unique?(test_key)
410
- !@document.state.page.fonts.keys.any? do |key|
443
+ @document.state.page.fonts.keys.none? do |key|
411
444
  key.to_s.start_with?("#{test_key}.")
412
445
  end
413
446
  end
414
447
 
448
+ protected
449
+
415
450
  def size
416
451
  @document.font_size
417
452
  end
@@ -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.
@@ -22,21 +22,28 @@ module Prawn
22
22
  end
23
23
 
24
24
  def width_of(string, options)
25
- f = if options[:style]
26
- # override style with :style => :bold
27
- @document.find_font(@document.font.family, :style => options[:style])
28
- else
29
- @document.font
30
- end
25
+ f =
26
+ if options[:style]
27
+ # override style with :style => :bold
28
+ @document.find_font(@document.font.family, style: options[:style])
29
+ else
30
+ @document.font
31
+ end
32
+
33
+ encoded_string = f.normalize_encoding(string)
34
+
35
+ key = CacheEntry.new(f, options, encoded_string)
36
+
37
+ @cache[key] ||= f.compute_width_of(encoded_string, options)
31
38
 
32
- key = CacheEntry.new(f, options, string)
39
+ length = @cache[key]
33
40
 
34
- unless length = @cache[ key ]
35
- length = @cache[ key ] = f.compute_width_of(string, options)
41
+ character_count = @document.font.character_count(encoded_string)
42
+ if character_count.positive?
43
+ length += @document.character_spacing * (character_count - 1)
36
44
  end
37
45
 
38
- length +
39
- (@document.character_spacing * @document.font.character_count(string))
46
+ length
40
47
  end
41
48
  end
42
49
  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,62 @@ 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 ||=
35
+ if ENV['METRICS']
36
+ ENV['METRICS'].split(':')
37
+ else
38
+ [
39
+ '.', '/usr/lib/afm',
40
+ '/usr/local/lib/afm',
41
+ '/usr/openwin/lib/fonts/afm',
42
+ "#{Prawn::DATADIR}/fonts"
43
+ ]
44
+ end
42
45
  end
43
46
 
44
47
  attr_reader :attributes #:nodoc:
45
48
 
49
+ # parse each ATM font file once only
50
+ def self.font_data
51
+ @font_data ||= SynchronizedCache.new
52
+ end
53
+
46
54
  def initialize(document, name, options = {}) #:nodoc:
55
+ name ||= options[:family]
47
56
  unless BUILT_INS.include?(name)
48
- fail Prawn::Errors::UnknownFont, "#{name} is not a known font."
57
+ raise Prawn::Errors::UnknownFont,
58
+ "#{name} (#{options[:style] || 'normal'}) is not a known font."
49
59
  end
50
60
 
51
61
  super
52
62
 
53
- @@font_data ||= SynchronizedCache.new # parse each ATM font file once only
54
-
55
63
  file_name = @name.dup
56
- file_name << ".afm" unless file_name =~ /\.afm$/
64
+ file_name << '.afm' unless /\.afm$/.match?(file_name)
57
65
  file_name = file_name[0] == '/' ? file_name : find_font(file_name)
58
66
 
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]
67
+ font_data = self.class.font_data[file_name] ||= parse_afm(file_name)
68
+ @glyph_widths = font_data[:glyph_widths]
69
+ @glyph_table = font_data[:glyph_table]
70
+ @bounding_boxes = font_data[:bounding_boxes]
71
+ @kern_pairs = font_data[:kern_pairs]
64
72
  @kern_pair_table = font_data[:kern_pair_table]
65
- @attributes = font_data[:attributes]
73
+ @attributes = font_data[:attributes]
66
74
 
67
- @ascender = @attributes["ascender"].to_i
68
- @descender = @attributes["descender"].to_i
69
- @line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
75
+ @ascender = @attributes['ascender'].to_i
76
+ @descender = @attributes['descender'].to_i
77
+ @line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
70
78
  end
71
79
 
72
80
  # The font bbox, as an array of integers
@@ -81,7 +89,7 @@ module Prawn
81
89
 
82
90
  if options[:kerning]
83
91
  strings, numbers = kern(string).partition { |e| e.is_a?(String) }
84
- total_kerning_offset = numbers.inject(0.0) { |s, r| s + r }
92
+ total_kerning_offset = numbers.sum
85
93
  (unscaled_width_of(strings.join) - total_kerning_offset) * scale
86
94
  else
87
95
  unscaled_width_of(string) * scale
@@ -90,26 +98,30 @@ module Prawn
90
98
 
91
99
  # Returns true if the font has kerning data, false otherwise
92
100
  #
101
+ # rubocop: disable Naming/PredicateName
93
102
  def has_kerning_data?
94
103
  @kern_pairs.any?
95
104
  end
105
+ # rubocop: enable Naming/PredicateName
96
106
 
97
107
  # built-in fonts only work with winansi encoding, so translate the
98
108
  # string. Changes the encoding in-place, so the argument itself
99
109
  # is replaced with a string in WinAnsi encoding.
100
110
  #
101
111
  def normalize_encoding(text)
102
- text.encode("windows-1252")
112
+ text.encode('windows-1252')
103
113
  rescue ::Encoding::InvalidByteSequenceError,
104
114
  ::Encoding::UndefinedConversionError
105
115
 
106
116
  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."
117
+ "Your document includes text that's not compatible with the " \
118
+ "Windows-1252 character set.\n" \
119
+ 'If you need full UTF-8 support, use external fonts instead of ' \
120
+ "PDF's built-in fonts.\n"
109
121
  end
110
122
 
111
123
  def to_utf8(text)
112
- text.encode("UTF-8")
124
+ text.encode('UTF-8')
113
125
  end
114
126
 
115
127
  # Returns the number of characters in +str+ (a WinAnsi-encoded string).
@@ -135,45 +147,52 @@ module Prawn
135
147
  end
136
148
 
137
149
  def glyph_present?(char)
138
- !!normalize_encoding(char)
150
+ !normalize_encoding(char).nil?
139
151
  rescue Prawn::Errors::IncompatibleStringEncoding
140
152
  false
141
153
  end
142
154
 
143
155
  private
144
156
 
145
- def register(subset)
157
+ def register(_subset)
146
158
  font_dict = {
147
- :Type => :Font,
148
- :Subtype => :Type1,
149
- :BaseFont => name.to_sym
159
+ Type: :Font,
160
+ Subtype: :Type1,
161
+ BaseFont: name.to_sym
150
162
  }
151
163
 
152
164
  # Symbolic AFM fonts (Symbol, ZapfDingbats) have their own encodings
153
- font_dict.merge!(:Encoding => :WinAnsiEncoding) unless symbolic?
165
+ font_dict[:Encoding] = :WinAnsiEncoding unless symbolic?
154
166
 
155
167
  @document.ref!(font_dict)
156
168
  end
157
169
 
158
170
  def symbolic?
159
- attributes["characterset"] == "Special"
171
+ attributes['characterset'] == 'Special'
160
172
  end
161
173
 
162
174
  def find_font(file)
163
- self.class.metrics_path.find { |f| File.exist? "#{f}/#{file}" } + "/#{file}"
175
+ self.class.metrics_path.find { |f| File.exist? "#{f}/#{file}" } +
176
+ "/#{file}"
164
177
  rescue NoMethodError
165
178
  raise Prawn::Errors::UnknownFont,
166
- "Couldn't find the font: #{file} in any of:\n" + self.class.metrics_path.join("\n")
179
+ "Couldn't find the font: #{file} in any of:\n" +
180
+ self.class.metrics_path.join("\n")
167
181
  end
168
182
 
169
183
  def parse_afm(file_name)
170
- data = { :glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {} }
184
+ data = {
185
+ glyph_widths: {},
186
+ bounding_boxes: {},
187
+ kern_pairs: {},
188
+ attributes: {}
189
+ }
171
190
  section = []
172
191
 
173
192
  File.foreach(file_name) do |line|
174
193
  case line
175
194
  when /^Start(\w+)/
176
- section.push $1
195
+ section.push Regexp.last_match(1)
177
196
  next
178
197
  when /^End(\w+)/
179
198
  section.pop
@@ -181,17 +200,19 @@ module Prawn
181
200
  end
182
201
 
183
202
  case section
184
- when ["FontMetrics", "CharMetrics"]
185
- next unless line =~ /^CH?\s/
203
+ when %w[FontMetrics CharMetrics]
204
+ next unless /^CH?\s/.match?(line)
186
205
 
187
- name = line[/\bN\s+(\.?\w+)\s*;/, 1]
188
- data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
206
+ name = line[/\bN\s+(\.?\w+)\s*;/, 1]
207
+ data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
189
208
  data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
190
- when ["FontMetrics", "KernData", "KernPairs"]
209
+ when %w[FontMetrics KernData KernPairs]
191
210
  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"]
211
+
212
+ data[:kern_pairs][[Regexp.last_match(1), Regexp.last_match(2)]] =
213
+ Regexp.last_match(3).to_i
214
+ when %w[FontMetrics KernData TrackKern],
215
+ %w[FontMetrics Composites]
195
216
  next
196
217
  else
197
218
  parse_generic_afm_attribute(line, data)
@@ -200,14 +221,20 @@ module Prawn
200
221
 
201
222
  # process data parsed from AFM file to build tables which
202
223
  # will be used when measuring and kerning text
203
- data[:glyph_table] = (0..255).map do |i|
204
- data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
205
- end
224
+ data[:glyph_table] =
225
+ (0..255).map do |i|
226
+ data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
227
+ end
206
228
 
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
229
+ character_hash = Hash[
230
+ Encoding::WinAnsi::CHARACTERS.zip(
231
+ (0..Encoding::WinAnsi::CHARACTERS.size).to_a
232
+ )
233
+ ]
234
+ data[:kern_pair_table] =
235
+ data[:kern_pairs].each_with_object({}) do |p, h|
236
+ h[p[0].map { |n| character_hash[n] }] = p[1]
237
+ end
211
238
 
212
239
  data.each_value(&:freeze)
213
240
  data.freeze
@@ -215,9 +242,15 @@ module Prawn
215
242
 
216
243
  def parse_generic_afm_attribute(line, hash)
217
244
  line =~ /(^\w+)\s+(.*)/
218
- key, value = $1.to_s.downcase, $2
245
+ key = Regexp.last_match(1).to_s.downcase
246
+ value = Regexp.last_match(2)
219
247
 
220
- hash[:attributes][key] = hash[:attributes][key] ? Array(hash[:attributes][key]) << value : value
248
+ hash[:attributes][key] =
249
+ if hash[:attributes][key]
250
+ Array(hash[:attributes][key]) << value
251
+ else
252
+ value
253
+ end
221
254
  end
222
255
 
223
256
  # converts a string into an array with spacing offsets
@@ -230,7 +263,8 @@ module Prawn
230
263
  last_byte = nil
231
264
 
232
265
  string.each_byte do |byte|
233
- if k = last_byte && @kern_pair_table[[last_byte, byte]]
266
+ k = last_byte && @kern_pair_table[[last_byte, byte]]
267
+ if k
234
268
  kerned << -k << [byte]
235
269
  else
236
270
  kerned.last << byte
@@ -238,16 +272,18 @@ module Prawn
238
272
  last_byte = byte
239
273
  end
240
274
 
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
- }
275
+ kerned.map do |e|
276
+ e = e.is_a?(Array) ? e.pack('C*') : e
277
+ if e.respond_to?(:force_encoding)
278
+ e.force_encoding(::Encoding::Windows_1252)
279
+ else
280
+ e
281
+ end
282
+ end
245
283
  end
246
284
 
247
- private
248
-
249
285
  def unscaled_width_of(string)
250
- string.bytes.inject(0) do |s, r|
286
+ string.bytes.reduce(0) do |s, r|
251
287
  s + @glyph_table[r]
252
288
  end
253
289
  end