prawn 2.0.2 → 2.1.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/data/images/blend_modes_bottom_layer.jpg +0 -0
  3. data/data/images/blend_modes_top_layer.jpg +0 -0
  4. data/data/images/indexed_transparency.png +0 -0
  5. data/data/images/indexed_transparency_alpha.dat +0 -0
  6. data/data/images/indexed_transparency_color.dat +0 -0
  7. data/lib/prawn.rb +2 -1
  8. data/lib/prawn/document.rb +1 -0
  9. data/lib/prawn/document/internals.rb +10 -2
  10. data/lib/prawn/font.rb +14 -1
  11. data/lib/prawn/graphics.rb +2 -0
  12. data/lib/prawn/graphics/blend_mode.rb +64 -0
  13. data/lib/prawn/graphics/patterns.rb +52 -16
  14. data/lib/prawn/graphics/transformation.rb +3 -0
  15. data/lib/prawn/images/png.rb +43 -5
  16. data/lib/prawn/text/formatted/arranger.rb +25 -17
  17. data/lib/prawn/text/formatted/line_wrap.rb +2 -3
  18. data/lib/prawn/transformation_stack.rb +42 -0
  19. data/lib/prawn/version.rb +1 -1
  20. data/manual/graphics/blend_mode.rb +49 -0
  21. data/manual/graphics/graphics.rb +1 -0
  22. data/manual/graphics/soft_masks.rb +1 -1
  23. data/prawn.gemspec +4 -5
  24. data/spec/acceptance/png_spec.rb +35 -0
  25. data/spec/blend_mode_spec.rb +71 -0
  26. data/spec/document_spec.rb +72 -76
  27. data/spec/font_spec.rb +11 -11
  28. data/spec/formatted_text_arranger_spec.rb +178 -149
  29. data/spec/formatted_text_box_spec.rb +23 -23
  30. data/spec/graphics_spec.rb +67 -28
  31. data/spec/image_handler_spec.rb +7 -7
  32. data/spec/images_spec.rb +1 -1
  33. data/spec/png_spec.rb +26 -4
  34. data/spec/repeater_spec.rb +9 -9
  35. data/spec/spec_helper.rb +1 -4
  36. data/spec/text_at_spec.rb +2 -2
  37. data/spec/text_box_spec.rb +20 -16
  38. data/spec/text_spec.rb +8 -14
  39. data/spec/transformation_stack_spec.rb +63 -0
  40. data/spec/view_spec.rb +10 -10
  41. metadata +27 -31
  42. data/data/images/pal_bk.png +0 -0
  43. data/spec/acceptance/png.rb +0 -24
  44. data/spec/extensions/mocha.rb +0 -45
@@ -265,11 +265,11 @@ describe "Document#page_fonts" do
265
265
  end
266
266
 
267
267
  def page_should_include_font(font)
268
- expect(page_includes_font?(font)).to be_true
268
+ expect(page_includes_font?(font)).to eq true
269
269
  end
270
270
 
271
271
  def page_should_not_include_font(font)
272
- expect(page_includes_font?(font)).to be_false
272
+ expect(page_includes_font?(font)).to eq false
273
273
  end
274
274
  end
275
275
 
@@ -313,13 +313,13 @@ describe "AFM fonts" do
313
313
  it "should not modify the original string when normalize_encoding() is used" do
314
314
  original = "Foo"
315
315
  normalized = @times.normalize_encoding(original)
316
- expect(original.equal?(normalized)).to be_false
316
+ expect(original.equal?(normalized)).to eq false
317
317
  end
318
318
 
319
319
  it "should modify the original string when normalize_encoding!() is used" do
320
320
  original = "Foo"
321
321
  normalized = @times.normalize_encoding!(original)
322
- expect(original.equal?(normalized)).to be_true
322
+ expect(original.equal?(normalized)).to eq true
323
323
  end
324
324
  end
325
325
 
@@ -335,25 +335,25 @@ describe "#glyph_present" do
335
335
 
