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