prawn 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/GPLv2 +340 -0
  2. data/GPLv3 +674 -0
  3. data/README.md +96 -0
  4. data/Rakefile +0 -12
  5. data/examples/example_helper.rb +3 -0
  6. data/lib/prawn.rb +1 -1
  7. data/lib/prawn/core/page.rb +4 -2
  8. data/lib/prawn/core/pdf_object.rb +7 -7
  9. data/lib/prawn/core/text/formatted/arranger.rb +2 -1
  10. data/lib/prawn/core/text/formatted/line_wrap.rb +2 -1
  11. data/lib/prawn/core/text/formatted/wrap.rb +5 -1
  12. data/lib/prawn/document.rb +135 -112
  13. data/lib/prawn/document/bounding_box.rb +51 -5
  14. data/lib/prawn/document/column_box.rb +0 -19
  15. data/lib/prawn/document/internals.rb +1 -0
  16. data/lib/prawn/document/snapshot.rb +3 -0
  17. data/lib/prawn/font.rb +4 -3
  18. data/lib/prawn/font/afm.rb +11 -2
  19. data/lib/prawn/font/ttf.rb +15 -0
  20. data/lib/prawn/images.rb +1 -1
  21. data/lib/prawn/table.rb +23 -7
  22. data/lib/prawn/table/cell.rb +31 -9
  23. data/lib/prawn/table/cell/text.rb +2 -2
  24. data/prawn.gemspec +5 -6
  25. data/spec/bounding_box_spec.rb +157 -0
  26. data/spec/data/curves.pdf +66 -0
  27. data/spec/document_spec.rb +59 -4
  28. data/spec/extensions/mocha.rb +1 -3
  29. data/spec/font_spec.rb +23 -0
  30. data/spec/formatted_text_box_spec.rb +40 -0
  31. data/spec/images_spec.rb +7 -0
  32. data/spec/object_store_spec.rb +3 -3
  33. data/spec/snapshot_spec.rb +33 -0
  34. data/spec/table_spec.rb +75 -0
  35. data/spec/template_spec.rb +11 -5
  36. data/spec/text_spacing_spec.rb +18 -0
  37. data/spec/text_spec.rb +32 -0
  38. metadata +217 -283
  39. data/HACKING +0 -50
  40. data/README +0 -141
  41. data/examples/bounding_box/bounding_boxes.rb +0 -44
  42. data/examples/bounding_box/indentation.rb +0 -35
  43. data/examples/bounding_box/stretched_nesting.rb +0 -68
  44. data/examples/general/background.rb +0 -24
  45. data/examples/general/canvas.rb +0 -16
  46. data/examples/general/float.rb +0 -12
  47. data/examples/general/margin.rb +0 -37
  48. data/examples/general/measurement_units.rb +0 -52
  49. data/examples/general/metadata-info.rb +0 -17
  50. data/examples/general/multi_page_layout.rb +0 -19
  51. data/examples/general/outlines.rb +0 -67
  52. data/examples/general/page_geometry.rb +0 -32
  53. data/examples/general/page_numbering.rb +0 -40
  54. data/examples/general/page_templates.rb +0 -20
  55. data/examples/general/repeaters.rb +0 -48
  56. data/examples/general/stamp.rb +0 -42
  57. data/examples/general/templates.rb +0 -14
  58. data/examples/graphics/basic_images.rb +0 -24
  59. data/examples/graphics/curves.rb +0 -12
  60. data/examples/graphics/hexagon.rb +0 -14
  61. data/examples/graphics/image_fit.rb +0 -16
  62. data/examples/graphics/image_flow.rb +0 -38
  63. data/examples/graphics/image_position.rb +0 -18
  64. data/examples/graphics/line.rb +0 -33
  65. data/examples/graphics/polygons.rb +0 -17
  66. data/examples/graphics/rounded_polygons.rb +0 -20
  67. data/examples/graphics/rounded_rectangle.rb +0 -21
  68. data/examples/graphics/ruport_style_helpers.rb +0 -20
  69. data/examples/graphics/stroke_bounds.rb +0 -21
  70. data/examples/graphics/stroke_cap_and_join.rb +0 -46
  71. data/examples/graphics/stroke_dash.rb +0 -43
  72. data/examples/graphics/transformations.rb +0 -53
  73. data/examples/graphics/transparency.rb +0 -27
  74. data/examples/grid/bounding_boxes.rb +0 -22
  75. data/examples/grid/column_gutter_grid.rb +0 -21
  76. data/examples/grid/multi_boxes.rb +0 -52
  77. data/examples/grid/show_grid.rb +0 -14
  78. data/examples/grid/simple_grid.rb +0 -21
  79. data/examples/m17n/chinese_text_wrapping.rb +0 -18
  80. data/examples/m17n/euro.rb +0 -16
  81. data/examples/m17n/utf8.rb +0 -14
  82. data/examples/security/hello_foo.rb +0 -9
  83. data/examples/table/borders.rb +0 -25
  84. data/examples/table/cell.rb +0 -13
  85. data/examples/table/checkerboard.rb +0 -23
  86. data/examples/table/inline_format_table.rb +0 -13
  87. data/examples/table/multi_page_table.rb +0 -10
  88. data/examples/table/simple_table.rb +0 -25
  89. data/examples/table/subtable.rb +0 -13
  90. data/examples/table/widths.rb +0 -21
  91. data/examples/text/alignment.rb +0 -19
  92. data/examples/text/character_spacing.rb +0 -13
  93. data/examples/text/dfont.rb +0 -49
  94. data/examples/text/family_based_styling.rb +0 -25
  95. data/examples/text/font_size.rb +0 -34
  96. data/examples/text/inline_format.rb +0 -104
  97. data/examples/text/kerning.rb +0 -31
  98. data/examples/text/rendering_mode.rb +0 -21
  99. data/examples/text/rotated.rb +0 -99
  100. data/examples/text/shaped_text_box.rb +0 -32
  101. data/examples/text/simple_text.rb +0 -18
  102. data/examples/text/simple_text_ttf.rb +0 -18
  103. data/examples/text/span.rb +0 -30
  104. data/examples/text/text_box.rb +0 -90
  105. data/examples/text/text_box_returning_excess.rb +0 -52
  106. data/examples/text/text_flow.rb +0 -68