336
336
  it "should return true when present in an AFM font" do
337
337
  font = @pdf.find_font("Helvetica")
338
- expect(font.glyph_present?("H")).to be_true
338
+ expect(font.glyph_present?("H")).to eq true
339
339
  end
340
340
 
341
341
  it "should return false when absent in an AFM font" do
342
342
  font = @pdf.find_font("Helvetica")
343
- expect(font.glyph_present?("再")).to be_false
343
+ expect(font.glyph_present?("再")).to eq false
344
344
  end
345
345
 
346
346
  it "should return true when present in a TTF font" do
347
347
  font = @pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
348
- expect(font.glyph_present?("H")).to be_true
348
+ expect(font.glyph_present?("H")).to eq true
349
349
  end
350
350
 
351
351
  it "should return false when absent in a TTF font" do
352
352
  font = @pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
353
- expect(font.glyph_present?("再")).to be_false
353
+ expect(font.glyph_present?("再")).to eq false
354
354
 
355
355
  font = @pdf.find_font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
356
- expect(font.glyph_present?("€")).to be_false
356
+ expect(font.glyph_present?("€")).to eq false
357
357
  end
358
358
  end
359
359
 
@@ -410,13 +410,13 @@ describe "TTF fonts" do
410
410
  it "should not modify the original string when normalize_encoding() is used" do
411
411
  original = "Foo"
412
412
  normalized = @font.normalize_encoding(original)
413
- expect(original.equal?(normalized)).to be_false
413
+ expect(original.equal?(normalized)).to eq false
414
414
  end
415
415
 
416
416
  it "should modify the original string when normalize_encoding!() is used" do
417
417
  original = "Foo"
418
418
  normalized = @font.normalize_encoding!(original)
419
- expect(original.equal?(normalized)).to be_true
419
+ expect(original.equal?(normalized)).to eq true
420
420
  end
421
421
  end
422
422
  end
@@ -2,167 +2,196 @@
2
2
 
3
3
  require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
4
 
