prawn 2.3.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 (91) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +2 -31
  5. data/lib/prawn.rb +1 -0
  6. data/lib/prawn/document.rb +20 -15
  7. data/lib/prawn/document/bounding_box.rb +10 -2
  8. data/lib/prawn/document/span.rb +2 -1
  9. data/lib/prawn/font.rb +6 -4
  10. data/lib/prawn/font_metric_cache.rb +7 -6
  11. data/lib/prawn/fonts/afm.rb +18 -16
  12. data/lib/prawn/fonts/ttf.rb +55 -29
  13. data/lib/prawn/graphics.rb +11 -11
  14. data/lib/prawn/graphics/color.rb +18 -16
  15. data/lib/prawn/graphics/join_style.rb +1 -1
  16. data/lib/prawn/graphics/patterns.rb +57 -49
  17. data/lib/prawn/graphics/transformation.rb +3 -3
  18. data/lib/prawn/grid.rb +36 -1
  19. data/lib/prawn/images.rb +29 -27
  20. data/lib/prawn/images/jpg.rb +4 -1
  21. data/lib/prawn/images/png.rb +2 -1
  22. data/lib/prawn/outline.rb +6 -5
  23. data/lib/prawn/repeater.rb +1 -1
  24. data/lib/prawn/security.rb +39 -36
  25. data/lib/prawn/text.rb +18 -18
  26. data/lib/prawn/text/box.rb +10 -9
  27. data/lib/prawn/text/formatted/arranger.rb +38 -19
  28. data/lib/prawn/text/formatted/box.rb +20 -15
  29. data/lib/prawn/text/formatted/line_wrap.rb +18 -16
  30. data/lib/prawn/text/formatted/parser.rb +28 -26
  31. data/lib/prawn/text/formatted/wrap.rb +15 -11
  32. data/lib/prawn/version.rb +1 -1
  33. data/lib/prawn/view.rb +2 -2
  34. data/manual/basic_concepts/measurement.rb +1 -1
  35. data/manual/bounding_box/canvas.rb +3 -3
  36. data/manual/bounding_box/nesting.rb +1 -1
  37. data/manual/document_and_page_options/background.rb +6 -2
  38. data/manual/document_and_page_options/document_and_page_options.rb +3 -3
  39. data/manual/document_and_page_options/print_scaling.rb +2 -1
  40. data/manual/graphics/common_lines.rb +1 -1
  41. data/manual/graphics/fill_and_stroke.rb +1 -1
  42. data/manual/graphics/fill_rules.rb +4 -3
  43. data/manual/graphics/helper.rb +11 -4
  44. data/manual/graphics/lines_and_curves.rb +1 -1
  45. data/manual/graphics/stroke_dash.rb +5 -5
  46. data/manual/graphics/translate.rb +2 -1
  47. data/manual/images/horizontal.rb +2 -2
  48. data/manual/images/scale.rb +3 -3
  49. data/manual/images/vertical.rb +6 -3
  50. data/manual/images/width_and_height.rb +3 -3
  51. data/manual/layout/content.rb +2 -2
  52. data/manual/outline/outline.rb +1 -1
  53. data/manual/security/permissions.rb +4 -2
  54. data/manual/security/security.rb +1 -1
  55. data/manual/text/alignment.rb +2 -2
  56. data/manual/text/column_box.rb +2 -2
  57. data/manual/text/font_style.rb +5 -2
  58. data/manual/text/formatted_callbacks.rb +17 -12
  59. data/manual/text/formatted_text.rb +7 -4
  60. data/manual/text/free_flowing_text.rb +3 -3
  61. data/manual/text/kerning_and_character_spacing.rb +4 -4
  62. data/manual/text/paragraph_indentation.rb +4 -5
  63. data/manual/text/rendering_and_color.rb +1 -1
  64. data/manual/text/right_to_left_text.rb +6 -6
  65. data/manual/text/rotation.rb +8 -3
  66. data/manual/text/text_box_extensions.rb +1 -1
  67. data/manual/text/text_box_overflow.rb +11 -9
  68. data/manual/text/win_ansi_charset.rb +9 -9
  69. data/prawn.gemspec +4 -10
  70. data/spec/prawn/document/bounding_box_spec.rb +82 -78
  71. data/spec/prawn/document/security_spec.rb +1 -1
  72. data/spec/prawn/document_span_spec.rb +14 -6
  73. data/spec/prawn/document_spec.rb +29 -26
  74. data/spec/prawn/font_spec.rb +16 -14
  75. data/spec/prawn/graphics_spec.rb +79 -44
  76. data/spec/prawn/images_spec.rb +13 -8
  77. data/spec/prawn/outline_spec.rb +127 -27
  78. data/spec/prawn/repeater_spec.rb +9 -8
  79. data/spec/prawn/soft_mask_spec.rb +1 -1
  80. data/spec/prawn/stamp_spec.rb +3 -2
  81. data/spec/prawn/text/box_spec.rb +57 -59
  82. data/spec/prawn/text/formatted/arranger_spec.rb +10 -10
  83. data/spec/prawn/text/formatted/box_spec.rb +8 -5
  84. data/spec/prawn/text/formatted/line_wrap_spec.rb +2 -1
  85. data/spec/prawn/text_draw_text_spec.rb +9 -8
  86. data/spec/prawn/text_spacing_spec.rb +2 -2
  87. data/spec/prawn/text_spec.rb +9 -9
  88. data/spec/prawn/text_with_inline_formatting_spec.rb +1 -1
  89. data/spec/prawn_manual_spec.rb +4 -4
  90. metadata +14 -98
  91. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dd091ffaaed7c69fe8a0353317736d1f145e91f3f133a6d04ed0cffd7b5eb37