@@ -0,0 +1,66 @@
1
+ %PDF-1.3
2
+ %����
3
+ 1 0 obj
4
+ << /Creator (Prawn)
5
+ /Producer (Prawn)
6
+ >>
7
+ endobj
8
+ 2 0 obj
9
+ << /Type /Pages
10
+ /Count 1
11
+ /Kids [5 0 R]
12
+ >>
13
+ endobj
14
+ 3 0 obj
15
+ << /Type /Catalog
16
+ /Pages 2 0 R
17
+ >>
18
+ endobj
19
+ 4 0 obj
20
+ << /Length 380
21
+ >>
22
+ stream
23
+ /DeviceRGB cs
24
+ 0.000 0.000 0.000 scn
25
+ /DeviceRGB CS
26
+ 0.000 0.000 0.000 SCN
27
+ q
28
+ 136.000 136.000 m
29
+ 96.000 126.000 96.000 126.000 86.000 86.000 c
30
+ S
31
+ 246.000 236.000 m
32
+ 246.000 241.523 241.523 246.000 236.000 246.000 c
33
+ 230.477 246.000 226.000 241.523 226.000 236.000 c
34
+ 226.000 230.477 230.477 226.000 236.000 226.000 c
35
+ 241.523 226.000 246.000 230.477 246.000 236.000 c
36
+ 236.000 236.000 m
37
+ f
38
+ Q
39
+
40
+ endstream
41
+ endobj
42
+ 5 0 obj
43
+ << /Type /Page
44
+ /Parent 2 0 R
45
+ /MediaBox [0 0 612.0 792.0]
46
+ /Contents 4 0 R
47
+ /Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
48
+ >>
49
+ >>
50
+ endobj
51
+ xref
52
+ 0 6
53
+ 0000000000 65535 f
54
+ 0000000015 00000 n
55
+ 0000000071 00000 n
56
+ 0000000128 00000 n
57
+ 0000000177 00000 n
58
+ 0000000608 00000 n
59
+ trailer
60
+ << /Size 6
61
+ /Root 3 0 R
62
+ /Info 1 0 R
63
+ >>
64
+ startxref
65
+ 762
66
+ %%EOF
@@ -3,6 +3,14 @@ require "tempfile"
3
3
 