5
- describe "Core::Text::Formatted::Arranger#format_array" do
6
- it "should populate unconsumed array" do
7
- create_pdf
8
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
9
- array = [{ :text => "hello " },
10
- { :text => "world how ", :styles => [:bold] },
11
- { :text => "are", :styles => [:bold, :italic] },
12
- { :text => " you?" }]
13
- arranger.format_array = array
14
- expect(arranger.unconsumed[0]).to eq(:text => "hello ")
15
- expect(arranger.unconsumed[1]).to eq(:text => "world how ",
16
- :styles => [:bold])
17
- expect(arranger.unconsumed[2]).to eq(:text => "are",
18
- :styles => [:bold, :italic])
19
- expect(arranger.unconsumed[3]).to eq(:text => " you?")
20
- end
21
- it "should split newlines into their own elements" do
22
- create_pdf
23
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
24
- array = [{ :text => "\nhello\nworld" }]
25
- arranger.format_array = array
26
- expect(arranger.unconsumed[0]).to eq(:text => "\n")
27
- expect(arranger.unconsumed[1]).to eq(:text => "hello")
28
- expect(arranger.unconsumed[2]).to eq(:text => "\n")
29
- expect(arranger.unconsumed[3]).to eq(:text => "world")
30
- end
31
- end
32
- describe "Core::Text::Formatted::Arranger#preview_next_string" do
33
- it "should not populate the consumed array" do
34
- create_pdf
35
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
36
- array = [{ :text => "hello" }]
37
- arranger.format_array = array
38
- arranger.preview_next_string
39
- expect(arranger.consumed).to eq([])
40
- end
41
- it "should not consumed array" do
42
- create_pdf
43
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
44
- array = [{ :text => "hello" }]
45
- arranger.format_array = array
46
- expect(arranger.preview_next_string).to eq("hello")
47
- end
48
- end
49
- describe "Core::Text::Formatted::Arranger#next_string" do
50
- before(:each) do
51
- create_pdf
52
- @arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
53
- array = [{ :text => "hello " },
54
- { :text => "world how ", :styles => [:bold] },
55
- { :text => "are", :styles => [:bold, :italic] },
56
- { :text => " you?" }]
57
- @arranger.format_array = array
58
- end
59
- it "should raise_error an error if called after a line was finalized and" \
60
- " before a new line was initialized" do
61
- @arranger.finalize_line
62
- expect do
63
- @arranger.next_string
64
- end.to raise_error(RuntimeError)
65
- end
66
- it "should populate consumed array" do
67
- while string = @arranger.next_string
5
+ describe Prawn::Text::Formatted::Arranger do
6
+ let(:document) { create_pdf }
7
+ subject { Prawn::Text::Formatted::Arranger.new document }
8
+
9
+ describe '#format_array' do
10
+ it 'populates the unconsumed array' do
11
+ array = [
12
+ { text: 'hello ' },
13
+ { text: 'world how ', styles: [:bold] },
14
+ { text: 'are', styles: [:bold, :italic] },
15
+ { text: ' you?' }
16
+ ]
17
+
18
+ subject.format_array = array
19
+
20
+ expect(subject.unconsumed[0]).to eq(text: 'hello ')
21
+ expect(subject.unconsumed[1]).to eq(text: 'world how ', styles: [:bold])
22
+ expect(subject.unconsumed[2]).to eq(text: 'are', styles: [:bold, :italic])
23
+ expect(subject.unconsumed[3]).to eq(text: ' you?')
24
+ end
25
+
26
+ it 'splits newlins into their own elements' do
27
+ array = [
28
+ { text: "\nhello\nworld" }
29
+ ]
30
+
31
+ subject.format_array = array
32
+
33
+ expect(subject.unconsumed[0]).to eq(text: "\n")
34
+ expect(subject.unconsumed[1]).to eq(text: "hello")
35
+ expect(subject.unconsumed[2]).to eq(text: "\n")
36
+ expect(subject.unconsumed[3]).to eq(text: "world")
68
37
  end
69
- expect(@arranger.consumed[0]).to eq(:text => "hello ")
70
- expect(@arranger.consumed[1]).to eq(:text => "world how ",
71
- :styles => [:bold])
72
- expect(@arranger.consumed[2]).to eq(:text => "are",
73
- :styles => [:bold, :italic])
74
- expect(@arranger.consumed[3]).to eq(:text => " you?")
75
38
  end
76
- it "should populate current_format_state array" do
77
- create_pdf
78
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
79
- array = [{ :text => "hello " },
80
- { :text => "world how ", :styles => [:bold] },
81
- { :text => "are", :styles => [:bold, :italic] },
82
- { :text => " you?" }]
83
- arranger.format_array = array
84
- counter = 0
85
- while string = arranger.next_string
86
- case counter
87
- when 0
88
- expect(arranger.current_format_state).to eq({})
89
- when 1
90
- expect(arranger.current_format_state).to eq(:styles => [:bold])
91
- when 2
92
- expect(arranger.current_format_state).to eq(:styles => [:bold, :italic])
93
- when 3
94
- expect(arranger.current_format_state).to eq({})
39
+
40
+ describe '#preview_next_string' do
41
+ context 'with a formatted array' do
42
+ let(:array) { [{ text: 'hello' }] }
43
+
44
+ before do
45
+ subject.format_array = array
46
+ end
47
+
48
+ it 'does not populate the consumed array' do
49
+ subject.preview_next_string
50
+ expect(subject.consumed).to eq([])
51
+ end
52
+
53
+ it 'returns the text of the next unconsumed hash' do
54
+ expect(subject.preview_next_string).to eq("hello")
55
+ end
56
+
57
+ it 'returns nil if there is no more unconsumed text' do
58
+ subject.next_string
59
+ expect(subject.preview_next_string).to be_nil
95
60
  end
96
- counter += 1
97
61
  end
98
62
  end
