Revision 912:5e80956cc792 lib/redmine/export

View differences:

lib/redmine/export/pdf.rb
18 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 19

  
20 20
require 'iconv'
21
require 'rfpdf/fpdf'
22 21
require 'fpdf/chinese'
23 22
require 'fpdf/japanese'
24 23
require 'fpdf/korean'
24
require 'core/rmagick'
25 25

  
26 26
module Redmine
27 27
  module Export
28 28
    module PDF
29 29
      include ActionView::Helpers::TextHelper
30 30
      include ActionView::Helpers::NumberHelper
31
      include IssuesHelper
31 32

  
32 33
      class ITCPDF < TCPDF
33 34
        include Redmine::I18n
34 35
        attr_accessor :footer_date
35 36

  
36 37
        def initialize(lang)
38
          @@k_path_cache = Rails.root.join('tmp', 'pdf')
39
          FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
37 40
          set_language_if_valid lang
38 41
          pdf_encoding = l(:general_pdf_encoding).upcase
39
          if RUBY_VERSION < '1.9'
40
            @ic = Iconv.new(pdf_encoding, 'UTF-8')
41
          end
42 42
          super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
43 43
          case current_language.to_s.downcase
44 44
          when 'vi'
......
104 104
        end
105 105

  
106 106
        def fix_text_encoding(txt)
107
          RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
107
          RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
108 108
        end
109 109

  
110 110
        def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
......
115 115
          MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
116 116
        end
117 117

  
118
        def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
119
          @attachments = attachments
120
          writeHTMLCell(w, h, x, y,
121
            fix_text_encoding(
122
              Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),
123
            border, ln, fill)
124
        end
125

  
126
        def getImageFilename(attrname)
127
          # attrname: general_pdf_encoding string file/uri name
128
          atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
129
          if atta
130
            return atta.diskfile
131
          else
132
            return nil
133
          end
134
        end
135

  
118 136
        def Footer
119 137
          SetFont(@font_for_footer, 'I', 8)
120 138
          SetY(-15)
......
150 168
        col_width = []
151 169
        unless query.columns.empty?
152 170
          col_width = query.columns.collect do |c|
153
            (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format)))? 4.0 : 1.0
171
            (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
172
              ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
154 173
          end
155 174
          ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
156 175
          col_width = col_width.collect {|w| w * ratio}
......
182 201
        pdf.SetFontStyle('',8)
183 202
        pdf.SetFillColor(255, 255, 255)
184 203
        previous_group = false
185
        issues.each do |issue|
204
        issue_list(issues) do |issue, level|
186 205
          if query.grouped? &&
187 206
               (group = query.group_by_column.value(issue)) != previous_group
188 207
            pdf.SetFontStyle('B',9)
......
199 218
              show_value(cv)
200 219
            else
201 220
              value = issue.send(column.name)
221
              if column.name == :subject
222
                value = "  " * level + value
223
              end
202 224
              if value.is_a?(Date)
203 225
                format_date(value)
204 226
              elsif value.is_a?(Time)
......
278 300
        pdf.footer_date = format_date(Date.today)
279 301
        pdf.AddPage
280 302
        pdf.SetFontStyle('B',11)
281
        pdf.RDMMultiCell(190,5,
282
             "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
303
        buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
304
        pdf.RDMMultiCell(190, 5, buf)
305
        pdf.Ln
306
        pdf.SetFontStyle('',8)
307
        base_x = pdf.GetX
308
        i = 1
309
        issue.ancestors.each do |ancestor|
310
          pdf.SetX(base_x + i)
311
          buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
312
          pdf.RDMMultiCell(190 - i, 5, buf)
313
          i += 1 if i < 35
314
        end
283 315
        pdf.Ln
284 316

  
285 317
        pdf.SetFontStyle('B',9)
......
340 372
        pdf.SetFontStyle('B',9)
341 373
        pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
342 374
        pdf.SetFontStyle('',9)
343
        pdf.RDMMultiCell(35+155, 5, issue.description.to_s, "LRB")
375

  
376
        # Set resize image scale
377
        pdf.SetImageScale(1.6)
378
        pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
379
              issue.description.to_s, issue.attachments, "LRB")
380

  
381
        unless issue.leaf?
382
          # for CJK
383
          truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
384
  
385
          pdf.SetFontStyle('B',9)
386
          pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
387
          pdf.Ln
388
          issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
389
            buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
390
                           :length => truncate_length)
391
            level = 10 if level >= 10
392
            pdf.SetFontStyle('',8)
393
            pdf.RDMCell(35+135,5, (level >=1 ? "  " * level : "") + buf, "L")
394
            pdf.SetFontStyle('B',8)
395
            pdf.RDMCell(20,5, child.status.to_s, "R")
396
            pdf.Ln
397
          end
398
        end
399

  
400
        relations = issue.relations.select { |r| r.other_issue(issue).visible? }
401
        unless relations.empty?
402
          # for CJK
403
          truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
404
  
405
          pdf.SetFontStyle('B',9)
406
          pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
407
          pdf.Ln
408
          relations.each do |relation|
409
            buf = ""
410
            buf += "#{l(relation.label_for(issue))} "
411
            if relation.delay && relation.delay != 0
412
              buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
413
            end
414
            if Setting.cross_project_issue_relations?