4
4
  require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
5
5
 
6
+ describe "Prawn::Document.new" do
7
+ it "should not modify its argument" do
8
+ options = {:page_layout => :landscape}
9
+ Prawn::Document.new(options)
10
+ options.should == {:page_layout => :landscape}
11
+ end
12
+ end
13
+
6
14
  describe "The cursor" do
7
15
  it "should equal pdf.y - bounds.absolute_bottom" do
8
16
  pdf = Prawn::Document.new
@@ -101,6 +109,31 @@ describe "When beginning each new page" do
101
109
 
102
110
  end
103
111
 
112
+ describe "Prawn::Document#float" do
113
+ it "should restore the original y-position" do
114
+ create_pdf
115
+ orig_y = @pdf.y
116
+ @pdf.float { @pdf.text "Foo" }
117
+ @pdf.y.should == orig_y
118
+ end
119
+
120
+ it "should teleport across pages if necessary" do
121
+ create_pdf
122
+
123
+ @pdf.float do
124
+ @pdf.text "Foo"
125
+ @pdf.start_new_page
126
+ @pdf.text "Bar"
127
+ end
128
+ @pdf.text "Baz"
129
+
130
+ pages = PDF::Inspector::Page.analyze(@pdf.render).pages
131
+ pages.size.should == 2
132
+ pages[0][:strings].should == ["Foo", "Baz"]
133
+ pages[1][:strings].should == ["Bar"]
134
+ end
135
+ end
136
+
104
137
  describe "The page_number method" do
105
138
  it "should be 1 for a new document" do
106
139
  pdf = Prawn::Document.new
@@ -250,6 +283,15 @@ describe "When reopening pages" do
250
283
  pages[1][:strings].should == ["New page 2"]
251
284
  pages[2][:strings].should == ["Old page 2"]
252
285
  end
286
+
287
+ it "should update the bounding box to the new page's margin box" do
288
+ Prawn::Document.new do
289
+ start_new_page :layout => :landscape
290
+ lsize = [bounds.width, bounds.height]
291
+ go_to_page 1
292
+ [bounds.width, bounds.height].should == lsize.reverse
293
+ end
294
+ end
253
295
  end
254
296
 
255
297
  describe "When setting page size" do
@@ -292,7 +334,7 @@ describe "When setting page layout" do
292
334
  pages.first[:size].should == Prawn::Document::PageGeometry::SIZES["A4"].reverse
293
335
  end
294
336
 
295
- it "should retain page layout by default when starting a new page" do
337
+ it "should retain page layout by default when starting a new page" do
296
338
  @pdf = Prawn::Document.new(:page_layout => :landscape)
297
339
  @pdf.start_new_page(:trace => true)
298
340
  pages = PDF::Inspector::Page.analyze(@pdf.render).pages
@@ -300,6 +342,13 @@ describe "When setting page layout" do
300
342
  page[:size].should == Prawn::Document::PageGeometry::SIZES["LETTER"].reverse
301
343
  end
302
344
  end
345
+
346
+ it "should swap the bounds when starting a new page with different layout" do
347
+ @pdf = Prawn::Document.new
348
+ size = [@pdf.bounds.width, @pdf.bounds.height]
349
+ @pdf.start_new_page(:layout => :landscape)
350
+ [@pdf.bounds.width, @pdf.bounds.height].should == size.reverse
351
+ end
303
352
  end
304
353
 
305
354
  describe "The mask() feature" do
@@ -529,13 +578,19 @@ describe "The number_pages method" do
529
578
  @pdf.number_pages "test, <total>", {:page_filter => :all}
530
579
  end
531
580
 
532
- it "must print the page if the page number matches" do
581
+ it "must print each page if given the :all page_filter" do
533
582
  10.times { @pdf.start_new_page }
534
- @pdf.expects(:text_box).at_least_once
583
+ @pdf.expects(:text_box).times(10)
535
584
  @pdf.number_pages "test", {:page_filter => :all}
536
585
  end