4
- data.tar.gz: 98d1b0c973c9bf1947681ea971240fe9c351a9bcc1a74c91e7f8f677510eb11c
3
+ metadata.gz: d64c2da1e4be48e40f4fa6fa4de35e04c75debf0119e8c59b3fba9d944c112db
4
+ data.tar.gz: 393bbdd1a5df657d5c6190d4bd10ecd7353522e714b2f60c8d9c5f70e5bc1ce9
5
5
  SHA512:
6
- metadata.gz: 9b75677d2586bf8a812549075cc9532bc02f78dc18b0e0db99711ad1087586f4fef6ae2f244e96a73366172410357d19e9371ac7b7eacf4a3bcbf85292ffb028
7
- data.tar.gz: 138ac861e5016c49836a882c8beda2d2ed250e63959307ffe720624d322fd92b21ca71945e9cf58d0439851f8ebec515d13fb5f1d1660a3c9f4c996c5d04e701
6
+ metadata.gz: c4980639ca24796d577be790ed0f6fa85c985f1a6eb147e4c5db1df261c5d50afe8a466f06b65588b71a897e22e8d2b7b01daceed1714f6164124057154d2d28
7
+ data.tar.gz: e0fd560d4a80f2827736d599266bcb2e2fe19abedcde639520fba8bd19a2f12fe2cf0e8a60a91b87dbac4336d25c680f94b2341e5f9ce3d6d24f2e65f24e24a3
Binary file
data.tar.gz.sig CHANGED
Binary file
data/Rakefile CHANGED
@@ -1,23 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rake'
4
- require 'rspec/core/rake_task'
5
- require 'yard'
6
- require 'rubygems/package_task'
7
- require 'rubocop/rake_task'
3
+ GEMSPEC = File.expand_path('prawn.gemspec', __dir__)
4
+ require 'prawn/dev/tasks'
8
5
 
9
6
  task default: %i[spec rubocop]
10
7
 