99
- end
100
63
 
101
- describe "Core::Text::Formatted::Arranger#retrieve_fragment" do
102
- it "should raise_error an error if called before finalize_line was called" do
103
- create_pdf
104
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
105
- array = [{ :text => "hello " },
106
- { :text => "world how ", :styles => [:bold] },
107
- { :text => "are", :styles => [:bold, :italic] },
108
- { :text => " you?" }]
109
- arranger.format_array = array
110
- while string = arranger.next_string
64
+ describe '#next_string' do
65
+ let(:array) {
66
+ [
67
+ { text: 'hello ' },
68
+ { text: 'world how ', styles: [:bold] },
69
+ { text: 'are', styles: [:bold, :italic] },
70
+ { text: ' you?' }
71
+ ]
72
+ }
73
+
74
+ before do
75
+ subject.format_array = array
111
76
  end
112
- expect do
113
- arranger.retrieve_fragment
114
- end.to raise_error(RuntimeError)
115
- end
116
- it "should return the consumed fragments in order of consumption" \
117
- " and update" do
118
- create_pdf
119
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
120
- array = [{ :text => "hello " },
121
- { :text => "world how ", :styles => [:bold] },
122
- { :text => "are", :styles => [:bold, :italic] },
123
- { :text => " you?" }]
124
- arranger.format_array = array
125
- while string = arranger.next_string
77
+
78
+ it 'raises RuntimeError if called after a line was finalized' do
79
+ subject.finalize_line
80
+ expect { subject.next_string }.to raise_error(RuntimeError)
126
81
  end
127
- arranger.finalize_line
128
- expect(arranger.retrieve_fragment.text).to eq("hello ")
129
- expect(arranger.retrieve_fragment.text).to eq("world how ")
130
- expect(arranger.retrieve_fragment.text).to eq("are")
131
- expect(arranger.retrieve_fragment.text).to eq(" you?")
132
- end
133
- it "should never return a fragment whose text is an empty string" do
134
- create_pdf
135
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
136
- array = [{ :text => "hello\nworld\n\n\nhow are you?" },
137
- { :text => "\n" },
138
- { :text => "\n" },
139
- { :text => "\n" },
140
- { :text => "" },
141
- { :text => "fine, thanks." },
142
- { :text => "" },
143
- { :text => "\n" },
144
- { :text => "" }]
145
- arranger.format_array = array
146
- while string = arranger.next_string
82
+
83
+ it 'populates the conumed array' do
84
+ while string = subject.next_string
85
+ end
86
+
87
+ expect(subject.consumed[0]).to eq(text: 'hello ')
88
+ expect(subject.consumed[1]).to eq(text: 'world how ', styles: [:bold])
89
+ expect(subject.consumed[2]).to eq(text: 'are', styles: [:bold, :italic])
90
+ expect(subject.consumed[3]).to eq(text: ' you?')
147
91
  end
148
- arranger.finalize_line
149
- while fragment = arranger.retrieve_fragment
150
- expect(fragment.text).not_to be_empty
92
+
93
+ it 'populates the current_format_state array' do
94
+ string = subject.next_string
95
+ expect(subject.current_format_state).to eq({})
96
+
97
+ string = subject.next_string
98
+ expect(subject.current_format_state).to eq(:styles => [:bold])
99
+
100
+ string = subject.next_string
101
+ expect(subject.current_format_state).to eq(:styles => [:bold, :italic])
102
+
103
+ string = subject.next_string
104
+ expect(subject.current_format_state).to eq({})
105
+ end
106
+
107
+ it 'returns the text of the newly consumed hash' do
108
+ expect(subject.next_string).to eq('hello ')
109
+ end
110
+
111
+ it 'returns nil when there are no more unconsumed hashes' do
112
+ 4.times do
113
+ subject.next_string
114
+ end
115
+
116
+ expect(subject.next_string).to be_nil
151
117
  end
152
118
  end
