##// END OF EJS Templates
pdf: restore "id_width" parameter of issues_to_pdf_draw_borders (#14178)...
Toshi MARUYAMA -
r11690:7bc0c2619836
parent child
Show More
@@ -1,806 +1,808
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require 'tcpdf'
20 require 'tcpdf'
21 require 'fpdf/chinese'
21 require 'fpdf/chinese'
22 require 'fpdf/japanese'
22 require 'fpdf/japanese'
23 require 'fpdf/korean'
23 require 'fpdf/korean'
24
24
25 if RUBY_VERSION < '1.9'
25 if RUBY_VERSION < '1.9'
26 require 'iconv'
26 require 'iconv'
27 end
27 end
28
28
29 module Redmine
29 module Redmine
30 module Export
30 module Export
31 module PDF
31 module PDF
32 include ActionView::Helpers::TextHelper
32 include ActionView::Helpers::TextHelper
33 include ActionView::Helpers::NumberHelper
33 include ActionView::Helpers::NumberHelper
34 include IssuesHelper
34 include IssuesHelper
35
35
36 class ITCPDF < TCPDF
36 class ITCPDF < TCPDF
37 include Redmine::I18n
37 include Redmine::I18n
38 attr_accessor :footer_date
38 attr_accessor :footer_date
39
39
40 def initialize(lang, orientation='P')
40 def initialize(lang, orientation='P')
41 @@k_path_cache = Rails.root.join('tmp', 'pdf')
41 @@k_path_cache = Rails.root.join('tmp', 'pdf')
42 FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
42 FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
43 set_language_if_valid lang
43 set_language_if_valid lang
44 pdf_encoding = l(:general_pdf_encoding).upcase
44 pdf_encoding = l(:general_pdf_encoding).upcase
45 super(orientation, 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
45 super(orientation, 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
46 case current_language.to_s.downcase
46 case current_language.to_s.downcase
47 when 'vi'
47 when 'vi'
48 @font_for_content = 'DejaVuSans'
48 @font_for_content = 'DejaVuSans'
49 @font_for_footer = 'DejaVuSans'
49 @font_for_footer = 'DejaVuSans'
50 else
50 else
51 case pdf_encoding
51 case pdf_encoding
52 when 'UTF-8'
52 when 'UTF-8'
53 @font_for_content = 'FreeSans'
53 @font_for_content = 'FreeSans'
54 @font_for_footer = 'FreeSans'
54 @font_for_footer = 'FreeSans'
55 when 'CP949'
55 when 'CP949'
56 extend(PDF_Korean)
56 extend(PDF_Korean)
57 AddUHCFont()
57 AddUHCFont()
58 @font_for_content = 'UHC'
58 @font_for_content = 'UHC'
59 @font_for_footer = 'UHC'
59 @font_for_footer = 'UHC'
60 when 'CP932', 'SJIS', 'SHIFT_JIS'
60 when 'CP932', 'SJIS', 'SHIFT_JIS'
61 extend(PDF_Japanese)
61 extend(PDF_Japanese)
62 AddSJISFont()
62 AddSJISFont()
63 @font_for_content = 'SJIS'
63 @font_for_content = 'SJIS'
64 @font_for_footer = 'SJIS'
64 @font_for_footer = 'SJIS'
65 when 'GB18030'
65 when 'GB18030'
66 extend(PDF_Chinese)
66 extend(PDF_Chinese)
67 AddGBFont()
67 AddGBFont()
68 @font_for_content = 'GB'
68 @font_for_content = 'GB'
69 @font_for_footer = 'GB'
69 @font_for_footer = 'GB'
70 when 'BIG5'
70 when 'BIG5'
71 extend(PDF_Chinese)
71 extend(PDF_Chinese)
72 AddBig5Font()
72 AddBig5Font()
73 @font_for_content = 'Big5'
73 @font_for_content = 'Big5'
74 @font_for_footer = 'Big5'
74 @font_for_footer = 'Big5'
75 else
75 else
76 @font_for_content = 'Arial'
76 @font_for_content = 'Arial'
77 @font_for_footer = 'Helvetica'
77 @font_for_footer = 'Helvetica'
78 end
78 end
79 end
79 end
80 SetCreator(Redmine::Info.app_name)
80 SetCreator(Redmine::Info.app_name)
81 SetFont(@font_for_content)
81 SetFont(@font_for_content)
82 @outlines = []
82 @outlines = []
83 @outlineRoot = nil
83 @outlineRoot = nil
84 end
84 end
85
85
86 def SetFontStyle(style, size)
86 def SetFontStyle(style, size)
87 SetFont(@font_for_content, style, size)
87 SetFont(@font_for_content, style, size)
88 end
88 end
89
89
90 def SetTitle(txt)
90 def SetTitle(txt)
91 txt = begin
91 txt = begin
92 utf16txt = to_utf16(txt)
92 utf16txt = to_utf16(txt)
93 hextxt = "<FEFF" # FEFF is BOM
93 hextxt = "<FEFF" # FEFF is BOM
94 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
94 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
95 hextxt << ">"
95 hextxt << ">"
96 rescue
96 rescue
97 txt
97 txt
98 end || ''
98 end || ''
99 super(txt)
99 super(txt)
100 end
100 end
101
101
102 def textstring(s)
102 def textstring(s)
103 # Format a text string
103 # Format a text string
104 if s =~ /^</ # This means the string is hex-dumped.
104 if s =~ /^</ # This means the string is hex-dumped.
105 return s
105 return s
106 else
106 else
107 return '('+escape(s)+')'
107 return '('+escape(s)+')'
108 end
108 end
109 end
109 end
110
110
111 def fix_text_encoding(txt)
111 def fix_text_encoding(txt)
112 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
112 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
113 end
113 end
114
114
115 def formatted_text(text)
115 def formatted_text(text)
116 html = Redmine::WikiFormatting.to_html(Setting.text_formatting, text)
116 html = Redmine::WikiFormatting.to_html(Setting.text_formatting, text)
117 # Strip {{toc}} tags
117 # Strip {{toc}} tags
118 html.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i, '')
118 html.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i, '')
119 html
119 html
120 end
120 end
121
121
122 # Encodes an UTF-8 string to UTF-16BE
122 # Encodes an UTF-8 string to UTF-16BE
123 def to_utf16(str)
123 def to_utf16(str)
124 if str.respond_to?(:encode)
124 if str.respond_to?(:encode)
125 str.encode('UTF-16BE')
125 str.encode('UTF-16BE')
126 else
126 else
127 Iconv.conv('UTF-16BE', 'UTF-8', str)
127 Iconv.conv('UTF-16BE', 'UTF-8', str)
128 end
128 end
129 end
129 end
130
130
131 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
131 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
132 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
132 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
133 end
133 end
134
134
135 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
135 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
136 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
136 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
137 end
137 end
138
138
139 def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
139 def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
140 @attachments = attachments
140 @attachments = attachments
141 writeHTMLCell(w, h, x, y,
141 writeHTMLCell(w, h, x, y,
142 fix_text_encoding(formatted_text(txt)),
142 fix_text_encoding(formatted_text(txt)),
143 border, ln, fill)
143 border, ln, fill)
144 end
144 end
145
145
146 def getImageFilename(attrname)
146 def getImageFilename(attrname)
147 # attrname: general_pdf_encoding string file/uri name
147 # attrname: general_pdf_encoding string file/uri name
148 atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
148 atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
149 if atta
149 if atta
150 return atta.diskfile
150 return atta.diskfile
151 else
151 else
152 return nil
152 return nil
153 end
153 end
154 end
154 end
155
155
156 def Footer
156 def Footer
157 SetFont(@font_for_footer, 'I', 8)
157 SetFont(@font_for_footer, 'I', 8)
158 SetY(-15)
158 SetY(-15)
159 SetX(15)
159 SetX(15)
160 RDMCell(0, 5, @footer_date, 0, 0, 'L')
160 RDMCell(0, 5, @footer_date, 0, 0, 'L')
161 SetY(-15)
161 SetY(-15)
162 SetX(-30)
162 SetX(-30)
163 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
163 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
164 end
164 end
165
165
166 def Bookmark(txt, level=0, y=0)
166 def Bookmark(txt, level=0, y=0)
167 if (y == -1)
167 if (y == -1)
168 y = GetY()
168 y = GetY()
169 end
169 end
170 @outlines << {:t => txt, :l => level, :p => PageNo(), :y => (@h - y)*@k}
170 @outlines << {:t => txt, :l => level, :p => PageNo(), :y => (@h - y)*@k}
171 end
171 end
172
172
173 def bookmark_title(txt)
173 def bookmark_title(txt)
174 txt = begin
174 txt = begin
175 utf16txt = to_utf16(txt)
175 utf16txt = to_utf16(txt)
176 hextxt = "<FEFF" # FEFF is BOM
176 hextxt = "<FEFF" # FEFF is BOM
177 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
177 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
178 hextxt << ">"
178 hextxt << ">"
179 rescue
179 rescue
180 txt
180 txt
181 end || ''
181 end || ''
182 end
182 end
183
183
184 def putbookmarks
184 def putbookmarks
185 nb=@outlines.size
185 nb=@outlines.size
186 return if (nb==0)
186 return if (nb==0)
187 lru=[]
187 lru=[]
188 level=0
188 level=0
189 @outlines.each_with_index do |o, i|
189 @outlines.each_with_index do |o, i|
190 if(o[:l]>0)
190 if(o[:l]>0)
191 parent=lru[o[:l]-1]
191 parent=lru[o[:l]-1]
192 #Set parent and last pointers
192 #Set parent and last pointers
193 @outlines[i][:parent]=parent
193 @outlines[i][:parent]=parent
194 @outlines[parent][:last]=i
194 @outlines[parent][:last]=i
195 if (o[:l]>level)
195 if (o[:l]>level)
196 #Level increasing: set first pointer
196 #Level increasing: set first pointer
197 @outlines[parent][:first]=i
197 @outlines[parent][:first]=i
198 end
198 end
199 else
199 else
200 @outlines[i][:parent]=nb
200 @outlines[i][:parent]=nb
201 end
201 end
202 if (o[:l]<=level && i>0)
202 if (o[:l]<=level && i>0)
203 #Set prev and next pointers
203 #Set prev and next pointers
204 prev=lru[o[:l]]
204 prev=lru[o[:l]]
205 @outlines[prev][:next]=i
205 @outlines[prev][:next]=i
206 @outlines[i][:prev]=prev
206 @outlines[i][:prev]=prev
207 end
207 end
208 lru[o[:l]]=i
208 lru[o[:l]]=i
209 level=o[:l]
209 level=o[:l]
210 end
210 end
211 #Outline items
211 #Outline items
212 n=self.n+1
212 n=self.n+1
213 @outlines.each_with_index do |o, i|
213 @outlines.each_with_index do |o, i|
214 newobj()
214 newobj()
215 out('<</Title '+bookmark_title(o[:t]))
215 out('<</Title '+bookmark_title(o[:t]))
216 out("/Parent #{n+o[:parent]} 0 R")
216 out("/Parent #{n+o[:parent]} 0 R")
217 if (o[:prev])
217 if (o[:prev])
218 out("/Prev #{n+o[:prev]} 0 R")
218 out("/Prev #{n+o[:prev]} 0 R")
219 end
219 end
220 if (o[:next])
220 if (o[:next])
221 out("/Next #{n+o[:next]} 0 R")
221 out("/Next #{n+o[:next]} 0 R")
222 end
222 end
223 if (o[:first])
223 if (o[:first])
224 out("/First #{n+o[:first]} 0 R")
224 out("/First #{n+o[:first]} 0 R")
225 end
225 end
226 if (o[:last])
226 if (o[:last])
227 out("/Last #{n+o[:last]} 0 R")
227 out("/Last #{n+o[:last]} 0 R")
228 end
228 end
229 out("/Dest [%d 0 R /XYZ 0 %.2f null]" % [1+2*o[:p], o[:y]])
229 out("/Dest [%d 0 R /XYZ 0 %.2f null]" % [1+2*o[:p], o[:y]])
230 out('/Count 0>>')
230 out('/Count 0>>')
231 out('endobj')
231 out('endobj')
232 end
232 end
233 #Outline root
233 #Outline root
234 newobj()
234 newobj()
235 @outlineRoot=self.n
235 @outlineRoot=self.n
236 out("<</Type /Outlines /First #{n} 0 R");
236 out("<</Type /Outlines /First #{n} 0 R");
237 out("/Last #{n+lru[0]} 0 R>>");
237 out("/Last #{n+lru[0]} 0 R>>");
238 out('endobj');
238 out('endobj');
239 end
239 end
240
240
241 def putresources()
241 def putresources()
242 super
242 super
243 putbookmarks()
243 putbookmarks()
244 end
244 end
245
245
246 def putcatalog()
246 def putcatalog()
247 super
247 super
248 if(@outlines.size > 0)
248 if(@outlines.size > 0)
249 out("/Outlines #{@outlineRoot} 0 R");
249 out("/Outlines #{@outlineRoot} 0 R");
250 out('/PageMode /UseOutlines');
250 out('/PageMode /UseOutlines');
251 end
251 end
252 end
252 end
253 end
253 end
254
254
255 # fetch row values
255 # fetch row values
256 def fetch_row_values(issue, query, level)
256 def fetch_row_values(issue, query, level)
257 query.inline_columns.collect do |column|
257 query.inline_columns.collect do |column|
258 s = if column.is_a?(QueryCustomFieldColumn)
258 s = if column.is_a?(QueryCustomFieldColumn)
259 cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
259 cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
260 show_value(cv)
260 show_value(cv)
261 else
261 else
262 value = issue.send(column.name)
262 value = issue.send(column.name)
263 if column.name == :subject
263 if column.name == :subject
264 value = " " * level + value
264 value = " " * level + value
265 end
265 end
266 if value.is_a?(Date)
266 if value.is_a?(Date)
267 format_date(value)
267 format_date(value)
268 elsif value.is_a?(Time)
268 elsif value.is_a?(Time)
269 format_time(value)
269 format_time(value)
270 else
270 else
271 value
271 value
272 end
272 end
273 end
273 end
274 s.to_s
274 s.to_s
275 end
275 end
276 end
276 end
277
277
278 # calculate columns width
278 # calculate columns width
279 def calc_col_width(issues, query, table_width, pdf)
279 def calc_col_width(issues, query, table_width, pdf)
280 # calculate statistics
280 # calculate statistics
281 # by captions
281 # by captions
282 pdf.SetFontStyle('B',8)
282 pdf.SetFontStyle('B',8)
283 col_padding = pdf.GetStringWidth('OO')
283 col_padding = pdf.GetStringWidth('OO')
284 col_width_min = query.inline_columns.map {|v| pdf.GetStringWidth(v.caption) + col_padding}
284 col_width_min = query.inline_columns.map {|v| pdf.GetStringWidth(v.caption) + col_padding}
285 col_width_max = Array.new(col_width_min)
285 col_width_max = Array.new(col_width_min)
286 col_width_avg = Array.new(col_width_min)
286 col_width_avg = Array.new(col_width_min)
287 word_width_max = query.inline_columns.map {|c|
287 word_width_max = query.inline_columns.map {|c|
288 n = 10
288 n = 10
289 c.caption.split.each {|w|
289 c.caption.split.each {|w|
290 x = pdf.GetStringWidth(w) + col_padding
290 x = pdf.GetStringWidth(w) + col_padding
291 n = x if n < x
291 n = x if n < x
292 }
292 }
293 n
293 n
294 }
294 }
295
295
296 # by properties of issues
296 # by properties of issues
297 pdf.SetFontStyle('',8)
297 pdf.SetFontStyle('',8)
298 col_padding = pdf.GetStringWidth('OO')
298 col_padding = pdf.GetStringWidth('OO')
299 k = 1
299 k = 1
300 issue_list(issues) {|issue, level|
300 issue_list(issues) {|issue, level|
301 k += 1
301 k += 1
302 values = fetch_row_values(issue, query, level)
302 values = fetch_row_values(issue, query, level)
303 values.each_with_index {|v,i|
303 values.each_with_index {|v,i|
304 n = pdf.GetStringWidth(v) + col_padding
304 n = pdf.GetStringWidth(v) + col_padding
305 col_width_max[i] = n if col_width_max[i] < n
305 col_width_max[i] = n if col_width_max[i] < n
306 col_width_min[i] = n if col_width_min[i] > n
306 col_width_min[i] = n if col_width_min[i] > n
307 col_width_avg[i] += n
307 col_width_avg[i] += n
308 v.split.each {|w|
308 v.split.each {|w|
309 x = pdf.GetStringWidth(w) + col_padding
309 x = pdf.GetStringWidth(w) + col_padding
310 word_width_max[i] = x if word_width_max[i] < x
310 word_width_max[i] = x if word_width_max[i] < x
311 }
311 }
312 }
312 }
313 }
313 }
314 col_width_avg.map! {|x| x / k}
314 col_width_avg.map! {|x| x / k}
315
315
316 # calculate columns width
316 # calculate columns width
317 ratio = table_width / col_width_avg.inject(0, :+)
317 ratio = table_width / col_width_avg.inject(0, :+)
318 col_width = col_width_avg.map {|w| w * ratio}
318 col_width = col_width_avg.map {|w| w * ratio}
319
319
320 # correct max word width if too many columns
320 # correct max word width if too many columns
321 ratio = table_width / word_width_max.inject(0, :+)
321 ratio = table_width / word_width_max.inject(0, :+)
322 word_width_max.map! {|v| v * ratio} if ratio < 1
322 word_width_max.map! {|v| v * ratio} if ratio < 1
323
323
324 # correct and lock width of some columns
324 # correct and lock width of some columns
325 done = 1
325 done = 1
326 col_fix = []
326 col_fix = []
327 col_width.each_with_index do |w,i|
327 col_width.each_with_index do |w,i|
328 if w > col_width_max[i]
328 if w > col_width_max[i]
329 col_width[i] = col_width_max[i]
329 col_width[i] = col_width_max[i]
330 col_fix[i] = 1
330 col_fix[i] = 1
331 done = 0
331 done = 0
332 elsif w < word_width_max[i]
332 elsif w < word_width_max[i]
333 col_width[i] = word_width_max[i]
333 col_width[i] = word_width_max[i]
334 col_fix[i] = 1
334 col_fix[i] = 1
335 done = 0
335 done = 0
336 else
336 else
337 col_fix[i] = 0
337 col_fix[i] = 0
338 end
338 end
339 end
339 end
340
340
341 # iterate while need to correct and lock coluns width
341 # iterate while need to correct and lock coluns width
342 while done == 0
342 while done == 0
343 # calculate free & locked columns width
343 # calculate free & locked columns width
344 done = 1
344 done = 1
345 fix_col_width = 0
345 fix_col_width = 0
346 free_col_width = 0
346 free_col_width = 0
347 col_width.each_with_index do |w,i|
347 col_width.each_with_index do |w,i|
348 if col_fix[i] == 1
348 if col_fix[i] == 1
349 fix_col_width += w
349 fix_col_width += w
350 else
350 else
351 free_col_width += w
351 free_col_width += w
352 end
352 end
353 end
353 end
354
354
355 # calculate column normalizing ratio
355 # calculate column normalizing ratio
356 if free_col_width == 0
356 if free_col_width == 0
357 ratio = table_width / col_width.inject(0, :+)
357 ratio = table_width / col_width.inject(0, :+)
358 else
358 else
359 ratio = (table_width - fix_col_width) / free_col_width
359 ratio = (table_width - fix_col_width) / free_col_width
360 end
360 end
361
361
362 # correct columns width
362 # correct columns width
363 col_width.each_with_index do |w,i|
363 col_width.each_with_index do |w,i|
364 if col_fix[i] == 0
364 if col_fix[i] == 0
365 col_width[i] = w * ratio
365 col_width[i] = w * ratio
366
366
367 # check if column width less then max word width
367 # check if column width less then max word width
368 if col_width[i] < word_width_max[i]
368 if col_width[i] < word_width_max[i]
369 col_width[i] = word_width_max[i]
369 col_width[i] = word_width_max[i]
370 col_fix[i] = 1
370 col_fix[i] = 1
371 done = 0
371 done = 0
372 elsif col_width[i] > col_width_max[i]
372 elsif col_width[i] > col_width_max[i]
373 col_width[i] = col_width_max[i]
373 col_width[i] = col_width_max[i]
374 col_fix[i] = 1
374 col_fix[i] = 1
375 done = 0
375 done = 0
376 end
376 end
377 end
377 end
378 end
378 end
379 end
379 end
380 col_width
380 col_width
381 end
381 end
382
382
383 def render_table_header(pdf, query, col_width, row_height, table_width)
383 def render_table_header(pdf, query, col_width, row_height, table_width)
384 # headers
384 # headers
385 pdf.SetFontStyle('B',8)
385 pdf.SetFontStyle('B',8)
386 pdf.SetFillColor(230, 230, 230)
386 pdf.SetFillColor(230, 230, 230)
387
387
388 # render it background to find the max height used
388 # render it background to find the max height used
389 base_x = pdf.GetX
389 base_x = pdf.GetX
390 base_y = pdf.GetY
390 base_y = pdf.GetY
391 max_height = issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true)
391 max_height = issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true)
392 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
392 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
393 pdf.SetXY(base_x, base_y);
393 pdf.SetXY(base_x, base_y);
394
394
395 # write the cells on page
395 # write the cells on page
396 issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true)
396 issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true)
397 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_width)
397 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width)
398 pdf.SetY(base_y + max_height);
398 pdf.SetY(base_y + max_height);
399
399
400 # rows
400 # rows
401 pdf.SetFontStyle('',8)
401 pdf.SetFontStyle('',8)
402 pdf.SetFillColor(255, 255, 255)
402 pdf.SetFillColor(255, 255, 255)
403 end
403 end
404
404
405 # Returns a PDF string of a list of issues
405 # Returns a PDF string of a list of issues
406 def issues_to_pdf(issues, project, query)
406 def issues_to_pdf(issues, project, query)
407 pdf = ITCPDF.new(current_language, "L")
407 pdf = ITCPDF.new(current_language, "L")
408 title = query.new_record? ? l(:label_issue_plural) : query.name
408 title = query.new_record? ? l(:label_issue_plural) : query.name
409 title = "#{project} - #{title}" if project
409 title = "#{project} - #{title}" if project
410 pdf.SetTitle(title)
410 pdf.SetTitle(title)
411 pdf.alias_nb_pages
411 pdf.alias_nb_pages
412 pdf.footer_date = format_date(Date.today)
412 pdf.footer_date = format_date(Date.today)
413 pdf.SetAutoPageBreak(false)
413 pdf.SetAutoPageBreak(false)
414 pdf.AddPage("L")
414 pdf.AddPage("L")
415
415
416 # Landscape A4 = 210 x 297 mm
416 # Landscape A4 = 210 x 297 mm
417 page_height = 210
417 page_height = 210
418 page_width = 297
418 page_width = 297
419 left_margin = 10
419 left_margin = 10
420 right_margin = 10
420 right_margin = 10
421 bottom_margin = 20
421 bottom_margin = 20
422 row_height = 4
422 row_height = 4
423
423
424 # column widths
424 # column widths
425 table_width = page_width - right_margin - left_margin
425 table_width = page_width - right_margin - left_margin
426 col_width = []
426 col_width = []
427 unless query.inline_columns.empty?
427 unless query.inline_columns.empty?
428 col_width = calc_col_width(issues, query, table_width, pdf)
428 col_width = calc_col_width(issues, query, table_width, pdf)
429 table_width = col_width.inject(0, :+)
429 table_width = col_width.inject(0, :+)
430 end
430 end
431
431
432 # use full width if the description is displayed
432 # use full width if the description is displayed
433 if table_width > 0 && query.has_column?(:description)
433 if table_width > 0 && query.has_column?(:description)
434 col_width = col_width.map {|w| w * (page_width - right_margin - left_margin) / table_width}
434 col_width = col_width.map {|w| w * (page_width - right_margin - left_margin) / table_width}
435 table_width = col_width.inject(0, :+)
435 table_width = col_width.inject(0, :+)
436 end
436 end
437
437
438 # title
438 # title
439 pdf.SetFontStyle('B',11)
439 pdf.SetFontStyle('B',11)
440 pdf.RDMCell(190,10, title)
440 pdf.RDMCell(190,10, title)
441 pdf.Ln
441 pdf.Ln
442 render_table_header(pdf, query, col_width, row_height, table_width)
442 render_table_header(pdf, query, col_width, row_height, table_width)
443 previous_group = false
443 previous_group = false
444 issue_list(issues) do |issue, level|
444 issue_list(issues) do |issue, level|
445 if query.grouped? &&
445 if query.grouped? &&
446 (group = query.group_by_column.value(issue)) != previous_group
446 (group = query.group_by_column.value(issue)) != previous_group
447 pdf.SetFontStyle('B',10)
447 pdf.SetFontStyle('B',10)
448 group_label = group.blank? ? 'None' : group.to_s.dup
448 group_label = group.blank? ? 'None' : group.to_s.dup
449 group_label << " (#{query.issue_count_by_group[group]})"
449 group_label << " (#{query.issue_count_by_group[group]})"
450 pdf.Bookmark group_label, 0, -1
450 pdf.Bookmark group_label, 0, -1
451 pdf.RDMCell(table_width, row_height * 2, group_label, 1, 1, 'L')
451 pdf.RDMCell(table_width, row_height * 2, group_label, 1, 1, 'L')
452 pdf.SetFontStyle('',8)
452 pdf.SetFontStyle('',8)
453 previous_group = group
453 previous_group = group
454 end
454 end
455
455
456 # fetch row values
456 # fetch row values
457 col_values = fetch_row_values(issue, query, level)
457 col_values = fetch_row_values(issue, query, level)
458
458
459 # render it off-page to find the max height used
459 # render it off-page to find the max height used
460 base_x = pdf.GetX
460 base_x = pdf.GetX
461 base_y = pdf.GetY
461 base_y = pdf.GetY
462 pdf.SetY(2 * page_height)
462 pdf.SetY(2 * page_height)
463 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
463 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
464 pdf.SetXY(base_x, base_y)
464 pdf.SetXY(base_x, base_y)
465
465
466 # make new page if it doesn't fit on the current one
466 # make new page if it doesn't fit on the current one
467 space_left = page_height - base_y - bottom_margin
467 space_left = page_height - base_y - bottom_margin
468 if max_height > space_left
468 if max_height > space_left
469 pdf.AddPage("L")
469 pdf.AddPage("L")
470 render_table_header(pdf, query, col_width, row_height, table_width)
470 render_table_header(pdf, query, col_width, row_height, table_width)
471 base_x = pdf.GetX
471 base_x = pdf.GetX
472 base_y = pdf.GetY
472 base_y = pdf.GetY
473 end
473 end
474
474
475 # write the cells on page
475 # write the cells on page
476 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
476 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
477 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_width)
477 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width)
478 pdf.SetY(base_y + max_height);
478 pdf.SetY(base_y + max_height);
479
479
480 if query.has_column?(:description) && issue.description?
480 if query.has_column?(:description) && issue.description?
481 pdf.SetX(10)
481 pdf.SetX(10)
482 pdf.SetAutoPageBreak(true, 20)
482 pdf.SetAutoPageBreak(true, 20)
483 pdf.RDMwriteHTMLCell(0, 5, 10, 0, issue.description.to_s, issue.attachments, "LRBT")
483 pdf.RDMwriteHTMLCell(0, 5, 10, 0, issue.description.to_s, issue.attachments, "LRBT")
484 pdf.SetAutoPageBreak(false)
484 pdf.SetAutoPageBreak(false)
485 end
485 end
486 end
486 end
487
487
488 if issues.size == Setting.issues_export_limit.to_i
488 if issues.size == Setting.issues_export_limit.to_i
489 pdf.SetFontStyle('B',10)
489 pdf.SetFontStyle('B',10)
490 pdf.RDMCell(0, row_height, '...')
490 pdf.RDMCell(0, row_height, '...')
491 end
491 end
492 pdf.Output
492 pdf.Output
493 end
493 end
494
494
495 # Renders MultiCells and returns the maximum height used
495 # Renders MultiCells and returns the maximum height used
496 def issues_to_pdf_write_cells(pdf, col_values, col_widths, row_height, head=false)
496 def issues_to_pdf_write_cells(pdf, col_values, col_widths, row_height, head=false)
497 base_y = pdf.GetY
497 base_y = pdf.GetY
498 max_height = row_height
498 max_height = row_height
499 col_values.each_with_index do |column, i|
499 col_values.each_with_index do |column, i|
500 col_x = pdf.GetX
500 col_x = pdf.GetX
501 if head == true
501 if head == true
502 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
502 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
503 else
503 else
504 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
504 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
505 end
505 end
506 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
506 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
507 pdf.SetXY(col_x + col_widths[i], base_y);
507 pdf.SetXY(col_x + col_widths[i], base_y);
508 end
508 end
509 return max_height
509 return max_height
510 end
510 end
511
511
512 # Draw lines to close the row (MultiCell border drawing in not uniform)
512 # Draw lines to close the row (MultiCell border drawing in not uniform)
513 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, col_widths)
513 #
514 # parameter "id_width" is not used. it is kept for compatibility.
515 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, id_width, col_widths)
514 col_x = top_x
516 col_x = top_x
515 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
517 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
516 col_widths.each do |width|
518 col_widths.each do |width|
517 col_x += width
519 col_x += width
518 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
520 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
519 end
521 end
520 pdf.Line(top_x, top_y, top_x, lower_y) # left border
522 pdf.Line(top_x, top_y, top_x, lower_y) # left border
521 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
523 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
522 end
524 end
523
525
524 # Returns a PDF string of a single issue
526 # Returns a PDF string of a single issue
525 def issue_to_pdf(issue, assoc={})
527 def issue_to_pdf(issue, assoc={})
526 pdf = ITCPDF.new(current_language)
528 pdf = ITCPDF.new(current_language)
527 pdf.SetTitle("#{issue.project} - #{issue.tracker} ##{issue.id}")
529 pdf.SetTitle("#{issue.project} - #{issue.tracker} ##{issue.id}")
528 pdf.alias_nb_pages
530 pdf.alias_nb_pages
529 pdf.footer_date = format_date(Date.today)
531 pdf.footer_date = format_date(Date.today)
530 pdf.AddPage
532 pdf.AddPage
531 pdf.SetFontStyle('B',11)
533 pdf.SetFontStyle('B',11)
532 buf = "#{issue.project} - #{issue.tracker} ##{issue.id}"
534 buf = "#{issue.project} - #{issue.tracker} ##{issue.id}"
533 pdf.RDMMultiCell(190, 5, buf)
535 pdf.RDMMultiCell(190, 5, buf)
534 pdf.SetFontStyle('',8)
536 pdf.SetFontStyle('',8)
535 base_x = pdf.GetX
537 base_x = pdf.GetX
536 i = 1
538 i = 1
537 issue.ancestors.visible.each do |ancestor|
539 issue.ancestors.visible.each do |ancestor|
538 pdf.SetX(base_x + i)
540 pdf.SetX(base_x + i)
539 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
541 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
540 pdf.RDMMultiCell(190 - i, 5, buf)
542 pdf.RDMMultiCell(190 - i, 5, buf)
541 i += 1 if i < 35
543 i += 1 if i < 35
542 end
544 end
543 pdf.SetFontStyle('B',11)
545 pdf.SetFontStyle('B',11)
544 pdf.RDMMultiCell(190 - i, 5, issue.subject.to_s)
546 pdf.RDMMultiCell(190 - i, 5, issue.subject.to_s)
545 pdf.SetFontStyle('',8)
547 pdf.SetFontStyle('',8)
546 pdf.RDMMultiCell(190, 5, "#{format_time(issue.created_on)} - #{issue.author}")
548 pdf.RDMMultiCell(190, 5, "#{format_time(issue.created_on)} - #{issue.author}")
547 pdf.Ln
549 pdf.Ln
548
550
549 left = []
551 left = []
550 left << [l(:field_status), issue.status]
552 left << [l(:field_status), issue.status]
551 left << [l(:field_priority), issue.priority]
553 left << [l(:field_priority), issue.priority]
552 left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')
554 left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')
553 left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')
555 left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')
554 left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')
556 left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')
555
557
556 right = []
558 right = []
557 right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')
559 right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')
558 right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')
560 right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')
559 right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio')
561 right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio')
560 right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')
562 right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')
561 right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)
563 right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)
562
564
563 rows = left.size > right.size ? left.size : right.size
565 rows = left.size > right.size ? left.size : right.size
564 while left.size < rows
566 while left.size < rows
565 left << nil
567 left << nil
566 end
568 end
567 while right.size < rows
569 while right.size < rows
568 right << nil
570 right << nil
569 end
571 end
570
572
571 half = (issue.custom_field_values.size / 2.0).ceil
573 half = (issue.custom_field_values.size / 2.0).ceil
572 issue.custom_field_values.each_with_index do |custom_value, i|
574 issue.custom_field_values.each_with_index do |custom_value, i|
573 (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)]
575 (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)]
574 end
576 end
575
577
576 rows = left.size > right.size ? left.size : right.size
578 rows = left.size > right.size ? left.size : right.size
577 rows.times do |i|
579 rows.times do |i|
578 item = left[i]
580 item = left[i]
579 pdf.SetFontStyle('B',9)
581 pdf.SetFontStyle('B',9)
580 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
582 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
581 pdf.SetFontStyle('',9)
583 pdf.SetFontStyle('',9)
582 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
584 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
583
585
584 item = right[i]
586 item = right[i]
585 pdf.SetFontStyle('B',9)
587 pdf.SetFontStyle('B',9)
586 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
588 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
587 pdf.SetFontStyle('',9)
589 pdf.SetFontStyle('',9)
588 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
590 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
589 pdf.Ln
591 pdf.Ln
590 end
592 end
591
593
592 pdf.SetFontStyle('B',9)
594 pdf.SetFontStyle('B',9)
593 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
595 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
594 pdf.SetFontStyle('',9)
596 pdf.SetFontStyle('',9)
595
597
596 # Set resize image scale
598 # Set resize image scale
597 pdf.SetImageScale(1.6)
599 pdf.SetImageScale(1.6)
598 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
600 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
599 issue.description.to_s, issue.attachments, "LRB")
601 issue.description.to_s, issue.attachments, "LRB")
600
602
601 unless issue.leaf?
603 unless issue.leaf?
602 # for CJK
604 # for CJK
603 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
605 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
604
606
605 pdf.SetFontStyle('B',9)
607 pdf.SetFontStyle('B',9)
606 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
608 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
607 pdf.Ln
609 pdf.Ln
608 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
610 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
609 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
611 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
610 :length => truncate_length)
612 :length => truncate_length)
611 level = 10 if level >= 10
613 level = 10 if level >= 10
612 pdf.SetFontStyle('',8)
614 pdf.SetFontStyle('',8)
613 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
615 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
614 pdf.SetFontStyle('B',8)
616 pdf.SetFontStyle('B',8)
615 pdf.RDMCell(20,5, child.status.to_s, "R")
617 pdf.RDMCell(20,5, child.status.to_s, "R")
616 pdf.Ln
618 pdf.Ln
617 end
619 end
618 end
620 end
619
621
620 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
622 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
621 unless relations.empty?
623 unless relations.empty?
622 # for CJK
624 # for CJK
623 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
625 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
624
626
625 pdf.SetFontStyle('B',9)
627 pdf.SetFontStyle('B',9)
626 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
628 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
627 pdf.Ln
629 pdf.Ln
628 relations.each do |relation|
630 relations.each do |relation|
629 buf = ""
631 buf = ""
630 buf += "#{l(relation.label_for(issue))} "
632 buf += "#{l(relation.label_for(issue))} "
631 if relation.delay && relation.delay != 0
633 if relation.delay && relation.delay != 0
632 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
634 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
633 end
635 end
634 if Setting.cross_project_issue_relations?
636 if Setting.cross_project_issue_relations?
635 buf += "#{relation.other_issue(issue).project} - "
637 buf += "#{relation.other_issue(issue).project} - "
636 end
638 end
637 buf += "#{relation.other_issue(issue).tracker}" +
639 buf += "#{relation.other_issue(issue).tracker}" +
638 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
640 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
639 buf = truncate(buf, :length => truncate_length)
641 buf = truncate(buf, :length => truncate_length)
640 pdf.SetFontStyle('', 8)
642 pdf.SetFontStyle('', 8)
641 pdf.RDMCell(35+155-60, 5, buf, "L")
643 pdf.RDMCell(35+155-60, 5, buf, "L")
642 pdf.SetFontStyle('B',8)
644 pdf.SetFontStyle('B',8)
643 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
645 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
644 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
646 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
645 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
647 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
646 pdf.Ln
648 pdf.Ln
647 end
649 end
648 end
650 end
649 pdf.RDMCell(190,5, "", "T")
651 pdf.RDMCell(190,5, "", "T")
650 pdf.Ln
652 pdf.Ln
651
653
652 if issue.changesets.any? &&
654 if issue.changesets.any? &&
653 User.current.allowed_to?(:view_changesets, issue.project)
655 User.current.allowed_to?(:view_changesets, issue.project)
654 pdf.SetFontStyle('B',9)
656 pdf.SetFontStyle('B',9)
655 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
657 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
656 pdf.Ln
658 pdf.Ln
657 for changeset in issue.changesets
659 for changeset in issue.changesets
658 pdf.SetFontStyle('B',8)
660 pdf.SetFontStyle('B',8)
659 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
661 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
660 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
662 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
661 pdf.RDMCell(190, 5, csstr)
663 pdf.RDMCell(190, 5, csstr)
662 pdf.Ln
664 pdf.Ln
663 unless changeset.comments.blank?
665 unless changeset.comments.blank?
664 pdf.SetFontStyle('',8)
666 pdf.SetFontStyle('',8)
665 pdf.RDMwriteHTMLCell(190,5,0,0,
667 pdf.RDMwriteHTMLCell(190,5,0,0,
666 changeset.comments.to_s, issue.attachments, "")
668 changeset.comments.to_s, issue.attachments, "")
667 end
669 end
668 pdf.Ln
670 pdf.Ln
669 end
671 end
670 end
672 end
671
673
672 if assoc[:journals].present?
674 if assoc[:journals].present?
673 pdf.SetFontStyle('B',9)
675 pdf.SetFontStyle('B',9)
674 pdf.RDMCell(190,5, l(:label_history), "B")
676 pdf.RDMCell(190,5, l(:label_history), "B")
675 pdf.Ln
677 pdf.Ln
676 assoc[:journals].each do |journal|
678 assoc[:journals].each do |journal|
677 pdf.SetFontStyle('B',8)
679 pdf.SetFontStyle('B',8)
678 title = "##{journal.indice} - #{format_time(journal.created_on)} - #{journal.user}"
680 title = "##{journal.indice} - #{format_time(journal.created_on)} - #{journal.user}"
679 title << " (#{l(:field_private_notes)})" if journal.private_notes?
681 title << " (#{l(:field_private_notes)})" if journal.private_notes?
680 pdf.RDMCell(190,5, title)
682 pdf.RDMCell(190,5, title)
681 pdf.Ln
683 pdf.Ln
682 pdf.SetFontStyle('I',8)
684 pdf.SetFontStyle('I',8)
683 details_to_strings(journal.details, true).each do |string|
685 details_to_strings(journal.details, true).each do |string|
684 pdf.RDMMultiCell(190,5, "- " + string)
686 pdf.RDMMultiCell(190,5, "- " + string)
685 end
687 end
686 if journal.notes?
688 if journal.notes?
687 pdf.Ln unless journal.details.empty?
689 pdf.Ln unless journal.details.empty?
688 pdf.SetFontStyle('',8)
690 pdf.SetFontStyle('',8)
689 pdf.RDMwriteHTMLCell(190,5,0,0,
691 pdf.RDMwriteHTMLCell(190,5,0,0,
690 journal.notes.to_s, issue.attachments, "")
692 journal.notes.to_s, issue.attachments, "")
691 end
693 end
692 pdf.Ln
694 pdf.Ln
693 end
695 end
694 end
696 end
695
697
696 if issue.attachments.any?
698 if issue.attachments.any?
697 pdf.SetFontStyle('B',9)
699 pdf.SetFontStyle('B',9)
698 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
700 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
699 pdf.Ln
701 pdf.Ln
700 for attachment in issue.attachments
702 for attachment in issue.attachments
701 pdf.SetFontStyle('',8)
703 pdf.SetFontStyle('',8)
702 pdf.RDMCell(80,5, attachment.filename)
704 pdf.RDMCell(80,5, attachment.filename)
703 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
705 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
704 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
706 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
705 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
707 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
706 pdf.Ln
708 pdf.Ln
707 end
709 end
708 end
710 end
709 pdf.Output
711 pdf.Output
710 end
712 end
711
713
712 # Returns a PDF string of a set of wiki pages
714 # Returns a PDF string of a set of wiki pages
713 def wiki_pages_to_pdf(pages, project)
715 def wiki_pages_to_pdf(pages, project)
714 pdf = ITCPDF.new(current_language)
716 pdf = ITCPDF.new(current_language)
715 pdf.SetTitle(project.name)
717 pdf.SetTitle(project.name)
716 pdf.alias_nb_pages
718 pdf.alias_nb_pages
717 pdf.footer_date = format_date(Date.today)
719 pdf.footer_date = format_date(Date.today)
718 pdf.AddPage
720 pdf.AddPage
719 pdf.SetFontStyle('B',11)
721 pdf.SetFontStyle('B',11)
720 pdf.RDMMultiCell(190,5, project.name)
722 pdf.RDMMultiCell(190,5, project.name)
721 pdf.Ln
723 pdf.Ln
722 # Set resize image scale
724 # Set resize image scale
723 pdf.SetImageScale(1.6)
725 pdf.SetImageScale(1.6)
724 pdf.SetFontStyle('',9)
726 pdf.SetFontStyle('',9)
725 write_page_hierarchy(pdf, pages.group_by(&:parent_id))
727 write_page_hierarchy(pdf, pages.group_by(&:parent_id))
726 pdf.Output
728 pdf.Output
727 end
729 end
728
730
729 # Returns a PDF string of a single wiki page
731 # Returns a PDF string of a single wiki page
730 def wiki_page_to_pdf(page, project)
732 def wiki_page_to_pdf(page, project)
731 pdf = ITCPDF.new(current_language)
733 pdf = ITCPDF.new(current_language)
732 pdf.SetTitle("#{project} - #{page.title}")
734 pdf.SetTitle("#{project} - #{page.title}")
733 pdf.alias_nb_pages
735 pdf.alias_nb_pages
734 pdf.footer_date = format_date(Date.today)
736 pdf.footer_date = format_date(Date.today)
735 pdf.AddPage
737 pdf.AddPage
736 pdf.SetFontStyle('B',11)
738 pdf.SetFontStyle('B',11)
737 pdf.RDMMultiCell(190,5,
739 pdf.RDMMultiCell(190,5,
738 "#{project} - #{page.title} - # #{page.content.version}")
740 "#{project} - #{page.title} - # #{page.content.version}")
739 pdf.Ln
741 pdf.Ln
740 # Set resize image scale
742 # Set resize image scale
741 pdf.SetImageScale(1.6)
743 pdf.SetImageScale(1.6)
742 pdf.SetFontStyle('',9)
744 pdf.SetFontStyle('',9)
743 write_wiki_page(pdf, page)
745 write_wiki_page(pdf, page)
744 pdf.Output
746 pdf.Output
745 end
747 end
746
748
747 def write_page_hierarchy(pdf, pages, node=nil, level=0)
749 def write_page_hierarchy(pdf, pages, node=nil, level=0)
748 if pages[node]
750 if pages[node]
749 pages[node].each do |page|
751 pages[node].each do |page|
750 if @new_page
752 if @new_page
751 pdf.AddPage
753 pdf.AddPage
752 else
754 else
753 @new_page = true
755 @new_page = true
754 end
756 end
755 pdf.Bookmark page.title, level
757 pdf.Bookmark page.title, level
756 write_wiki_page(pdf, page)
758 write_wiki_page(pdf, page)
757 write_page_hierarchy(pdf, pages, page.id, level + 1) if pages[page.id]
759 write_page_hierarchy(pdf, pages, page.id, level + 1) if pages[page.id]
758 end
760 end
759 end
761 end
760 end
762 end
761
763
762 def write_wiki_page(pdf, page)
764 def write_wiki_page(pdf, page)
763 pdf.RDMwriteHTMLCell(190,5,0,0,
765 pdf.RDMwriteHTMLCell(190,5,0,0,
764 page.content.text.to_s, page.attachments, 0)
766 page.content.text.to_s, page.attachments, 0)
765 if page.attachments.any?
767 if page.attachments.any?
766 pdf.Ln
768 pdf.Ln
767 pdf.SetFontStyle('B',9)
769 pdf.SetFontStyle('B',9)
768 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
770 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
769 pdf.Ln
771 pdf.Ln
770 for attachment in page.attachments
772 for attachment in page.attachments
771 pdf.SetFontStyle('',8)
773 pdf.SetFontStyle('',8)
772 pdf.RDMCell(80,5, attachment.filename)
774 pdf.RDMCell(80,5, attachment.filename)
773 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
775 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
774 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
776 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
775 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
777 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
776 pdf.Ln
778 pdf.Ln
777 end
779 end
778 end
780 end
779 end
781 end
780
782
781 class RDMPdfEncoding
783 class RDMPdfEncoding
782 def self.rdm_from_utf8(txt, encoding)
784 def self.rdm_from_utf8(txt, encoding)
783 txt ||= ''
785 txt ||= ''
784 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
786 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
785 if txt.respond_to?(:force_encoding)
787 if txt.respond_to?(:force_encoding)
786 txt.force_encoding('ASCII-8BIT')
788 txt.force_encoding('ASCII-8BIT')
787 end
789 end
788 txt
790 txt
789 end
791 end
790
792
791 def self.attach(attachments, filename, encoding)
793 def self.attach(attachments, filename, encoding)
792 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
794 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
793 atta = nil
795 atta = nil
794 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
796 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
795 atta = Attachment.latest_attach(attachments, filename_utf8)
797 atta = Attachment.latest_attach(attachments, filename_utf8)
796 end
798 end
797 if atta && atta.readable? && atta.visible?
799 if atta && atta.readable? && atta.visible?
798 return atta
800 return atta
799 else
801 else
800 return nil
802 return nil
801 end
803 end
802 end
804 end
803 end
805 end
804 end
806 end
805 end
807 end
806 end
808 end
General Comments 0
You need to be logged in to leave comments. Login now