415
              buf += "#{relation.other_issue(issue).project} - "
416
            end
417
            buf += "#{relation.other_issue(issue).tracker}" +
418
                   " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
419
            buf = truncate(buf, :length => truncate_length)
420
            pdf.SetFontStyle('', 8)
421
            pdf.RDMCell(35+155-60, 5, buf, "L")
422
            pdf.SetFontStyle('B',8)
423
            pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
424
            pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
425
            pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
426
            pdf.Ln
427
          end
428
        end
429
        pdf.RDMCell(190,5, "", "T")
344 430
        pdf.Ln
345 431

  
346 432
        if issue.changesets.any? &&
......
356 442
            pdf.Ln
357 443
            unless changeset.comments.blank?
358 444
              pdf.SetFontStyle('',8)
359
              pdf.RDMMultiCell(190,5, changeset.comments.to_s)
445
              pdf.RDMwriteHTMLCell(190,5,0,0,
446
                    changeset.comments.to_s, issue.attachments, "")
360 447
            end
361 448
            pdf.Ln
362 449
          end
......
365 452
        pdf.SetFontStyle('B',9)
366 453
        pdf.RDMCell(190,5, l(:label_history), "B")
367 454
        pdf.Ln
455
        indice = 0
368 456
        for journal in issue.journals.find(
369 457
                          :all, :include => [:user, :details],
370 458
                          :order => "#{Journal.table_name}.created_on ASC")
459
          indice = indice + 1
371 460
          pdf.SetFontStyle('B',8)
372 461
          pdf.RDMCell(190,5,
373
             format_time(journal.created_on) + " - " + journal.user.name)
462
             "#" + indice.to_s +
463
             " - " + format_time(journal.created_on) +
464
             " - " + journal.user.name)
374 465
          pdf.Ln
375 466
          pdf.SetFontStyle('I',8)
376 467
          for detail in journal.details
......
379 470
          if journal.notes?
380 471
            pdf.Ln unless journal.details.empty?
381 472
            pdf.SetFontStyle('',8)
382
            pdf.RDMMultiCell(190,5, journal.notes.to_s)
473
            pdf.RDMwriteHTMLCell(190,5,0,0,
474
                  journal.notes.to_s, issue.attachments, "")
383 475
          end
384 476
          pdf.Ln
385 477
        end
......
400 492
        pdf.Output
401 493
      end
402 494

  
495
      # Returns a PDF string of a single wiki page
496
      def wiki_to_pdf(page, project)
497
        pdf = ITCPDF.new(current_language)
498
        pdf.SetTitle("#{project} - #{page.title}")
499
        pdf.alias_nb_pages
500
        pdf.footer_date = format_date(Date.today)
501
        pdf.AddPage
502
        pdf.SetFontStyle('B',11)
503
        pdf.RDMMultiCell(190,5,
504
             "#{project} - #{page.title} - # #{page.content.version}")
505
        pdf.Ln
506
        # Set resize image scale
507
        pdf.SetImageScale(1.6)
508
        pdf.SetFontStyle('',9)
509
        pdf.RDMwriteHTMLCell(190,5,0,0,
510
              page.content.text.to_s, page.attachments, "TLRB")
511
        if page.attachments.any?
512
          pdf.Ln
513
          pdf.SetFontStyle('B',9)
514
          pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
515
          pdf.Ln
516
          for attachment in page.attachments
517
            pdf.SetFontStyle('',8)
518
            pdf.RDMCell(80,5, attachment.filename)
519
            pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
520
            pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
521
            pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
522
            pdf.Ln
523
          end
524
        end
525
        pdf.Output
526
      end
527

  
403 528
      class RDMPdfEncoding
404
        include Redmine::I18n
405
        def self.rdm_pdf_iconv(ic, txt)
529
        def self.rdm_from_utf8(txt, encoding)
406 530
          txt ||= ''
531
          txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
407 532
          if txt.respond_to?(:force_encoding)
408
            txt.force_encoding('UTF-8')
409
            if l(:general_pdf_encoding).upcase != 'UTF-8'
410
              txt = txt.encode(l(:general_pdf_encoding), :invalid => :replace,
411
                               :undef => :replace, :replace => '?')
412
            else
413
              txt = Redmine::CodesetUtil.replace_invalid_utf8(txt)
414
            end
415 533
            txt.force_encoding('ASCII-8BIT')
416
          elsif RUBY_PLATFORM == 'java'
417
            begin
418
              ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
419
              txt = ic.iconv(txt)
420
            rescue
421
              txt = txt.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
422
            end
423
          else
424
            ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
425
            txtar = ""
426
            begin
427
              txtar += ic.iconv(txt)
428
            rescue Iconv::IllegalSequence
429
              txtar += $!.success
430
              txt = '?' + $!.failed[1,$!.failed.length]
431
              retry
432
            rescue
433
              txtar += $!.success
434
            end
435
            txt = txtar
436 534
          end
437 535
          txt
438 536
        end
537

  
538
        def self.attach(attachments, filename, encoding)
539
          filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
540
          atta = nil
541
          if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
542
            atta = Attachment.latest_attach(attachments, filename_utf8)
543
          end
544
          if atta && atta.readable? && atta.visible?
545
            return atta
546
          else
547
            return nil
548
          end
549
        end
439 550
      end
440 551
    end
441 552
  end

Also available in: Unified diff