diff --git a/lib/redmine/helpers/gantt.rb b/lib/redmine/helpers/gantt.rb
index 4218c51efaf..40f1f33a511 100644
--- a/lib/redmine/helpers/gantt.rb
+++ b/lib/redmine/helpers/gantt.rb
@@ -165,17 +165,27 @@ def render_project(project, options={})
output = ''
# Project Header
- project_header = if options[:render] == :subject
- subject_for_project(project, options)
- else
- # :line
- line_for_project(project, options)
- end
- output << project_header if options[:format] == :html
+ if options[:format] == :pdf
+ subject_for_project(project, options)
+ line_for_project(project, options)
+ else
+ project_header = if options[:render] == :subject
+ subject_for_project(project, options)
+ else
+ # :line
+ line_for_project(project, options)
+ end
+ output << project_header if options[:format] == :html
+ end
options[:top] += options[:top_increment]
options[:indent] += options[:indent_increment]
+ # Insert a page break if we hit the bottom of the page
+ if options[:format] == :pdf and options[:top] > 180
+ pdf_page_break(options[:pdf], options)
+ end
+
# Second, Issues without a version
issues = project.issues.for_gantt.without_version.with_query(@query)
sort_issues!(issues)
@@ -205,14 +215,24 @@ def render_project(project, options={})
def render_issues(issues, options={})
output = ''
issues.each do |i|
- issue_rendering = if options[:render] == :subject
- subject_for_issue(i, options)
- else
- # :line
- line_for_issue(i, options)
- end
- output << issue_rendering if options[:format] == :html
+ if options[:format] == :pdf
+ subject_for_issue(i, options)
+ line_for_issue(i, options)
+ else
+ issue_rendering = if options[:render] == :subject
+ subject_for_issue(i, options)
+ else
+ # :line
+ line_for_issue(i, options)
+ end
+ output << issue_rendering if options[:format] == :html
+ end
options[:top] += options[:top_increment]
+
+ # Insert a page break if we hit the bottom of the page
+ if options[:format] == :pdf and options[:top] > 180
+ pdf_page_break(options[:pdf], options)
+ end
end
output
end
@@ -220,17 +240,26 @@ def render_issues(issues, options={})
def render_version(version, options={})
output = ''
# Version header
- version_rendering = if options[:render] == :subject
- subject_for_version(version, options)
- else
- # :line
- line_for_version(version, options)
- end
+ if options[:format] == :pdf
+ subject_for_version(version, options)
+ line_for_version(version, options)
+ else
+ version_rendering = if options[:render] == :subject
+ subject_for_version(version, options)
+ else
+ # :line
+ line_for_version(version, options)
+ end
- output << version_rendering if options[:format] == :html
-
+ output << version_rendering if options[:format] == :html
+ end
options[:top] += options[:top_increment]
+ # Insert a page break if we hit the bottom of the page
+ if options[:format] == :pdf and options[:top] > 180
+ pdf_page_break(options[:pdf], options)
+ end
+
# Remove the project requirement for Versions because it will
# restrict issues to only be on the current project. This
# ends up missing issues which are assigned to shared versions.
@@ -266,7 +295,6 @@ def subject_for_project(project, options)
output
when :image
-
options[:image].fill('black')
options[:image].stroke('transparent')
options[:image].stroke_width(1)
@@ -289,7 +317,6 @@ def line_for_project(project, options)
if project.is_a?(Project) && project.start_date && project.due_date
options[:zoom] ||= 1
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
-
case options[:format]
when :html
@@ -326,7 +353,6 @@ def line_for_project(project, options)
if d_width > 0 && i_left <= options[:g_width]
output<< "
"
end
-
# Starting diamond
if start_left <= options[:g_width] && start_left > 0
@@ -460,7 +486,6 @@ def line_for_version(version, options)
if d_width > 0 && i_left <= options[:g_width]
output<< "
"
end
-
# Starting diamond
if start_left <= options[:g_width] && start_left > 0
@@ -735,20 +760,20 @@ def to_image(format='PNG')
show_days = @zoom > 2
subject_width = 400
- header_heigth = 18
+ header_height = 18
# width of one day in pixels
zoom = @zoom*2
g_width = (@date_to - @date_from + 1)*zoom
g_height = 20 * number_of_rows + 30
- headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
- height = g_height + headers_heigth
+ headers_height = (show_weeks ? 2*header_height : header_height)
+ height = g_height + headers_height
imgl = Magick::ImageList.new
imgl.new_image(subject_width+g_width+1, height)
gc = Magick::Draw.new
# Subjects
- subjects(:image => gc, :top => (headers_heigth + 20), :indent => 4, :format => :image)
+ subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image)
# Months headers
month_f = @date_from
@@ -770,7 +795,7 @@ def to_image(format='PNG')
# Weeks headers
if show_weeks
left = subject_width
- height = header_heigth
+ height = header_height
if @date_from.cwday == 1
# date_from is monday
week_f = date_from
@@ -781,7 +806,7 @@ def to_image(format='PNG')
gc.fill('white')
gc.stroke('grey')
gc.stroke_width(1)
- gc.rectangle(left, header_heigth, left + width, 2*header_heigth + g_height-1)
+ gc.rectangle(left, header_height, left + width, 2*header_height + g_height-1)
left = left + width
end
while week_f <= date_to
@@ -789,11 +814,11 @@ def to_image(format='PNG')
gc.fill('white')
gc.stroke('grey')
gc.stroke_width(1)
- gc.rectangle(left.round, header_heigth, left.round + width, 2*header_heigth + g_height-1)
+ gc.rectangle(left.round, header_height, left.round + width, 2*header_height + g_height-1)
gc.fill('black')
gc.stroke('transparent')
gc.stroke_width(1)
- gc.text(left.round + 2, header_heigth + 14, week_f.cweek.to_s)
+ gc.text(left.round + 2, header_height + 14, week_f.cweek.to_s)
left = left + width
week_f = week_f+7
end
@@ -802,14 +827,14 @@ def to_image(format='PNG')
# Days details (week-end in grey)
if show_days
left = subject_width
- height = g_height + header_heigth - 1
+ height = g_height + header_height - 1
wday = @date_from.cwday
(date_to - @date_from + 1).to_i.times do
width = zoom
gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
gc.stroke('grey')
gc.stroke_width(1)
- gc.rectangle(left, 2*header_heigth, left + width, 2*header_heigth + g_height-1)
+ gc.rectangle(left, 2*header_height, left + width, 2*header_height + g_height-1)
left = left + width
wday = wday + 1
wday = 1 if wday > 7
@@ -820,12 +845,12 @@ def to_image(format='PNG')
gc.fill('transparent')
gc.stroke('grey')
gc.stroke_width(1)
- gc.rectangle(0, 0, subject_width+g_width, headers_heigth)
+ gc.rectangle(0, 0, subject_width+g_width, headers_height)
gc.stroke('black')
- gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
+ gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_height-1)
# content
- top = headers_heigth + 20
+ top = headers_height + 20
lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image)
@@ -833,7 +858,7 @@ def to_image(format='PNG')
if Date.today >= @date_from and Date.today <= date_to
gc.stroke('red')
x = (Date.today-@date_from+1)*zoom + subject_width
- gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
+ gc.line(x, headers_height, x, headers_height + g_height-1)
end
gc.draw(imgl)
@@ -852,37 +877,98 @@ def to_pdf
pdf.Cell(PDF::LeftPaneWidth, 20, project.to_s)
pdf.Ln
pdf.SetFontStyle('B',9)
+ pdf.SetAutoPageBreak(FALSE)
subject_width = PDF::LeftPaneWidth
- header_heigth = 5
+ header_height = 5
- headers_heigth = header_heigth
+ headers_height = header_height
show_weeks = false
show_days = false
if self.months < 7
show_weeks = true
- headers_heigth = 2*header_heigth
+ headers_height = 2*header_height
if self.months < 3
show_days = true
- headers_heigth = 3*header_heigth
+ headers_height = 3*header_height
end
end
g_width = PDF.right_pane_width
zoom = (g_width) / (self.date_to - self.date_from + 1)
g_height = 120
- t_height = g_height + headers_heigth
+ t_height = g_height + headers_height
y_start = pdf.GetY
+ options = {
+ :top => y_start,
+ :headers_top => y_start,
+ :header_height => header_height,
+ :headers_height => headers_height,
+ :subject_width => subject_width,
+ :show_weeks => show_weeks,
+ :show_days => show_days,
+ :zoom => zoom,
+ :g_width => g_width
+ }
+
+ pdf_date_headers(pdf, options)
+
+ # Tasks
+ top = headers_height + y_start
+ options[:top] = top
+
+ pdf_subjects_and_lines(pdf, options)
+
+ pdf.Line(15, options[:top]+2, subject_width+g_width, options[:top]+2)
+ pdf.Output
+ end
+
+ private
+
+ # Sorts a collection of issues by start_date, due_date, id for gantt rendering
+ def sort_issues!(issues)
+ issues.sort! do |a, b|
+ cmp = 0
+ cmp = (a.start_date <=> b.start_date) if a.start_date? && b.start_date?
+ cmp = (a.due_date <=> b.due_date) if cmp == 0 && a.due_date? && b.due_date?
+ cmp = (a.id <=> b.id) if cmp == 0
+ cmp
+ end
+ end
+
+ # Goes to the next page so it doesn't overflow
+ def pdf_page_break(pdf, options = {})
+ top = options[:top]
+ subject_width = options[:subject_width]
+ g_width = options[:g_width]
+ pdf.Line(15, top + 2, subject_width + g_width, top + 2)
+ pdf.AddPage('L')
+ options[:top] = options[:headers_top]
+ pdf_date_headers(pdf, options)
+ options[:top] += options[:headers_height]
+ end
+
+ # Renders the date headers for the PDF format
+ def pdf_date_headers(pdf, options = {})
+ top = options[:top]
+ g_width = options[:g_width]
+ header_height = options[:header_height]
+ headers_height = options[:headers_height]
+ subject_width = options[:subject_width]
+ show_weeks = options[:show_weeks]
+ show_days = options[:show_days]
+ zoom = options[:zoom]
+
# Months headers
month_f = self.date_from
left = subject_width
- height = header_heigth
+ height = header_height
self.months.times do
width = ((month_f >> 1) - month_f) * zoom
- pdf.SetY(y_start)
+ pdf.SetY(top)
pdf.SetX(left)
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
left = left + width
@@ -892,7 +978,7 @@ def to_pdf
# Weeks headers
if show_weeks
left = subject_width
- height = header_heigth
+ height = header_height
if self.date_from.cwday == 1
# self.date_from is monday
week_f = self.date_from
@@ -900,14 +986,14 @@ def to_pdf
# find next monday after self.date_from
week_f = self.date_from + (7 - self.date_from.cwday + 1)
width = (7 - self.date_from.cwday + 1) * zoom-1
- pdf.SetY(y_start + header_heigth)
+ pdf.SetY(top + header_height)
pdf.SetX(left)
pdf.Cell(width + 1, height, "", "LTR")
left = left + width+1
end
while week_f <= self.date_to
width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom
- pdf.SetY(y_start + header_heigth)
+ pdf.SetY(top + header_height)
pdf.SetX(left)
pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
left = left + width
@@ -918,70 +1004,44 @@ def to_pdf
# Days headers
if show_days
left = subject_width
- height = header_heigth
+ height = header_height
wday = self.date_from.cwday
pdf.SetFontStyle('B',7)
(self.date_to - self.date_from + 1).to_i.times do
width = zoom
- pdf.SetY(y_start + 2 * header_heigth)
+ pdf.SetY(top + 2 * header_height)
pdf.SetX(left)
pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
left = left + width
wday = wday + 1
wday = 1 if wday > 7
end
- end
-
- pdf.SetY(y_start)
- pdf.SetX(15)
- pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
-
- # Tasks
- top = headers_heigth + y_start
- pdf_subjects_and_lines(pdf, {
- :top => top,
- :zoom => zoom,
- :subject_width => subject_width,
- :g_width => g_width
- })
+ end
-
- pdf.Line(15, top, subject_width+g_width, top)
- pdf.Output
-
-
- end
-
- private
-
- # Sorts a collection of issues by start_date, due_date, id for gantt rendering
- def sort_issues!(issues)
- issues.sort! do |a, b|
- cmp = 0
- cmp = (a.start_date <=> b.start_date) if a.start_date? && b.start_date?
- cmp = (a.due_date <=> b.due_date) if cmp == 0 && a.due_date? && b.due_date?
- cmp = (a.id <=> b.id) if cmp == 0
- cmp
- end
+ pdf.SetY(top)
+ pdf.SetX(15)
+ pdf.Cell(subject_width + g_width - 15, headers_height, "", 1)
end
# Renders both the subjects and lines of the Gantt chart for the
# PDF format
def pdf_subjects_and_lines(pdf, options = {})
- subject_options = {:indent => 0, :indent_increment => 5, :top_increment => 3, :render => :subject, :format => :pdf, :pdf => pdf}.merge(options)
- line_options = {:indent => 0, :indent_increment => 5, :top_increment => 3, :render => :line, :format => :pdf, :pdf => pdf}.merge(options)
+ # Don't use Hash#merge to set defaults because the calling method
+ # wants to read the final value of options[:top]
+ options[:indent] ||= 0
+ options[:indent_increment] ||= 5
+ options[:top_increment] ||= 3
+ options[:format] ||= :pdf
+ options[:pdf] ||= pdf
if @project
- render_project(@project, subject_options)
- render_project(@project, line_options)
+ render_project(@project, options)
else
Project.roots.each do |project|
- render_project(project, subject_options)
- render_project(project, line_options)
+ render_project(project, options)
end
end
end
-
end
end
end