153
- it "should not alter the current font style" do
154
- create_pdf
155
- arranger = Prawn::Text::Formatted::Arranger.new(@pdf)
156
- array = [{ :text => "hello " },
157
- { :text => "world how ", :styles => [:bold] },
158
- { :text => "are", :styles => [:bold, :italic] },
159
- { :text => " you?" }]
160
- arranger.format_array = array
161
- while string = arranger.next_string
119
+
120
+ describe '#retrieve_fragment' do
121
+ context 'with a formatted array whos text is an empty string' do
122
+ let(:array) {
123
+ [
124
+ { text: "hello\nworld\n\n\nhow are you?" },
125
+ { text: "\n" },
126
+ { text: "\n" },
127
+ { text: "\n" },
128
+ { text: "" },
129
+ { text: "fine, thanks." },
130
+ { text: "" },
131
+ { text: "\n" },
132
+ { text: "" }
133
+ ]
134
+ }
135
+
136
+ before do
137
+ subject.format_array = array
138
+
139
+ while string = subject.next_string
140
+ end
141
+
142
+ subject.finalize_line
143
+ end
144
+
145
+ it 'never returns a fragment whose text is an empty string' do
146
+ while fragment = subject.retrieve_fragment
147
+ expect(fragment.text).not_to be_empty
148
+ end
149
+ end
150
+ end
151
+
152
+ context 'with formatted array' do
153
+ let(:array) {
154
+ [
155
+ { text: 'hello ' },
156
+ { text: 'world how ', styles: [:bold] },
157
+ { text: 'are', styles: [:bold, :italic] },
158
+ { text: ' you?' }
159
+ ]
160
+ }
161
+
162
+ before do
163
+ subject.format_array = array
164
+ end
165
+
166
+ context 'after all strings have been consumed' do
167
+ before do
168
+ while string = subject.next_string
169
+ end
170
+ end
171
+
172
+ it 'should raise RuntimeError an error if not finalized' do
173
+ expect { subject.retrieve_fragment }.to raise_error(RuntimeError)
174
+ end
175
+
176
+ context 'and finalized' do
177
+ before do
178
+ subject.finalize_line
179
+ end
180
+
181
+ it 'returns the consumed fragments in order of consumption' do
182
+ expect(subject.retrieve_fragment.text).to eq("hello ")
183
+ expect(subject.retrieve_fragment.text).to eq("world how ")
184
+ expect(subject.retrieve_fragment.text).to eq("are")
185
+ expect(subject.retrieve_fragment.text).to eq(" you?")
186
+ end
187
+
188
+ it 'does not alter the current font style' do
189
+ subject.retrieve_fragment
190
+ expect(subject.current_format_state[:styles]).to be_nil
191
+ end
192
+ end
193
+ end
162
194
  end
163
- arranger.finalize_line
164
- arranger.retrieve_fragment
165
- expect(arranger.current_format_state[:styles]).to be_nil
166
195
  end
167
196
  end
168
197
 
@@ -225,7 +225,7 @@ describe "Text::Formatted::Box" do
225
225
  :document => @pdf,
226
226
  :fallback_fonts => [])
227
227
 
228
- box.expects(:process_fallback_fonts).never
228
+ expect(box).to_not receive(:process_fallback_fonts)
229
229
  box.render
230
230
  end
231
231
 
@@ -236,7 +236,7 @@ describe "Text::Formatted::Box" do
236
236
 
237
237
  @pdf.font("Kai")
238
238
 
239
- box.expects(:process_fallback_fonts).never
239
+ expect(box).to_not receive(:process_fallback_fonts)
240
240
  box.render
241
241
  end
242
242
  end
@@ -339,10 +339,10 @@ describe "Text::Formatted::Box#render" do
339
339
  it "should be able to perform fragment callbacks" do
340
340
  create_pdf
341
341
  callback_object = TestFragmentCallback.new("something", 7, :document => @pdf)