586
+
587
+ it "must print each page if no :page_filter is specified" do
588
+ 10.times { @pdf.start_new_page }
589
+ @pdf.expects(:text_box).times(10)
590
+ @pdf.number_pages "test"
591
+ end
537
592
 
538
- it "must not print the page number without a filter" do
593
+ it "must not print the page number if given a nil filter" do
539
594
  10.times { @pdf.start_new_page }
540
595
  @pdf.expects(:text_box).never
541
596
  @pdf.number_pages "test", {:page_filter => nil}
@@ -12,12 +12,10 @@
12
12
  class ParameterChecker < Mocha::ParametersMatcher
13
13
  def initialize(&matching_block)
14
14
  @matching_block = matching_block
15
- @run_matching_block = false
16
15
  end
17
16
 
18
17
  def match?(actual_parameters = [])
19
- @matching_block.call(*actual_parameters) unless @run_matching_block
20
- @run_matching_block = true
18
+ @matching_block.call(*actual_parameters)
21
19
 
22
20
  true # always succeed
23
21
  end
@@ -20,6 +20,15 @@ describe "#width_of" do
20
20
  @pdf.width_of("hello world").should == original_width + 11 * 7
21
21
  end
22
22
  end
23
+
24
+ it "should exclude newlines" do
25
+ create_pdf
26
+ # Use a TTF font that has a non-zero width for \n
27
+ @pdf.font("#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf")
28
+
29
+ @pdf.width_of("\nhello world\n").should ==
30
+ @pdf.width_of("hello world")
31
+ end
23
32
  end
24
33
 
25
34
  describe "#font_size" do
@@ -353,3 +362,17 @@ describe "DFont fonts" do
353
362
  assert_equal f2.object_id, @pdf.find_font(@file, :font => "ActionMan-Bold").object_id
354
363
  end
355
364
  end
365
+
366
+ describe "#character_count(text)" do
367
+ it "should work on TTF fonts" do
368
+ create_pdf
369
+ @pdf.font("#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf")
370
+ @pdf.font.character_count("こんにちは世界").should == 7
371
+ @pdf.font.character_count("Hello, world!").should == 13
372
+ end
373
+
374
+ it "should work on AFM fonts" do
375
+ create_pdf
376
+ @pdf.font.character_count("Hello, world!").should == 13
377
+ end
378
+ end
@@ -75,6 +75,46 @@ describe "Text::Formatted::Box wrapping" do
75
75
  text_box.render
76
76
  text_box.text.should == "Hello World\n2"
77
77
  end
78
+
79
+ it "should properly handle empty slices using default encoding" do
80
+ texts = [{ :text => "Noua Delineatio Geographica generalis | Apostolicarum peregrinationum | S FRANCISCI XAUERII | Indiarum & Iaponiæ Apostoli", :font => 'Courier', :size => 10 }]
81
+ text_box = Prawn::Text::Formatted::Box.new(texts, :document => @pdf, :width => @pdf.width_of("Noua Delineatio Geographica gen"))
82
+ assert_nothing_raised do
83
+ text_box.render
84
+ end
85
+ text_box.text.should == "Noua Delineatio Geographica\ngeneralis | Apostolicarum\nperegrinationum | S FRANCISCI\nXAUERII | Indiarum & Iaponi\346\nApostoli"
86
+ end
87
+
88
+ describe "Unicode" do
89
+ before do
90
+ if RUBY_VERSION < '1.9'
91
+ @reset_value = $KCODE
92
+ $KCODE='u'
93
+ else
94
+ @reset_value = [Encoding.default_external, Encoding.default_internal]
95
+ Encoding.default_external = Encoding::UTF_8
96
+ Encoding.default_internal = Encoding::UTF_8
97
+ end
98
+ end
99
+
100
+ after do
101
+ if RUBY_VERSION < '1.9'
102
+ $KCODE=@reset_value
103
+ else
104
+ Encoding.default_external = @reset_value[0]
105
+ Encoding.default_internal = @reset_value[1]
106
+ end
107
+ end
108
+
109
+ it "should properly handle empty slices using Unicode encoding" do
110
+ texts = [{ :text => "Noua Delineatio Geographica generalis | Apostolicarum peregrinationum | S FRANCISCI XAUERII | Indiarum & Iaponiæ Apostoli", :font => 'Courier', :size => 10 }]
111
+ text_box = Prawn::Text::Formatted::Box.new(texts, :document => @pdf, :width => @pdf.width_of("Noua Delineatio Geographica gen"))
112
+ assert_nothing_raised do
113
+ text_box.render
114
+ end
115
+ text_box.text.should == "Noua Delineatio Geographica\ngeneralis | Apostolicarum\nperegrinationum | S FRANCISCI\nXAUERII | Indiarum & Iaponi\346\nApostoli"
116
+ end
117
+ end
78
118
  end
