##// END OF EJS Templates
Rails4: replace ActionView::Helpers::TextHelper#truncate by String#truncate at lib/redmine/export/pdf.rb...
Toshi MARUYAMA -
r12557:fd6fea57136b
parent child
Show More
@@ -1,809 +1,807
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2014 Jean-Philippe Lang
4 # Copyright (C) 2006-2014 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.visible_custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
259 cv = issue.visible_custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
260 show_value(cv, false)
260 show_value(cv, false)
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, 0, 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, 0, 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 #
513 #
514 # parameter "col_id_width" is not used. it is kept for compatibility.
514 # parameter "col_id_width" is not used. it is kept for compatibility.
515 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
515 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
516 col_id_width, col_widths)
516 col_id_width, col_widths)
517 col_x = top_x
517 col_x = top_x
518 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
518 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
519 col_widths.each do |width|
519 col_widths.each do |width|
520 col_x += width
520 col_x += width
521 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
521 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
522 end
522 end
523 pdf.Line(top_x, top_y, top_x, lower_y) # left border
523 pdf.Line(top_x, top_y, top_x, lower_y) # left border
524 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
524 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
525 end
525 end
526
526
527 # Returns a PDF string of a single issue
527 # Returns a PDF string of a single issue
528 def issue_to_pdf(issue, assoc={})
528 def issue_to_pdf(issue, assoc={})
529 pdf = ITCPDF.new(current_language)
529 pdf = ITCPDF.new(current_language)
530 pdf.SetTitle("#{issue.project} - #{issue.tracker} ##{issue.id}")
530 pdf.SetTitle("#{issue.project} - #{issue.tracker} ##{issue.id}")
531 pdf.alias_nb_pages
531 pdf.alias_nb_pages
532 pdf.footer_date = format_date(Date.today)
532 pdf.footer_date = format_date(Date.today)
533 pdf.AddPage
533 pdf.AddPage
534 pdf.SetFontStyle('B',11)
534 pdf.SetFontStyle('B',11)
535 buf = "#{issue.project} - #{issue.tracker} ##{issue.id}"
535 buf = "#{issue.project} - #{issue.tracker} ##{issue.id}"
536 pdf.RDMMultiCell(190, 5, buf)
536 pdf.RDMMultiCell(190, 5, buf)
537 pdf.SetFontStyle('',8)
537 pdf.SetFontStyle('',8)
538 base_x = pdf.GetX
538 base_x = pdf.GetX
539 i = 1
539 i = 1
540 issue.ancestors.visible.each do |ancestor|
540 issue.ancestors.visible.each do |ancestor|
541 pdf.SetX(base_x + i)
541 pdf.SetX(base_x + i)
542 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
542 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
543 pdf.RDMMultiCell(190 - i, 5, buf)
543 pdf.RDMMultiCell(190 - i, 5, buf)
544 i += 1 if i < 35
544 i += 1 if i < 35
545 end
545 end
546 pdf.SetFontStyle('B',11)
546 pdf.SetFontStyle('B',11)
547 pdf.RDMMultiCell(190 - i, 5, issue.subject.to_s)
547 pdf.RDMMultiCell(190 - i, 5, issue.subject.to_s)
548 pdf.SetFontStyle('',8)
548 pdf.SetFontStyle('',8)
549 pdf.RDMMultiCell(190, 5, "#{format_time(issue.created_on)} - #{issue.author}")
549 pdf.RDMMultiCell(190, 5, "#{format_time(issue.created_on)} - #{issue.author}")
550 pdf.Ln
550 pdf.Ln
551
551
552 left = []
552 left = []
553 left << [l(:field_status), issue.status]
553 left << [l(:field_status), issue.status]
554 left << [l(:field_priority), issue.priority]
554 left << [l(:field_priority), issue.priority]
555 left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')
555 left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')
556 left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')
556 left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')
557 left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')
557 left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')
558
558
559 right = []
559 right = []
560 right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')
560 right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')
561 right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')
561 right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')
562 right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio')
562 right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio')
563 right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')
563 right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')
564 right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)
564 right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)
565
565
566 rows = left.size > right.size ? left.size : right.size
566 rows = left.size > right.size ? left.size : right.size
567 while left.size < rows
567 while left.size < rows
568 left << nil
568 left << nil
569 end
569 end
570 while right.size < rows
570 while right.size < rows
571 right << nil
571 right << nil
572 end
572 end
573
573
574 half = (issue.visible_custom_field_values.size / 2.0).ceil
574 half = (issue.visible_custom_field_values.size / 2.0).ceil
575 issue.visible_custom_field_values.each_with_index do |custom_value, i|
575 issue.visible_custom_field_values.each_with_index do |custom_value, i|
576 (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value, false)]
576 (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value, false)]
577 end
577 end
578
578
579 rows = left.size > right.size ? left.size : right.size
579 rows = left.size > right.size ? left.size : right.size
580 rows.times do |i|
580 rows.times do |i|
581 item = left[i]
581 item = left[i]
582 pdf.SetFontStyle('B',9)
582 pdf.SetFontStyle('B',9)
583 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
583 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
584 pdf.SetFontStyle('',9)
584 pdf.SetFontStyle('',9)
585 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
585 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
586
586
587 item = right[i]
587 item = right[i]
588 pdf.SetFontStyle('B',9)
588 pdf.SetFontStyle('B',9)
589 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
589 pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
590 pdf.SetFontStyle('',9)
590 pdf.SetFontStyle('',9)
591 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
591 pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
592 pdf.Ln
592 pdf.Ln
593 end
593 end
594
594
595 pdf.SetFontStyle('B',9)
595 pdf.SetFontStyle('B',9)
596 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
596 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
597 pdf.SetFontStyle('',9)
597 pdf.SetFontStyle('',9)
598
598
599 # Set resize image scale
599 # Set resize image scale
600 pdf.SetImageScale(1.6)
600 pdf.SetImageScale(1.6)
601 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
601 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
602 issue.description.to_s, issue.attachments, "LRB")
602 issue.description.to_s, issue.attachments, "LRB")
603
603
604 unless issue.leaf?
604 unless issue.leaf?
605 # for CJK
605 # for CJK
606 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
606 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
607
608 pdf.SetFontStyle('B',9)
607 pdf.SetFontStyle('B',9)
609 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
608 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
610 pdf.Ln
609 pdf.Ln
611 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
610 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
612 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
611 buf = "#{child.tracker} # #{child.id}: #{child.subject}".
613 :length => truncate_length)
612 truncate(truncate_length)
614 level = 10 if level >= 10
613 level = 10 if level >= 10
615 pdf.SetFontStyle('',8)
614 pdf.SetFontStyle('',8)
616 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
615 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
617 pdf.SetFontStyle('B',8)
616 pdf.SetFontStyle('B',8)
618 pdf.RDMCell(20,5, child.status.to_s, "R")
617 pdf.RDMCell(20,5, child.status.to_s, "R")
619 pdf.Ln
618 pdf.Ln
620 end
619 end
621 end
620 end
622
621
623 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
622 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
624 unless relations.empty?
623 unless relations.empty?
625 # for CJK
624 # for CJK
626 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
625 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
627
628 pdf.SetFontStyle('B',9)
626 pdf.SetFontStyle('B',9)
629 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
627 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
630 pdf.Ln
628 pdf.Ln
631 relations.each do |relation|
629 relations.each do |relation|
632 buf = ""
630 buf = ""
633 buf += "#{l(relation.label_for(issue))} "
631 buf += "#{l(relation.label_for(issue))} "
634 if relation.delay && relation.delay != 0
632 if relation.delay && relation.delay != 0
635 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
633 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
636 end
634 end
637 if Setting.cross_project_issue_relations?
635 if Setting.cross_project_issue_relations?
638 buf += "#{relation.other_issue(issue).project} - "
636 buf += "#{relation.other_issue(issue).project} - "
639 end
637 end
640 buf += "#{relation.other_issue(issue).tracker}" +
638 buf += "#{relation.other_issue(issue).tracker}" +
641 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
639 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
642 buf = truncate(buf, :length => truncate_length)
640 buf = buf.truncate(truncate_length)
643 pdf.SetFontStyle('', 8)
641 pdf.SetFontStyle('', 8)
644 pdf.RDMCell(35+155-60, 5, buf, "L")
642 pdf.RDMCell(35+155-60, 5, buf, "L")
645 pdf.SetFontStyle('B',8)
643 pdf.SetFontStyle('B',8)
646 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
644 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
647 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
645 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
648 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
646 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
649 pdf.Ln
647 pdf.Ln
650 end
648 end
651 end
649 end
652 pdf.RDMCell(190,5, "", "T")
650 pdf.RDMCell(190,5, "", "T")
653 pdf.Ln
651 pdf.Ln
654
652
655 if issue.changesets.any? &&
653 if issue.changesets.any? &&
656 User.current.allowed_to?(:view_changesets, issue.project)
654 User.current.allowed_to?(:view_changesets, issue.project)
657 pdf.SetFontStyle('B',9)
655 pdf.SetFontStyle('B',9)
658 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
656 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
659 pdf.Ln
657 pdf.Ln
660 for changeset in issue.changesets
658 for changeset in issue.changesets
661 pdf.SetFontStyle('B',8)
659 pdf.SetFontStyle('B',8)
662 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
660 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
663 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
661 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
664 pdf.RDMCell(190, 5, csstr)
662 pdf.RDMCell(190, 5, csstr)
665 pdf.Ln
663 pdf.Ln
666 unless changeset.comments.blank?
664 unless changeset.comments.blank?
667 pdf.SetFontStyle('',8)
665 pdf.SetFontStyle('',8)
668 pdf.RDMwriteHTMLCell(190,5,0,0,
666 pdf.RDMwriteHTMLCell(190,5,0,0,
669 changeset.comments.to_s, issue.attachments, "")
667 changeset.comments.to_s, issue.attachments, "")
670 end
668 end
671 pdf.Ln
669 pdf.Ln
672 end
670 end
673 end
671 end
674
672
675 if assoc[:journals].present?
673 if assoc[:journals].present?
676 pdf.SetFontStyle('B',9)
674 pdf.SetFontStyle('B',9)
677 pdf.RDMCell(190,5, l(:label_history), "B")
675 pdf.RDMCell(190,5, l(:label_history), "B")
678 pdf.Ln
676 pdf.Ln
679 assoc[:journals].each do |journal|
677 assoc[:journals].each do |journal|
680 pdf.SetFontStyle('B',8)
678 pdf.SetFontStyle('B',8)
681 title = "##{journal.indice} - #{format_time(journal.created_on)} - #{journal.user}"
679 title = "##{journal.indice} - #{format_time(journal.created_on)} - #{journal.user}"
682 title << " (#{l(:field_private_notes)})" if journal.private_notes?
680 title << " (#{l(:field_private_notes)})" if journal.private_notes?
683 pdf.RDMCell(190,5, title)
681 pdf.RDMCell(190,5, title)
684 pdf.Ln
682 pdf.Ln
685 pdf.SetFontStyle('I',8)
683 pdf.SetFontStyle('I',8)
686 details_to_strings(journal.visible_details, true).each do |string|
684 details_to_strings(journal.visible_details, true).each do |string|
687 pdf.RDMMultiCell(190,5, "- " + string)
685 pdf.RDMMultiCell(190,5, "- " + string)
688 end
686 end
689 if journal.notes?
687 if journal.notes?
690 pdf.Ln unless journal.details.empty?
688 pdf.Ln unless journal.details.empty?
691 pdf.SetFontStyle('',8)
689 pdf.SetFontStyle('',8)
692 pdf.RDMwriteHTMLCell(190,5,0,0,
690 pdf.RDMwriteHTMLCell(190,5,0,0,
693 journal.notes.to_s, issue.attachments, "")
691 journal.notes.to_s, issue.attachments, "")
694 end
692 end
695 pdf.Ln
693 pdf.Ln
696 end
694 end
697 end
695 end
698
696
699 if issue.attachments.any?
697 if issue.attachments.any?
700 pdf.SetFontStyle('B',9)
698 pdf.SetFontStyle('B',9)
701 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
699 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
702 pdf.Ln
700 pdf.Ln
703 for attachment in issue.attachments
701 for attachment in issue.attachments
704 pdf.SetFontStyle('',8)
702 pdf.SetFontStyle('',8)
705 pdf.RDMCell(80,5, attachment.filename)
703 pdf.RDMCell(80,5, attachment.filename)
706 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
704 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
707 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
705 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
708 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
706 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
709 pdf.Ln
707 pdf.Ln
710 end
708 end
711 end
709 end
712 pdf.Output
710 pdf.Output
713 end
711 end
714
712
715 # Returns a PDF string of a set of wiki pages
713 # Returns a PDF string of a set of wiki pages
716 def wiki_pages_to_pdf(pages, project)
714 def wiki_pages_to_pdf(pages, project)
717 pdf = ITCPDF.new(current_language)
715 pdf = ITCPDF.new(current_language)
718 pdf.SetTitle(project.name)
716 pdf.SetTitle(project.name)
719 pdf.alias_nb_pages
717 pdf.alias_nb_pages
720 pdf.footer_date = format_date(Date.today)
718 pdf.footer_date = format_date(Date.today)
721 pdf.AddPage
719 pdf.AddPage
722 pdf.SetFontStyle('B',11)
720 pdf.SetFontStyle('B',11)
723 pdf.RDMMultiCell(190,5, project.name)
721 pdf.RDMMultiCell(190,5, project.name)
724 pdf.Ln
722 pdf.Ln
725 # Set resize image scale
723 # Set resize image scale
726 pdf.SetImageScale(1.6)
724 pdf.SetImageScale(1.6)
727 pdf.SetFontStyle('',9)
725 pdf.SetFontStyle('',9)
728 write_page_hierarchy(pdf, pages.group_by(&:parent_id))
726 write_page_hierarchy(pdf, pages.group_by(&:parent_id))
729 pdf.Output
727 pdf.Output
730 end
728 end
731
729
732 # Returns a PDF string of a single wiki page
730 # Returns a PDF string of a single wiki page
733 def wiki_page_to_pdf(page, project)
731 def wiki_page_to_pdf(page, project)
734 pdf = ITCPDF.new(current_language)
732 pdf = ITCPDF.new(current_language)
735 pdf.SetTitle("#{project} - #{page.title}")
733 pdf.SetTitle("#{project} - #{page.title}")
736 pdf.alias_nb_pages
734 pdf.alias_nb_pages
737 pdf.footer_date = format_date(Date.today)
735 pdf.footer_date = format_date(Date.today)
738 pdf.AddPage
736 pdf.AddPage
739 pdf.SetFontStyle('B',11)
737 pdf.SetFontStyle('B',11)
740 pdf.RDMMultiCell(190,5,
738 pdf.RDMMultiCell(190,5,
741 "#{project} - #{page.title} - # #{page.content.version}")
739 "#{project} - #{page.title} - # #{page.content.version}")
742 pdf.Ln
740 pdf.Ln
743 # Set resize image scale
741 # Set resize image scale
744 pdf.SetImageScale(1.6)
742 pdf.SetImageScale(1.6)
745 pdf.SetFontStyle('',9)
743 pdf.SetFontStyle('',9)
746 write_wiki_page(pdf, page)
744 write_wiki_page(pdf, page)
747 pdf.Output
745 pdf.Output
748 end
746 end
749
747
750 def write_page_hierarchy(pdf, pages, node=nil, level=0)
748 def write_page_hierarchy(pdf, pages, node=nil, level=0)
751 if pages[node]
749 if pages[node]
752 pages[node].each do |page|
750 pages[node].each do |page|
753 if @new_page
751 if @new_page
754 pdf.AddPage
752 pdf.AddPage
755 else
753 else
756 @new_page = true
754 @new_page = true
757 end
755 end
758 pdf.Bookmark page.title, level
756 pdf.Bookmark page.title, level
759 write_wiki_page(pdf, page)
757 write_wiki_page(pdf, page)
760 write_page_hierarchy(pdf, pages, page.id, level + 1) if pages[page.id]
758 write_page_hierarchy(pdf, pages, page.id, level + 1) if pages[page.id]
761 end
759 end
762 end
760 end
763 end
761 end
764
762
765 def write_wiki_page(pdf, page)
763 def write_wiki_page(pdf, page)
766 pdf.RDMwriteHTMLCell(190,5,0,0,
764 pdf.RDMwriteHTMLCell(190,5,0,0,
767 page.content.text.to_s, page.attachments, 0)
765 page.content.text.to_s, page.attachments, 0)
768 if page.attachments.any?
766 if page.attachments.any?
769 pdf.Ln
767 pdf.Ln
770 pdf.SetFontStyle('B',9)
768 pdf.SetFontStyle('B',9)
771 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
769 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
772 pdf.Ln
770 pdf.Ln
773 for attachment in page.attachments
771 for attachment in page.attachments
774 pdf.SetFontStyle('',8)
772 pdf.SetFontStyle('',8)
775 pdf.RDMCell(80,5, attachment.filename)
773 pdf.RDMCell(80,5, attachment.filename)
776 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
774 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
777 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
775 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
778 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
776 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
779 pdf.Ln
777 pdf.Ln
780 end
778 end
781 end
779 end
782 end
780 end
783
781
784 class RDMPdfEncoding
782 class RDMPdfEncoding
785 def self.rdm_from_utf8(txt, encoding)
783 def self.rdm_from_utf8(txt, encoding)
786 txt ||= ''
784 txt ||= ''
787 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
785 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
788 if txt.respond_to?(:force_encoding)
786 if txt.respond_to?(:force_encoding)
789 txt.force_encoding('ASCII-8BIT')
787 txt.force_encoding('ASCII-8BIT')
790 end
788 end
791 txt
789 txt
792 end
790 end
793
791
794 def self.attach(attachments, filename, encoding)
792 def self.attach(attachments, filename, encoding)
795 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
793 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
796 atta = nil
794 atta = nil
797 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
795 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
798 atta = Attachment.latest_attach(attachments, filename_utf8)
796 atta = Attachment.latest_attach(attachments, filename_utf8)
799 end
797 end
800 if atta && atta.readable? && atta.visible?
798 if atta && atta.readable? && atta.visible?
801 return atta
799 return atta
802 else
800 else
803 return nil
801 return nil
804 end
802 end
805 end
803 end
806 end
804 end
807 end
805 end
808 end
806 end
809 end
807 end
General Comments 0
You need to be logged in to leave comments. Login now