342
- callback_object.expects(:render_behind).with(
342
+ expect(callback_object).to receive(:render_behind).with(
343
343
  kind_of(Prawn::Text::Formatted::Fragment)
344
344
  )
345
- callback_object.expects(:render_in_front).with(
345
+ expect(callback_object).to receive(:render_in_front).with(
346
346
  kind_of(Prawn::Text::Formatted::Fragment)
347
347
  )
348
348
  array = [{ :text => "hello world " },
@@ -355,18 +355,18 @@ describe "Text::Formatted::Box#render" do
355
355
  create_pdf
356
356
 
357
357
  callback_object = TestFragmentCallback.new("something", 7, :document => @pdf)
358
- callback_object.expects(:render_behind).with(
358
+ expect(callback_object).to receive(:render_behind).with(
359
359
  kind_of(Prawn::Text::Formatted::Fragment)
360
360
  )
361
- callback_object.expects(:render_in_front).with(
361
+ expect(callback_object).to receive(:render_in_front).with(
362
362
  kind_of(Prawn::Text::Formatted::Fragment)
363
363
  )
364
364
 
365
365
  callback_object2 = TestFragmentCallback.new("something else", 14, :document => @pdf)
366
- callback_object2.expects(:render_behind).with(
366
+ expect(callback_object2).to receive(:render_behind).with(
367
367
  kind_of(Prawn::Text::Formatted::Fragment)
368
368
  )
369
- callback_object2.expects(:render_in_front).with(
369
+ expect(callback_object2).to receive(:render_in_front).with(
370
370
  kind_of(Prawn::Text::Formatted::Fragment)
371
371
  )
372
372
 
@@ -485,12 +485,12 @@ describe "Text::Formatted::Box#render" do
485
485
  end
486
486
  it "should be able to add URL links" do
487
487
  create_pdf
488
- @pdf.expects(:link_annotation).with(kind_of(Array), :Border => [0, 0, 0],
489
- :A => {
490
- :Type => :Action,
491
- :S => :URI,
492
- :URI => "http://example.com"
493
- })
488
+ expect(@pdf).to receive(:link_annotation).with(kind_of(Array), :Border => [0, 0, 0],
489
+ :A => {
490
+ :Type => :Action,
491
+ :S => :URI,
492
+ :URI => "http://example.com"
493
+ })
494
494
  array = [{ :text => "click " },
495
495
  { :text => "here", :link => "http://example.com" },
496
496
  { :text => " to visit" }]
@@ -499,8 +499,8 @@ describe "Text::Formatted::Box#render" do
499
499
  end
500
500
  it "should be able to add destination links" do
501
501
  create_pdf
502
- @pdf.expects(:link_annotation).with(kind_of(Array), :Border => [0, 0, 0],
503
- :Dest => "ToC")
502
+ expect(@pdf).to receive(:link_annotation).with(kind_of(Array), :Border => [0, 0, 0],
503
+ :Dest => "ToC")
504
504
  array = [{ :text => "Go to the " },
505
505
  { :text => "Table of Contents", :anchor => "ToC" }]
506
506
  text_box = Prawn::Text::Formatted::Box.new(array, :document => @pdf)
@@ -508,13 +508,13 @@ describe "Text::Formatted::Box#render" do
508
508
  end
509
509
  it "should be able to add local actions" do
510
510
  create_pdf
511
- @pdf.expects(:link_annotation).with(kind_of(Array), :Border => [0, 0, 0],
512
- :A => {
513
- :Type => :Action,
514
- :S => :Launch,
515
- :F => "../example.pdf",
516
- :NewWindow => true
517
- })
511
+ expect(@pdf).to receive(:link_annotation).with(kind_of(Array), :Border => [0, 0, 0],
512
+ :A => {
513
+ :Type => :Action,
514
+ :S => :Launch,
515
+ :F => "../example.pdf",
516
+ :NewWindow => true
517
+ })
518
518
  array = [{ :text => "click " },
519
519
  { :text => "here", :local => "../example.pdf" },
520
520
  { :text => " to open a local file" }]