pdf_gen 0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +189 -0
  3. data/lib/base_region.rb +95 -0
  4. data/lib/caption.rb +78 -0
  5. data/lib/containers/caption_container.rb +15 -0
  6. data/lib/containers/div_container.rb +14 -0
  7. data/lib/containers/image_container.rb +15 -0
  8. data/lib/containers/span_container.rb +14 -0
  9. data/lib/containers/table_container.rb +14 -0
  10. data/lib/data/ds_ar.rb +31 -0
  11. data/lib/data/ds_base.rb +24 -0
  12. data/lib/data/ds_hash.rb +24 -0
  13. data/lib/div.rb +148 -0
  14. data/lib/document.rb +50 -0
  15. data/lib/fixnum.rb +18 -0
  16. data/lib/float.rb +18 -0
  17. data/lib/image.rb +46 -0
  18. data/lib/modules/base_attributes.rb +86 -0
  19. data/lib/modules/canvas.rb +15 -0
  20. data/lib/modules/composite.rb +54 -0
  21. data/lib/pdf_gen.rb +30 -0
  22. data/lib/smart_table.rb +155 -0
  23. data/lib/span.rb +69 -0
  24. data/lib/table.rb +116 -0
  25. data/lib/writer.rb +30 -0
  26. data/samples/border.rb +15 -0
  27. data/samples/div.rb +23 -0
  28. data/samples/div_illustration.rb +64 -0
  29. data/samples/headerfooter.rb +94 -0
  30. data/samples/image.rb +9 -0
  31. data/samples/ruby_logo.jpg +0 -0
  32. data/samples/sampl1_1.png +0 -0
  33. data/samples/sampl1_2.png +0 -0
  34. data/samples/sampl1_3.png +0 -0
  35. data/samples/simple.rb +7 -0
  36. data/samples/smart_table.rb +98 -0
  37. data/samples/span.rb +12 -0
  38. data/samples/span_illustration.rb +41 -0
  39. data/samples/spanindiv_ill.rb +53 -0
  40. data/samples/table.rb +37 -0
  41. data/samples/table_illustration.rb +177 -0
  42. data/samples/tableindiv_ill.rb +99 -0
  43. data/test/caption_test.rb +99 -0
  44. data/test/container_test.rb +29 -0
  45. data/test/div_test.rb +39 -0
  46. data/test/helpers_for_testing.rb +19 -0
  47. data/test/image_test.rb +54 -0
  48. data/test/shared_examples.rb +68 -0
  49. data/test/span_test.rb +83 -0
  50. data/test/table_test.rb +27 -0
  51. metadata +139 -0
