prawn 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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" }]