##// END OF EJS Templates
Use 1.8.7 compatible @Pathname#to_s@ alias for @Pathname#to_path@....
Etienne Massip -
r9786:e2851a8b2a66
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

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