11
- desc 'Run all rspec files'
12
- RSpec::Core::RakeTask.new('spec') do |c|
13
- c.rspec_opts = '-t ~unresolved'
14
- end
15
-
16
- YARD::Rake::YardocTask.new do |t|
17
- t.options = ['--output-dir', 'doc/html']
18
- end
19
- task docs: :yard
20
-
21
8
  desc "Generate the 'Prawn by Example' manual"
22
9
  task :manual do
23
10
  puts 'Building manual...'
@@ -26,12 +13,6 @@ task :manual do
26
13
  puts 'The Prawn manual is available at manual.pdf. Happy Prawning!'
27
14
  end
28
15
 
29
- spec = Gem::Specification.load 'prawn.gemspec'
30
- Gem::PackageTask.new(spec) do |pkg|
31
- pkg.need_zip = true
32
- pkg.need_tar = true
33
- end
34
-
35
16
  desc 'Run a console with Prawn loaded'
36
17
  task :console do
37
18
  require 'irb'
@@ -42,13 +23,3 @@ task :console do
42
23
  ARGV.clear
43
24
  IRB.start
44
25
  end
45
-
46
- RuboCop::RakeTask.new
47
-
48
- task :checksum do
49
- require 'digest/sha2'
50
- built_gem_path = "prawn-#{Prawn::VERSION}.gem"
51
- checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path))
52
- checksum_path = "checksums/#{built_gem_path}.sha512"
53
- File.write(checksum_path, checksum)
54
- end
@@ -32,6 +32,7 @@ module Prawn
32
32
  #
33
33
  # @private
34
34
  attr_accessor :debug
35
+
35
36
  module_function :debug, :debug=
36
37
 
37
38
  module_function
@@ -98,6 +98,7 @@ module Prawn
98
98
 
99
99
  # @private
100
100
  def self.inherited(base)
101
+ super
101
102
  extensions.each { |e| base.extensions << e }
102
103
  end
103
104
 
@@ -570,11 +571,12 @@ module Prawn
570
571
  opts = options.dup
571
572
  start_count_at = opts.delete(:start_count_at).to_i
572
573
 
573
- page_filter = if opts.key?(:page_filter)
574
- opts.delete(:page_filter)
575
- else
576
- :all
577
- end
574
+ page_filter =
575
+ if opts.key?(:page_filter)
576
+ opts.delete(:page_filter)
577
+ else
578
+ :all
579
+ end
578
580
 
579
581
  total_pages = opts.delete(:total_pages)
580
582
  txtcolor = opts.delete(:color)
@@ -585,12 +587,13 @@ module Prawn
585
587
  pseudopage = 0
586
588
  (1..page_count).each do |p|
587
589
  unless start_count
588
- pseudopage = case start_count_at
589
- when 0
590
- 1
591
- else
592
- start_count_at.to_i
593
- end
590
+ pseudopage =
591
+ case start_count_at
592
+ when 0
593
+ 1
594
+ else
595
+ start_count_at.to_i
596
+ end
594
597
  end
595
598
  if page_match?(page_filter, p)
596
599
  go_to_page(p)
@@ -668,9 +671,9 @@ module Prawn
668
671
  # and then restores the original values after the block has executed.
669
672
  # -- I will remove the nodoc if/when this feature is a little less hacky
670
673
  stored = {}
671
- fields.each { |f| stored[f] = send(f) }
674
+ fields.each { |f| stored[f] = public_send(f) }
672
675
  yield
673
- fields.each { |f| send("#{f}=", stored[f]) }
676
+ fields.each { |f| public_send("#{f}=", stored[f]) }
674
677
  end
675
678
 
676
679
  # @group Extension API
@@ -737,8 +740,10 @@ module Prawn
737
740
 
738
741
  # Treat :margin as CSS shorthand with 1-4 values.