79
119
 
80
120
  describe "Text::Formatted::Box with :fallback_fonts option that includes" +
@@ -91,6 +91,13 @@ describe "the image() function" do
91
91
  Set.new(hash[pages[0]][:Resources][:XObject].keys).should ==
92
92
  Set.new([:I1, :I2])
93
93
  end
94
+
95
+ it "should not start a new page just for a stretchy bounding box" do
96
+ @pdf.expects(:start_new_page).times(0)
97
+ @pdf.bounding_box([0, @pdf.cursor], :width => @pdf.bounds.width) do
98
+ @pdf.image @filename
99
+ end
100
+ end
94
101
 
95
102
  describe ":fit option" do
96
103
  it "should fit inside the defined constraints" do
@@ -16,20 +16,20 @@ describe "Prawn::ObjectStore" do
16
16
  end
17
17
 
18
18
  it "should import objects from an existing PDF" do
19
- filename = "#{Prawn::BASEDIR}/reference_pdfs/curves.pdf"
19
+ filename = "#{Prawn::BASEDIR}/spec/data/curves.pdf"
20
20
  store = Prawn::Core::ObjectStore.new(:template => filename)
21
21
  store.size.should == 5
22
22
  end
23
23
 
24
24
  it "should point to existing roots when importing objects from an existing PDF" do
25
- filename = "#{Prawn::BASEDIR}/reference_pdfs/curves.pdf"
25
+ filename = "#{Prawn::BASEDIR}/spec/data/curves.pdf"
26
26
  store = Prawn::Core::ObjectStore.new(:template => filename)
27
27
  store.info.class.should == Prawn::Core::Reference
28
28
  store.root.class.should == Prawn::Core::Reference
29
29
  end
30
30
 
31
31
  it "should initialize with pages when importing objects from an existing PDF" do
32
- filename = "#{Prawn::BASEDIR}/reference_pdfs/curves.pdf"
32
+ filename = "#{Prawn::BASEDIR}/spec/data/curves.pdf"
33
33
  store = Prawn::Core::ObjectStore.new(:template => filename)
34
34
  store.pages.data[:Count].should == 1
35
35
  end
@@ -112,6 +112,39 @@ describe "Prawn::Document#transaction" do
112
112
  end
113
113
 
114
114
  end
115
+
116
+ it "should restore bounds on rollback" do
117
+ Prawn::Document.new(:page_layout => :landscape) do
118
+ size = [bounds.width, bounds.height]
119
+ transaction do
120
+ start_new_page :layout => :portrait
121
+ rollback
122
+ end
123
+ [bounds.width, bounds.height].should == size
124
+ end
125
+ end
126
+
127
+ it "should set new bounding box on start_new_page with different layout" do
128
+ Prawn::Document.new(:page_layout => :landscape) do
129
+ size = [bounds.width, bounds.height]
130
+ transaction do
131
+ start_new_page
132
+ rollback
133
+ end
134
+
135
+ start_new_page :layout => :portrait
136
+ [bounds.width, bounds.height].should == size.reverse
137
+ end
138
+ end
139
+
140
+ it "should work with dests" do
141
+ Prawn::Document.new do |pdf|
142
+ pdf.add_dest("dest", pdf.dest_fit_horizontally(pdf.cursor,
143
+ pdf.page.dictionary))
144
+ pdf.text("Hello world")
145
+ lambda { pdf.transaction{} }.should.not.raise
146
+ end
147
+ end
115
148
 
116
149
  describe "with a stamp dictionary present" do
117
150
 
@@ -219,6 +219,13 @@ describe "Prawn::Table" do
219
219
  table.width.should == 300
220
220
  end