data/lib/document.rb ADDED
@@ -0,0 +1,50 @@
1
+ require "lib/base_region"
2
+ require "lib/modules/composite"
3
+ require "lib/containers/div_container"
4
+ require "lib/containers/span_container"
5
+ require "lib/containers/caption_container"
6
+ require "lib/containers/image_container"
7
+ require "lib/containers/table_container"
8
+
9
+ module PDFGen
10
+
11
+ class Document < BaseRegion
12
+
13
+ include Canvas, Composite, TableContainer, CaptionContainer, SpanContainer,
14
+ DivContainer, ImageContainer
15
+
16
+ def initialize(pdf, page_pad_top)
17
+ super(nil)
18
+ pdf.y += pdf.top_margin # clear predefined top_margin
19
+ pdf.y -= page_pad_top # set y considering page_pad_top
20
+ pdf.top_margin = 0
21
+
22
+ @pdf = pdf
23
+ @page_pad_top = page_pad_top
24
+ @header = []
25
+ end
26
+
27
+ attr_reader :pdf, :header
28
+
29
+ #creates new page
30
+ def break_page
31
+ pdf.page_break @page_pad_top
32
+ end
33
+
34
+ def render
35
+ pos = [0, pdf.y]
36
+ regions.each do |region|
37
+ pos[1] = pdf.y
38
+ status = region.render(pos, pdf.y)
39
+ if status[1]
40
+ pdf.y -= status[0]
41
+ else
42
+ self.break_page
43
+ redo
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ end
data/lib/fixnum.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "pdf/writer"
2
+
3
+
4
+ #Extension of the standard Fixnum class
5
+ #with PDF - related units conversions
6
+ class Fixnum
7
+
8
+ #cm2pts
9
+ def cm
10
+ PDF::Writer.cm2pts(self)
11
+ end
12
+
13
+ #in2pts
14
+ def in
15
+ PDF::Writer.in2pts(self)
16
+ end
17
+
18
+ end
data/lib/float.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "pdf/writer"
2
+
3
+
4
+ #Extension of the standard Float class
5
+ #with PDF - related units conversions
6
+ class Float
7
+
8
+ #cm2pts
9
+ def cm
10
+ PDF::Writer.cm2pts(self)
11
+ end
12
+
13
+ #in2pts
14
+ def in
15
+ PDF::Writer.in2pts(self)
16
+ end
17
+
18
+ end
data/lib/image.rb ADDED
@@ -0,0 +1,46 @@
1
+ require "pdf/writer"
2
+ require 'lib/base_region'
3
+
4
+
5
+ module PDFGen
6
+
7
+ class Image < BaseRegion
8
+
9
+ def initialize(parent, image_res)
10
+ super(parent)
11
+
12
+ @image = image_res
13
+ @info = PDF::Writer::Graphics::ImageInfo.new(@image)
14
+ end
15
+
16
+ attr_accessor :image
17
+
18
+ def set_properties(props = {})
19
+ super
20
+
21
+ if width.zero? && height.zero?
22
+ self.width = @info.width
23
+ self.height = @info.height
24
+ elsif width.zero?
25
+ self.width = height / @info.height.to_f * @info.width
26
+ elsif height.zero?
27
+ self.height = width * @info.height / @info.width.to_f
28
+ end
29
+ self.height = self.height - pad_top - pad_bottom
30
+ end
31
+
32
+ def render(pos, av_height, test=false)
33
+ self.check_fit_in_height
34
+ if av_height >= self.height
35
+ document.pdf.add_image(@image, pos[0]+pad_left, pos[1]-height+pad_bottom,
36
+ width-pad_left-pad_right, height-pad_top-pad_bottom)
37
+ super
38
+ [self.height, true]
39
+ else
40
+ [0, false]
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,86 @@
1
+ module BaseAttributes
2
+
3
+ module ClassMethods
4
+ def common_setter(*setters)
5
+ setters.each do |set|
6
+ define_method("#{set}=") do |val|
7
+ if val && set != val
8
+ self.instance_variable_set("@#{set}",val)
9
+ clear_minimal_height
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ extend ClassMethods
17
+
18
+ common_setter :pad_left, :pad_right, :pad_top, :pad_bottom, :width
19
+
20
+ def self.included(base)
21
+ base.extend(ClassMethods)
22
+ end
23
+
24
+ attr_accessor :background_color
25
+
26
+ def var_init
27
+ @width = 0 #document.pdf.page_width - document.pdf.left_margin - document.pdf.right_margin
28
+ @height = 0
29
+
30
+ @pad_top = 0
31
+ @pad_bottom = 0
32
+ @pad_left = 0
33
+ @pad_right = 0
34
+
35
+ @border_top = false
36
+ @border_left = false
37
+ @border_right = false
38
+ @border_bottom = false
39
+ @border_style = :solid
40
+ @border_width = 1
41
+ @border_color = Color::RGB::Black
42
+
43
+ @is_breakable = false
44
+
45
+ @background_color = nil
46
+ end
47
+
48
+ def breakable?
49
+ @is_breakable
50
+ end
51
+
52
+ attr_writer :is_breakable, :height
53
+
54
+ def height
55
+ [minimal_height, @height].max
56
+ end
57
+
58
+ #padding from the page top after page break
59
+ attr_accessor :page_pad_top
60
+
61
+ attr_reader :width
62
+
63
+ attr_accessor :border_top, :border_bottom, :border_left, :border_right,
64
+ :border_style, :border_color, :border_width
65
+
66
+ def border= value
67
+ self.border_top = self.border_bottom = self.border_left =
68
+ self.border_right = value
69
+ end
70
+
71
+ attr_reader :pad_top, :pad_bottom, :pad_left, :pad_right
72
+
73
+ def paddings=(value)
74
+ @pad_bottom = @pad_left = @pad_right = @pad_top = value
75
+ end
76
+
77
+ def border_params
78
+ border_type = {:solid => [1, 0], :dotted => [1, 1], :none => [0, 1]}
79
+ PDF::Writer::StrokeStyle.new(@border_width, :dash => {:pattern => border_type[@border_style]})
80
+ end
81
+
82
+ def av_width
83
+ width - pad_left - pad_right
84
+ end
85
+
86
+ end
@@ -0,0 +1,15 @@
1
+ module PDFGen
2
+
3
+ module Canvas
4
+
5
+ def y
6
+ pdf.y
7
+ end
8
+
9
+ def y= (value)
10
+ pdf.y = value
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,54 @@
1
+ module PDFGen
2
+
3
+ module Composite
4
+
5
+ def regions
6
+ @regions ||= []
7
+ end
8
+
9
+ def [](index)
10
+ regions[index]
11
+ end
12
+
13
+ #executes specified initialization block on all inner regions
14
+ def elements(style = {}, &initialization_block)
15
+ regions.each do |region|
16
+ region.set_properties style unless style.nil?
17
+ region.instance_eval(&initialization_block) if initialization_block
18
+ end
19
+ end
20
+
21
+ #applies specified values
22
+ def apply_values(values = {})
23
+ regions.each{|region| region.apply_values values}
24
+ end
25
+
26
+ def page_pad_top=(value)
27
+ super
28
+
29
+ regions.each {|region| region.page_pad_top=value}
30
+ end
31
+
32
+ #adds new region to the span
33
+ def add_region region
34
+ regions << region if region
35
+ end
36
+
37
+ def render_regions(x, y, test = false)
38
+ regions.each { |region| region.render([x, document.pdf.y], document.pdf.y, test)}
39
+ end
40
+
41
+ #renders specified span at the specified position
42
+ #returns real position that caption was generated on
43
+ def render(pos,av_height, test = false)
44
+ if av_height >= self.height
45
+ render_regions(pos[0],pos[1], test)
46
+ [self.height, true]
47
+ else
48
+ [0, false]
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
data/lib/pdf_gen.rb ADDED
@@ -0,0 +1,30 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + "/../"))
2
+ require "rubygems"
3
+ require "pdf/writer"
4
+ require "lib/writer"
5
+ require "lib/base_region"
6
+ require "lib/fixnum"
7
+ require "lib/float"
8
+ require "lib/table"
9
+ require "lib/caption"
10
+ require "lib/modules/canvas"
11
+ require "lib/modules/composite"
12
+ require "lib/span"
13
+ require "lib/div"
14
+ require "lib/image"
15
+ require "lib/containers/table_container"
16
+ require "lib/smart_table"
17
+ require "lib/document"
18
+
19
+
20
+ module PDFGen
21
+
22
+ def self.document(pdf, page_pad_top, &initialization_block)
23
+ document = Document.new(pdf, page_pad_top)
24
+ document.width = document.pdf.page_width
25
+ document.instance_eval(&initialization_block)
26
+ document.render
27
+ pdf
28
+ end
29
+
30
+ end
@@ -0,0 +1,155 @@
1
+ require "lib/data/ds_hash"
2
+ require 'lib/data/ds_ar'
3
+ require "lib/table"
4
+ require "lib/div"
5
+
6
+
7
+ module PDFGen
8
+
9
+ class SmartTable < Table
10
+
11
+ attr_accessor :header_region, :body_regions
12
+
13
+ class RowsContainer < Div
14
+
15
+ def initialize(parent)
16
+ super
17
+ @cells = []
18
+ end
19
+
20
+ def ds
21
+ parent.data_source
22
+ end
23
+
24
+ def cell(region=nil,style=nil)
25
+ if region.is_a?(Array)
26
+ @cells << region.last #todo
27
+ region.delete(region.last)
28
+ else
29
+ caption = Caption.new(parent)
30
+ caption.text = region
31
+ caption.border_left = true
32
+ caption.set_properties style unless style.nil?
33
+ @cells << caption
34
+ end
35
+ end
36
+
37
+ def row
38
+ span = Span.new(self.document)
39
+ span.width = self.width
40
+ span.border = true
41
+ yield if block_given?
42
+ @cells.each do |cell|
43
+ if cell.width.zero? || cell.width == self.width
44
+ cell.width = span.width / @cells.size
45
+ end
46
+ span.add_region(cell)
47
+ end
48
+
49
+ @cells.clear
50
+ self.add_region(span)
51
+ end
52
+
53
+ end
54
+
55
+ def initialize(parent)
56
+ super(parent)
57
+ @title = RowsContainer.new(self)
58
+ @header = RowsContainer.new(self)
59
+ @body = RowsContainer.new(self)
60
+ @footer = RowsContainer.new(self)
61
+
62
+ init_width(parent)
63
+
64
+ @data_source = nil
65
+ @header_data = nil
66
+ @body_data = nil
67
+
68
+ @is_header_rendered = false
69
+ @is_body_rendered = false
70
+ @columns = nil
71
+
72
+ @body_regions = []
73
+
74
+ end
75
+
76
+ def data_source=(data)
77
+ if data.is_a?(Array)
78
+ @data_source = DsActiveRecord.new(data)
79
+ else
80
+ @data_source = DsHash.new(data)
81
+ end
82
+ @header_data = @data_source.columns
83
+ @body_data = @data_source.body
84
+ end
85
+
86
+ attr_reader :data_source
87
+
88
+ def calculate_minimal_height
89
+ regions_formation
90
+ super
91
+ end
92
+
93
+ def regions_formation
94
+ @header.add_region(build_header) unless @is_header_rendered
95
+ build_body unless @is_body_rendered
96
+
97
+ @body_regions.each { |region| @body.add_region(region) }
98
+ end
99
+
100
+ def render(pos, av_height, test=false)
101
+ @header_region = nil if @is_header_rendered
102
+ @body_regions.clear if @is_body_rendered
103
+
104
+ regions_formation
105
+
106
+ super
107
+ end
108
+
109
+ def build_header()
110
+ span = Span.new(self.document)
111
+ span.width = self.width
112
+ span.border = true
113
+ @header_data.each do |cap|
114
+ caption = Caption.new(self.document)
115
+ caption.text = cap
116
+ caption.width = self.width / @header_data.size
117
+ caption.border_left = true
118
+ yield(span, caption) if block_given?
119
+ span.add_region(caption)
120
+ end
121
+ @is_header_rendered = true
122
+ span
123
+ end
124
+
125
+ def build_body()
126
+ @body_data.each do |row|
127
+ span = Span.new(self.document)
128
+ span.width = self.width
129
+ span.border = true
130
+ row.each do |cap|
131
+ caption = Caption.new(self.document)
132
+ caption.text = cap
133
+ caption.width = self.width / row.size
134
+ caption.border_left = true
135
+ yield(span, caption) if block_given?
136
+ span.add_region(caption)
137
+ end
138
+ @body_regions << span
139
+ end
140
+ @is_body_rendered = true
141
+ end
142
+
143
+ def header(style = nil, &initialization_block)
144
+ super
145
+ @is_header_rendered = true
146
+ end
147
+
148
+ def body(style = nil, &initialization_block)
149
+ super
150
+ @is_body_rendered = true
151
+ end
152
+
153
+ end
154
+
155
+ end
data/lib/span.rb ADDED
@@ -0,0 +1,69 @@
1
+ require "pdf/writer"
2
+ require "lib/image"
3
+ require "lib/base_region"
4
+ require "lib/modules/composite"
5
+ require "lib/caption"
6
+ require "lib/containers/caption_container"
7
+ require "lib/containers/image_container"
8
+ require "lib/containers/div_container"
9
+
10
+ module PDFGen
11
+
12
+ #Horizontal captions line
13
+ class Span < BaseRegion
14
+
15
+ include Composite, CaptionContainer, ImageContainer, SpanContainer,
16
+ DivContainer, TableContainer
17
+
18
+ def initialize(parent)
19
+ super
20
+
21
+ @vertical_interval = 0
22
+ @vertical_align = false
23
+ end
24
+
25
+ attr_accessor :vertical_interval, :vertical_align
26
+
27
+ def calculate_minimal_height
28
+ render_regions
29
+ (regions.collect{|region| region.height}.max || 0) + pad_top + pad_bottom
30
+ end
31
+
32
+ #renders inner regions
33
+ #returns height of the region content
34
+ def render_regions(x=0, y=document.y, test=true)
35
+ content_width = pad_left
36
+ last = regions.last
37
+ regions.each do |region|
38
+ if region.breakable?
39
+ region.check_fit_in_height
40
+ end
41
+
42
+ if (content_width + region.width) > self.width
43
+ regions[regions.index(region), regions.size].each do |item|
44
+ regions.delete(item)
45
+ end
46
+ else
47
+ if vertical_align && !test
48
+ region.height = height
49
+ end
50
+ region.render([(x + content_width), (y - pad_top)], y-pad_top) unless test
51
+
52
+ content_width += region.width
53
+ content_width += vertical_interval unless region == last
54
+ end
55
+ end
56
+
57
+ content_width + pad_right
58
+ end
59
+
60
+ def render(pos, av_height, test=false)
61
+ self.check_fit_in_height
62
+ fill(pos) unless test
63
+ add_border(pos) unless test
64
+ super
65
+ end
66
+
67
+ end
68
+
69
+ end
data/lib/table.rb ADDED
@@ -0,0 +1,116 @@
1
+ require "lib/base_region"
2
+ require "lib/div"
3
+ require "lib/containers/table_container"
4
+
5
+
6
+ module PDFGen
7
+
8
+ class Table < BaseRegion
9
+
10
+ def initialize(parent)
11
+ super(parent)
12
+ @is_breakable = true
13
+ @title = Div.new(self)
14
+ @header = Div.new(self)
15
+ @body = Div.new(self)
16
+ @footer = Div.new(self)
17
+
18
+ init_width(parent)
19
+
20
+ @repeat_header_on_each_page = false
21
+ @repeat_footer_on_each_page = false
22
+ end
23
+
24
+ def init_width(parent)
25
+ self.width = parent.width - parent.pad_left - parent.pad_right
26
+ end
27
+
28
+ def width=(value)
29
+ [@title,@header,@body,@footer].each do |region|
30
+ region.width = value
31
+ end
32
+
33
+ super
34
+ end
35
+
36
+ attr_accessor :repeat_header_on_each_page, :repeat_footer_on_each_page
37
+
38
+ def align_cell_in_row
39
+ [@header,@body,@footer].each do |container|
40
+ container.regions.each do |row|
41
+ row.vertical_align = true if row.respond_to?(:vertical_align)
42
+ end
43
+ end
44
+ end
45
+
46
+ def calculate_minimal_height
47
+ [@title,@header,@body,@footer].inject(0){ |height, region| height + region.height }
48
+ end
49
+
50
+ def render(pos, av_height, test=false)
51
+ align_cell_in_row
52
+ super
53
+
54
+ pos_x, pos_y = pos
55
+ title_height = @title.render([pos_x, pos_y], pos_y, true)
56
+ header_height = @header.render([pos_x, pos_y], pos_y, true)
57
+
58
+ if (title_height[0] + header_height[0]) > av_height
59
+ return [0, false]
60
+ end
61
+
62
+ title_status = @title.render([pos_x, pos_y], pos_y)
63
+ pos_y -= title_status[0]
64
+ @header.reset_count_rendered_regions if @repeat_header_on_each_page
65
+ header_status = @header.render([pos_x, pos_y], pos_y)
66
+ pos_y -= header_status[0]
67
+
68
+ status = @body.render([pos_x, pos_y], pos_y)
69
+ pos_y -= status[0]
70
+
71
+ footer_height = @footer.render([pos_x, pos_y], pos_y, true)
72
+ if footer_height[0] > pos[1]
73
+ return [av_height-pos_y, false]
74
+ end
75
+
76
+ if status[1]
77
+ footer_status = @footer.render([pos_x, pos_y], pos_y)
78
+ else
79
+ if @repeat_footer_on_each_page
80
+ footer_status = @footer.render([pos_x, pos_y], pos_y)
81
+
82
+ @footer.reset_count_rendered_regions
83
+ end
84
+ end
85
+ pos_y -= footer_status[0] if footer_status
86
+
87
+ [av_height-pos_y, status[1] && footer_status[1]]
88
+ end
89
+
90
+
91
+ def title(style = nil, &initialization_block)
92
+ access_region(@title, style, &initialization_block)
93
+ end
94
+
95
+ def header(style = nil, &initialization_block)
96
+ access_region(@header, style, &initialization_block)
97
+ end
98
+
99
+ def body(style = nil, &initialization_block)
100
+ access_region(@body, style, &initialization_block)
101
+ end
102
+
103
+ def footer(style = nil, &initialization_block)
104
+ access_region(@footer, style, &initialization_block)
105
+ end
106
+
107
+ def access_region(region, style=nil, &initialization_block)
108
+ region.set_properties style unless style.nil?
109
+ region.instance_eval(&initialization_block) if initialization_block
110
+ end
111
+
112
+ private :access_region
113
+
114
+ end
115
+
116
+ end
data/lib/writer.rb ADDED
@@ -0,0 +1,30 @@
1
+ require "pdf/writer"
2
+
3
+
4
+ module PDF
5
+
6
+ class Writer
7
+
8
+ alias_method :save, :save_as
9
+ #extends PDF writer class StrikeStyle with several well known styles
10
+ class StrokeStyle
11
+
12
+ DOTTED = StrokeStyle.new(1, :dash => {:pattern => [1, 1]})
13
+ SOLID = StrokeStyle.new(1, :dash => {:pattern => [1, 0]})
14
+ NONE = StrokeStyle.new(1, :dash => {:pattern => [0, 1]})
15
+
16
+ end
17
+
18
+ #performs page break with the specified top padding (page header height for ex.)
19
+ def page_break(pad_top)
20
+ start_new_page
21
+ self.y = self.y - pad_top
22
+ end
23
+
24
+ def y=(yy)
25
+ @y = yy
26
+ end
27
+
28
+ end
29
+
30
+ end
data/samples/border.rb ADDED
@@ -0,0 +1,15 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + "/../"))
2
+ require "lib/pdf_gen"
3
+
4
+
5
+ PDFGen::document PDF::Writer.new, 2.cm do
6
+ span :pad_left => 2.cm do
7
+ caption 'test text'*50, :width=>5.cm,
8
+ :border => true, :border_style => :solid, :border_width => 2
9
+
10
+ caption 'test text'*50, :width=>5.cm,
11
+ :border => true, :border_style => :dotted, :border_width => 3
12
+ end
13
+ end.save("#{File.basename(__FILE__, ".rb")}.pdf")
14
+
15
+