pdf_gen 0.7

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 (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
+