221
221
 
222
+ it "should accept Numeric for column_widths" do
223
+ table = Prawn::Table.new([%w[ a b c ], %w[d e f]], @pdf) do |t|
224
+ t.column_widths = 50
225
+ end
226
+ table.width.should == 150
227
+ end
228
+
222
229
  it "should calculate unspecified column widths as "+
223
230
  "(max(string_width) + 2*horizontal_padding)" do
224
231
  hpad, fs = 3, 12
@@ -485,6 +492,37 @@ describe "Prawn::Table" do
485
492
  output.pages[0][:strings].should.be.empty
486
493
  output.pages[1][:strings].should == ["Header", "Body"]
487
494
  end
495
+
496
+ it "should draw background before borders, but only within pages" do
497
+ seq = sequence("drawing_order")
498
+
499
+ @pdf = Prawn::Document.new
500
+
501
+ # give enough room for only the first row
502
+ @pdf.y = @pdf.bounds.absolute_bottom + 30
503
+ t = @pdf.make_table([["A", "B"],
504
+ ["C", "D"]],
505
+ :cell_style => {:background_color => 'ff0000'})
506
+
507
+ ca = t.cells[0, 0]
508
+ cb = t.cells[0, 1]
509
+ cc = t.cells[1, 0]
510
+ cd = t.cells[1, 1]
511
+
512
+ # All backgrounds should draw before any borders on page 1...
513
+ ca.expects(:draw_background).in_sequence(seq)
514
+ cb.expects(:draw_background).in_sequence(seq)
515
+ ca.expects(:draw_borders).in_sequence(seq)
516
+ cb.expects(:draw_borders).in_sequence(seq)
517
+ # ...and page 2
518
+ @pdf.expects(:start_new_page).in_sequence(seq)
519
+ cc.expects(:draw_background).in_sequence(seq)
520
+ cd.expects(:draw_background).in_sequence(seq)
521
+ cc.expects(:draw_borders).in_sequence(seq)
522
+ cd.expects(:draw_borders).in_sequence(seq)
523
+
524
+ t.draw
525
+ end
488
526
  end
489
527
 
490
528
  describe "#style" do
@@ -581,6 +619,27 @@ describe "Prawn::Table" do
581
619
  end
582
620
  end
583
621
 
622
+ it "should draw all backgrounds before any borders" do
623
+ # lest backgrounds overlap borders:
624
+ # https://github.com/sandal/prawn/pull/226
625
+
626
+ seq = sequence("drawing_order")
627
+
628
+ t = @pdf.make_table([["A", "B"]],
629
+ :cell_style => {:background_color => 'ff0000'})
630
+ ca = t.cells[0, 0]
631
+ cb = t.cells[0, 1]
632
+
633
+ # XXX Not a perfectly general test, because it would still be acceptable
634
+ # if we drew B then A
635
+ ca.expects(:draw_background).in_sequence(seq)
636
+ cb.expects(:draw_background).in_sequence(seq)
637
+ ca.expects(:draw_borders).in_sequence(seq)
638
+ cb.expects(:draw_borders).in_sequence(seq)
639
+
640
+ t.draw
641
+ end
642
+
584
643
  it "should allow multiple inkings of the same table" do
585
644
  pdf = Prawn::Document.new
586
645
  t = Prawn::Table.new([["foo"]], pdf)
@@ -595,6 +654,22 @@ describe "Prawn::Table" do
595
654
  pdf.move_cursor_to(400)
596
655
  t.draw
597
656
  end
657
+
658
+ describe "in stretchy bounding boxes" do
659
+ it "should draw all cells on a row at the same y-position" do
660
+ pdf = Prawn::Document.new
661
+
662
+ text_y = pdf.y.to_i - 5 # text starts 5pt below current y pos (padding)
663
+
664
+ pdf.bounding_box([0, pdf.cursor], :width => pdf.bounds.width) do
665
+ pdf.expects(:draw_text!).checking { |text, options|
666
+ pdf.bounds.absolute_top.should == text_y
667
+ }.times(3)
668
+
669
+ pdf.table([%w[a b c]])
670
+ end
671
+ end
672
+ end
598
673
  end
599
674
 
600
675
  describe "headers" do