739
742
  positions = {
740
- 4 => [0, 1, 2, 3], 3 => [0, 1, 2, 1],
741
- 2 => [0, 1, 0, 1], 1 => [0, 0, 0, 0],
743
+ 4 => [0, 1, 2, 3],
744
+ 3 => [0, 1, 2, 1],
745
+ 2 => [0, 1, 0, 1],
746
+ 1 => [0, 0, 0, 0],
742
747
  0 => []
743
748
  }[margin.length]
744
749
 
@@ -173,7 +173,9 @@ module Prawn
173
173
  init_bounding_box(block, hold_position: true) do |_|
174
174
  # Canvas bbox acts like margin_box in that its parent bounds are unset.
175
175
  @bounding_box = BoundingBox.new(
176
- self, nil, [0, page.dimensions[3]],
176
+ self,
177
+ nil,
178
+ [0, page.dimensions[3]],
177
179
  width: page.dimensions[2],
178
180
  height: page.dimensions[3]
179
181
  )
@@ -221,6 +223,12 @@ module Prawn
221
223
  # is used for.
222
224
  #
223
225
  class BoundingBox
226
+ class NoReferenceBounds < StandardError
227
+ def initialize(message = "Can't find reference bounds: my parent is unset")
228
+ super
229
+ end
230
+ end
231
+
224
232
  # @private
225
233
  def initialize(document, parent, point, options = {})
226
234
  unless options[:width]
@@ -504,7 +512,7 @@ module Prawn
504
512
  #
505
513
  def reference_bounds
506
514
  if stretchy?
507
- raise "Can't find reference bounds: my parent is unset" unless @parent
515
+ raise NoReferenceBounds unless @parent
508
516
 
509
517
  @parent.reference_bounds
510
518
  else
@@ -51,7 +51,8 @@ module Prawn
51
51
  [
52
52
  left_boundary,
53
53
  margin_box.absolute_top
54
- ], width: width
54
+ ],
55
+ width: width
55
56
  ) do
56
57
  self.y = original_position
57
58
  yield
@@ -318,10 +318,10 @@ module Prawn
318
318
  return options.fetch(:format, 'ttf') if src.respond_to? :read
319
319
 
320
320
  case src.to_s
321
- when /\.ttf$/i then 'ttf'
322
- when /\.otf$/i then 'otf'
321
+ when /\.ttf$/i then 'ttf'
322
+ when /\.otf$/i then 'otf'
323
323
  when /\.dfont$/i then 'dfont'
324
- when /\.ttc$/i then 'ttc'
324
+ when /\.ttc$/i then 'ttc'
325
325
  else 'afm'
326
326
  end
327
327
  end
@@ -420,7 +420,7 @@ module Prawn
420
420
  #
421
421
  def eql?(other) #:nodoc:
422
422
  self.class == other.class && name == other.name &&
423
- family == other.family && size == other.send(:size)
423
+ family == other.family && size == other.size
424
424
  end
425
425
 
426
426
  private
@@ -445,6 +445,8 @@ module Prawn
445
445
  end
446
446
  end
447
447
 
448
+ protected
449
+
448
450
  def size
449
451
  @document.font_size
450
452
  end
@@ -22,12 +22,13 @@ 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
31
32
 
32
33
  encoded_string = f.normalize_encoding(string)
33
34
 
@@ -31,16 +31,17 @@ module Prawn
31
31
  end
32
32
 
33
33
  def self.metrics_path
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
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
44
45
  end
45
46
 
46
47
  attr_reader :attributes #:nodoc:
@@ -88,7 +89,7 @@ module Prawn
88
89
 
89
90
  if options[:kerning]
90
91
  strings, numbers = kern(string).partition { |e| e.is_a?(String) }
91
- total_kerning_offset = numbers.inject(0.0) { |a, e| a + e }
92
+ total_kerning_offset = numbers.sum
92
93
  (unscaled_width_of(strings.join) - total_kerning_offset) * scale
93
94
  else
94
95
  unscaled_width_of(string) * scale
@@ -176,7 +177,7 @@ module Prawn
176
177
  rescue NoMethodError
177
178
  raise Prawn::Errors::UnknownFont,
178
179
  "Couldn't find the font: #{file} in any of:\n" +
179
- self.class.metrics_path.join("\n")
180
+ self.class.metrics_path.join("\n")
180
181
  end
181
182
 
182
183
  def parse_afm(file_name)
@@ -220,9 +221,10 @@ module Prawn
220
221
 
221
222
  # process data parsed from AFM file to build tables which
222
223
  # will be used when measuring and kerning text
223
- data[:glyph_table] = (0..255).map do |i|
224
- data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
225
- end
224
+ data[:glyph_table] =
225
+ (0..255).map do |i|
226
+ data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
227
+ end
226
228
 
227
229
  character_hash = Hash[
228
230
  Encoding::WinAnsi::CHARACTERS.zip(
@@ -281,7 +283,7 @@ module Prawn
281
283
  end
282
284
 
283
285
  def unscaled_width_of(string)
284
- string.bytes.inject(0) do |s, r|
286
+ string.bytes.reduce(0) do |s, r|
285
287
  s + @glyph_table[r]
286
288
  end
287
289
  end
@@ -14,6 +14,29 @@ module Prawn
14
14
  module Fonts
15
15
  # @private
16
16
  class TTF < Font
17
+ class Error < StandardError
18
+ DEFAULT_MESSAGE = 'TTF font error'
19
+ MESSAGE_WITH_FONT = 'TTF font error in font %<font>s'
20
+
21
+ def initialize(message = DEFAULT_MESSAGE, font: nil)
22
+ if font && message == DEFAULT_MESSAGE
23
+ super format(MESSAGE_WITH_FONT, font: font)
24
+ else
25
+ super message
26
+ end
27
+ end
28
+ end
29
+
30
+ class NoUnicodeCMap < Error
31
+ DEFAULT_MESSAGE = 'No unicode cmap found in font'
32
+ MESSAGE_WITH_FONT = 'No unicode cmap found in font %<font>s'
33
+ end
34
+
35
+ class NoPostscriptName < Error
36
+ DEFAULT_MESSAGE = 'Can not detect a postscript name'
37
+ MESSAGE_WITH_FONT = 'Can not detect a postscript name in font %<font>s'
38
+ end
39
+
17
40
  attr_reader :ttf, :subsets
18
41
 
19
42
  def unicode?
@@ -41,15 +64,15 @@ module Prawn
41
64
  def compute_width_of(string, options = {}) #:nodoc:
42
65
  scale = (options[:size] || size) / 1000.0
43
66
  if options[:kerning]
44
- kern(string).inject(0) do |s, r|
67
+ kern(string).reduce(0) do |s, r|
45
68
  if r.is_a?(Numeric)
46
69
  s - r
47
70
  else
48
- r.inject(s) { |a, e| a + character_width_by_code(e) }
71
+ r.reduce(s) { |a, e| a + character_width_by_code(e) }
49
72
  end
50
73
  end * scale
51
74
  else
52
- string.codepoints.inject(0) do |s, r|
75
+ string.codepoints.reduce(0) do |s, r|
53
76
  s + character_width_by_code(r)
54
77
  end * scale
55
78
  end
@@ -82,7 +105,7 @@ module Prawn
82
105
 
83
106
  if options[:kerning]
84
107
  last_subset = nil
85
- kern(text).inject([]) do |result, element|
108
+ kern(text).reduce([]) do |result, element|
86
109
  if element.is_a?(Numeric)
87
110
  unless result.last[1].is_a?(Array)
88
111
  result.last[1] = [result.last[1]]
@@ -136,10 +159,11 @@ module Prawn
136
159
  end
137
160
 
138
161
  def cap_height
139
- @cap_height ||= begin
140
- height = @ttf.os2.exists? && @ttf.os2.cap_height || 0
141
- height.zero? ? @ascender : height
142
- end
162
+ @cap_height ||=
163
+ begin
164
+ height = @ttf.os2.exists? && @ttf.os2.cap_height || 0
165
+ height.zero? ? @ascender : height
166
+ end
143
167
  end
144
168
 
145
169
  def x_height
@@ -161,23 +185,24 @@ module Prawn
161
185
  end
162
186
 
163
187
  def pdf_flags
164
- @pdf_flags ||= begin
165
- flags = 0
166
- flags |= 0x0001 if @ttf.postscript.fixed_pitch?
167
- flags |= 0x0002 if serif?
168
- flags |= 0x0008 if script?
169
- flags |= 0x0040 if italic_angle != 0
170
- # Assume the font contains at least some non-latin characters
171
- flags | 0x0004
172
- end
188
+ @pdf_flags ||=
189
+ begin
190
+ flags = 0
191
+ flags |= 0x0001 if @ttf.postscript.fixed_pitch?
192
+ flags |= 0x0002 if serif?
193
+ flags |= 0x0008 if script?
194
+ flags |= 0x0040 if italic_angle != 0
195
+ # Assume the font contains at least some non-latin characters
196
+ flags | 0x0004
197
+ end
173
198
  end
174
199
 
175
200
  def normalize_encoding(text)
176
201
  text.encode(::Encoding::UTF_8)
177
202
  rescue StandardError => e
178
203
  puts e
179
- raise Prawn::Errors::IncompatibleStringEncoding, 'Encoding ' \
180
- "#{text.encoding} can not be transparently converted to UTF-8. " \
204
+ raise Prawn::Errors::IncompatibleStringEncoding,
205
+ "Encoding #{text.encoding} can not be transparently converted to UTF-8. " \
181
206
  'Please ensure the encoding of the string you are attempting ' \
182
207
  'to use is set correctly'
183
208
  end
@@ -200,7 +225,7 @@ module Prawn
200
225
  private
201
226
 
202
227
  def cmap
203
- (@cmap ||= @ttf.cmap.unicode.first) || raise('no unicode cmap for font')
228
+ (@cmap ||= @ttf.cmap.unicode.first) || raise(NoUnicodeCMap.new(font: name))
204
229
  end
205
230
 
206
231
  # +string+ must be UTF8-encoded.
@@ -275,7 +300,7 @@ module Prawn
275
300
  # if their font name is more than 33 bytes long. Strange. But true.
276
301
  basename = font.name.postscript_name[0, 33].delete("\0")
277
302
 
278
- raise "Can't detect a postscript name for #{file}" if basename.nil?
303
+ raise NoPostscriptName.new(font: font) if basename.nil?
279
304
 
280
305
  fontfile = @document.ref!(Length1: font_content.size)
281
306
  fontfile.stream << font_content
@@ -313,7 +338,7 @@ module Prawn
313
338
  map = @subsets[subset].to_unicode_map
314
339
 
315
340
  ranges = [[]]
316
- map.keys.sort.inject('') do |_s, code|
341
+ map.keys.sort.reduce('') do |_s, code|
317
342
  ranges << [] if ranges.last.length >= 100
318
343
  unicode = map[code]
319
344
  ranges.last << format(
@@ -323,13 +348,14 @@ module Prawn
323
348
  )
324
349
  end
325
350
 
326
- range_blocks = ranges.inject(+'') do |s, list|
327
- s << format(
328
- "%<lenght>d beginbfchar\n%<list>s\nendbfchar\n",
329
- lenght: list.length,
330
- list: list.join("\n")
331
- )
332
- end
351
+ range_blocks =
352
+ ranges.reduce(+'') do |s, list|
353
+ s << format(
354
+ "%<lenght>d beginbfchar\n%<list>s\nendbfchar\n",
355
+ lenght: list.length,
356
+ list: list.join("\n")
357
+ )
358
+ end
333
359
 
334
360
  to_unicode_cmap = UNICODE_CMAP_TEMPLATE % range_blocks.strip
335
361