| 335 | 
  335 | 
  
          end 
   | 
  | 336 | 
  336 | 
  
    
   | 
  | 337 | 
  337 | 
  
          # Generates a gantt image 
   | 
  | 338 | 
   | 
  
          # Only defined if RMagick is avalaible 
   | 
   | 
  338 | 
  
          # Only defined if MiniMagick is avalaible 
   | 
  | 339 | 
  339 | 
  
          def to_image(format='PNG') 
   | 
  | 340 | 
  340 | 
  
            date_to = (@date_from >> @months) - 1 
   | 
  | 341 | 
  341 | 
  
            show_weeks = @zoom > 1 
   | 
  | ... | ... |  | 
  | 348 | 
  348 | 
  
            g_height = 20 * number_of_rows + 30 
   | 
  | 349 | 
  349 | 
  
            headers_height = (show_weeks ? 2 * header_height : header_height) 
   | 
  | 350 | 
  350 | 
  
            height = g_height + headers_height 
   | 
  | 351 | 
   | 
  
            imgl = Magick::ImageList.new 
   | 
  | 352 | 
   | 
  
            imgl.new_image(subject_width + g_width + 1, height) 
   | 
  | 353 | 
   | 
  
            gc = Magick::Draw.new 
   | 
  | 354 | 
   | 
  
            gc.font = Redmine::Configuration['rmagick_font_path'] || "" 
   | 
  | 355 | 
   | 
  
            # Subjects 
   | 
  | 356 | 
   | 
  
            gc.stroke('transparent')
   | 
  | 357 | 
   | 
  
            subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image) 
   | 
  | 358 | 
   | 
  
            # Months headers 
   | 
  | 359 | 
   | 
  
            month_f = @date_from 
   | 
  | 360 | 
   | 
  
            left = subject_width 
   | 
  | 361 | 
   | 
  
            @months.times do 
   | 
  | 362 | 
   | 
  
              width = ((month_f >> 1) - month_f) * zoom 
   | 
  | 363 | 
   | 
  
              gc.fill('white')
   | 
  | 364 | 
   | 
  
              gc.stroke('grey')
   | 
  | 365 | 
   | 
  
              gc.stroke_width(1) 
   | 
  | 366 | 
   | 
  
              gc.rectangle(left, 0, left + width, height) 
   | 
  | 367 | 
   | 
  
              gc.fill('black')
   | 
   | 
  351 | 
  
            Rails.logger.warn('rmagick_font_path option is deprecated. use minimagick_font_path instead.') \
   | 
   | 
  352 | 
  
              unless Redmine::Configuration['rmagick_font_path'].nil? 
   | 
   | 
  353 | 
  
            font_path = Redmine::Configuration['minimagick_font_path'].presence || Redmine::Configuration['rmagick_font_path'].presence 
   | 
   | 
  354 | 
  
            img = MiniMagick::Image.create(".#{format}", false)
   | 
   | 
  355 | 
  
            MiniMagick::Tool::Convert.new do |gc| 
   | 
   | 
  356 | 
  
              gc.size('%dx%d' % [subject_width + g_width + 1, height])
   | 
   | 
  357 | 
  
              gc.xc('white')
   | 
   | 
  358 | 
  
              gc.font(font_path) if font_path.present? 
   | 
   | 
  359 | 
  
              # Subjects 
   | 
  | 368 | 
  360 | 
  
              gc.stroke('transparent')
   | 
  | 369 | 
   | 
  
              gc.stroke_width(1) 
   | 
  | 370 | 
   | 
  
              gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
   | 
  | 371 | 
   | 
  
              left = left + width 
   | 
  | 372 | 
   | 
  
              month_f = month_f >> 1 
   | 
  | 373 | 
   | 
  
            end 
   | 
  | 374 | 
   | 
  
            # Weeks headers 
   | 
  | 375 | 
   | 
  
            if show_weeks 
   | 
   | 
  361 | 
  
              subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image) 
   | 
   | 
  362 | 
  
              # Months headers 
   | 
   | 
  363 | 
  
              month_f = @date_from 
   | 
  | 376 | 
  364 | 
  
              left = subject_width 
   | 
  | 377 | 
   | 
  
              height = header_height 
   | 
  | 378 | 
   | 
  
              if @date_from.cwday == 1 
   | 
  | 379 | 
   | 
  
                # date_from is monday 
   | 
  | 380 | 
   | 
  
                week_f = date_from 
   | 
  | 381 | 
   | 
  
              else 
   | 
  | 382 | 
   | 
  
                # find next monday after date_from 
   | 
  | 383 | 
   | 
  
                week_f = @date_from + (7 - @date_from.cwday + 1) 
   | 
  | 384 | 
   | 
  
                width = (7 - @date_from.cwday + 1) * zoom 
   | 
  | 385 | 
   | 
  
                gc.fill('white')
   | 
  | 386 | 
   | 
  
                gc.stroke('grey')
   | 
  | 387 | 
   | 
  
                gc.stroke_width(1) 
   | 
  | 388 | 
   | 
  
                gc.rectangle(left, header_height, left + width, 2 * header_height + g_height - 1) 
   | 
  | 389 | 
   | 
  
                left = left + width 
   | 
  | 390 | 
   | 
  
              end 
   | 
  | 391 | 
   | 
  
              while week_f <= date_to 
   | 
  | 392 | 
   | 
  
                width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom 
   | 
   | 
  365 | 
  
              @months.times do 
   | 
   | 
  366 | 
  
                width = ((month_f >> 1) - month_f) * zoom 
   | 
  | 393 | 
  367 | 
  
                gc.fill('white')
   | 
  | 394 | 
  368 | 
  
                gc.stroke('grey')
   | 
  | 395 | 
   | 
  
                gc.stroke_width(1) 
   | 
  | 396 | 
   | 
  
                gc.rectangle(left.round, header_height, left.round + width, 2 * header_height + g_height - 1) 
   | 
   | 
  369 | 
  
                gc.strokewidth(1) 
   | 
   | 
  370 | 
  
                gc.draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  371 | 
  
                  left, 0, left + width, height 
   | 
   | 
  372 | 
  
                ]) 
   | 
  | 397 | 
  373 | 
  
                gc.fill('black')
   | 
  | 398 | 
  374 | 
  
                gc.stroke('transparent')
   | 
  | 399 | 
   | 
  
                gc.stroke_width(1) 
   | 
  | 400 | 
   | 
  
                gc.text(left.round + 2, header_height + 14, week_f.cweek.to_s) 
   | 
   | 
  375 | 
  
                gc.strokewidth(1) 
   | 
   | 
  376 | 
  
                gc.draw('text %d,%d %s' % [
   | 
   | 
  377 | 
  
                  left.round + 8, 14, Redmine::Utils::Shell::shell_quote("#{month_f.year}-#{month_f.month}")
   | 
   | 
  378 | 
  
                ]) 
   | 
  | 401 | 
  379 | 
  
                left = left + width 
   | 
  | 402 | 
   | 
  
                week_f = week_f + 7 
   | 
   | 
  380 | 
  
                month_f = month_f >> 1 
   | 
   | 
  381 | 
  
              end 
   | 
   | 
  382 | 
  
              # Weeks headers 
   | 
   | 
  383 | 
  
              if show_weeks 
   | 
   | 
  384 | 
  
                left = subject_width 
   | 
   | 
  385 | 
  
                height = header_height 
   | 
   | 
  386 | 
  
                if @date_from.cwday == 1 
   | 
   | 
  387 | 
  
                  # date_from is monday 
   | 
   | 
  388 | 
  
                  week_f = date_from 
   | 
   | 
  389 | 
  
                else 
   | 
   | 
  390 | 
  
                  # find next monday after date_from 
   | 
   | 
  391 | 
  
                  week_f = @date_from + (7 - @date_from.cwday + 1) 
   | 
   | 
  392 | 
  
                  width = (7 - @date_from.cwday + 1) * zoom 
   | 
   | 
  393 | 
  
                  gc.fill('white')
   | 
   | 
  394 | 
  
                  gc.stroke('grey')
   | 
   | 
  395 | 
  
                  gc.strokewidth(1) 
   | 
   | 
  396 | 
  
                  gc.draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  397 | 
  
                    left, header_height, left + width, 2 * header_height + g_height - 1 
   | 
   | 
  398 | 
  
                  ]) 
   | 
   | 
  399 | 
  
                  left = left + width 
   | 
   | 
  400 | 
  
                end 
   | 
   | 
  401 | 
  
                while week_f <= date_to 
   | 
   | 
  402 | 
  
                  width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom 
   | 
   | 
  403 | 
  
                  gc.fill('white')
   | 
   | 
  404 | 
  
                  gc.stroke('grey')
   | 
   | 
  405 | 
  
                  gc.strokewidth(1) 
   | 
   | 
  406 | 
  
                  gc.draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  407 | 
  
                    left.round, header_height, left.round + width, 2 * header_height + g_height - 1 
   | 
   | 
  408 | 
  
                  ]) 
   | 
   | 
  409 | 
  
                  gc.fill('black')
   | 
   | 
  410 | 
  
                  gc.stroke('transparent')
   | 
   | 
  411 | 
  
                  gc.strokewidth(1) 
   | 
   | 
  412 | 
  
                  gc.draw('text %d,%d %s' % [
   | 
   | 
  413 | 
  
                    left.round + 2, header_height + 14, Redmine::Utils::Shell::shell_quote(week_f.cweek.to_s) 
   | 
   | 
  414 | 
  
                  ]) 
   | 
   | 
  415 | 
  
                  left = left + width 
   | 
   | 
  416 | 
  
                  week_f = week_f + 7 
   | 
   | 
  417 | 
  
                end 
   | 
  | 403 | 
  418 | 
  
              end 
   | 
  | 404 | 
   | 
  
            end 
   | 
  | 405 | 
   | 
  
            # Days details (week-end in grey) 
   | 
  | 406 | 
   | 
  
            if show_days 
   | 
  | 407 | 
   | 
  
              left = subject_width 
   | 
  | 408 | 
   | 
  
              height = g_height + header_height - 1 
   | 
  | 409 | 
   | 
  
              wday = @date_from.cwday 
   | 
  | 410 | 
   | 
  
              (date_to - @date_from + 1).to_i.times do 
   | 
  | 411 | 
   | 
  
                width =  zoom 
   | 
  | 412 | 
   | 
  
                gc.fill(non_working_week_days.include?(wday) ? '#eee' : 'white') 
   | 
  | 413 | 
   | 
  
                gc.stroke('#ddd')
   | 
  | 414 | 
   | 
  
                gc.stroke_width(1) 
   | 
  | 415 | 
   | 
  
                gc.rectangle(left, 2 * header_height, left + width, 2 * header_height + g_height - 1) 
   | 
  | 416 | 
   | 
  
                left = left + width 
   | 
  | 417 | 
   | 
  
                wday = wday + 1 
   | 
  | 418 | 
   | 
  
                wday = 1 if wday > 7 
   | 
   | 
  419 | 
  
              # Days details (week-end in grey) 
   | 
   | 
  420 | 
  
              if show_days 
   | 
   | 
  421 | 
  
                left = subject_width 
   | 
   | 
  422 | 
  
                height = g_height + header_height - 1 
   | 
   | 
  423 | 
  
                wday = @date_from.cwday 
   | 
   | 
  424 | 
  
                (date_to - @date_from + 1).to_i.times do 
   | 
   | 
  425 | 
  
                  width =  zoom 
   | 
   | 
  426 | 
  
                  gc.fill(non_working_week_days.include?(wday) ? '#eee' : 'white') 
   | 
   | 
  427 | 
  
                  gc.stroke('#ddd')
   | 
   | 
  428 | 
  
                  gc.strokewidth(1) 
   | 
   | 
  429 | 
  
                  gc.draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  430 | 
  
                    left, 2 * header_height, left + width, 2 * header_height + g_height - 1 
   | 
   | 
  431 | 
  
                  ]) 
   | 
   | 
  432 | 
  
                  left = left + width 
   | 
   | 
  433 | 
  
                  wday = wday + 1 
   | 
   | 
  434 | 
  
                  wday = 1 if wday > 7 
   | 
   | 
  435 | 
  
                end 
   | 
  | 419 | 
  436 | 
  
              end 
   | 
  | 420 | 
   | 
  
            end 
   | 
  | 421 | 
   | 
  
            # border 
   | 
  | 422 | 
   | 
  
            gc.fill('transparent')
   | 
  | 423 | 
   | 
  
            gc.stroke('grey')
   | 
  | 424 | 
   | 
  
            gc.stroke_width(1) 
   | 
  | 425 | 
   | 
  
            gc.rectangle(0, 0, subject_width + g_width, headers_height) 
   | 
  | 426 | 
   | 
  
            gc.stroke('black')
   | 
  | 427 | 
   | 
  
            gc.rectangle(0, 0, subject_width + g_width, g_height + headers_height - 1) 
   | 
  | 428 | 
   | 
  
            # content 
   | 
  | 429 | 
   | 
  
            top = headers_height + 20 
   | 
  | 430 | 
   | 
  
            gc.stroke('transparent')
   | 
  | 431 | 
   | 
  
            lines(:image => gc, :top => top, :zoom => zoom, 
   | 
  | 432 | 
   | 
  
                  :subject_width => subject_width, :format => :image) 
   | 
  | 433 | 
   | 
  
            # today red line 
   | 
  | 434 | 
   | 
  
            if User.current.today >= @date_from and User.current.today <= date_to 
   | 
  | 435 | 
   | 
  
              gc.stroke('red')
   | 
  | 436 | 
   | 
  
              x = (User.current.today - @date_from + 1) * zoom + subject_width 
   | 
  | 437 | 
   | 
  
              gc.line(x, headers_height, x, headers_height + g_height - 1) 
   | 
  | 438 | 
   | 
  
            end 
   | 
  | 439 | 
   | 
  
            gc.draw(imgl) 
   | 
  | 440 | 
   | 
  
            imgl.format = format 
   | 
  | 441 | 
   | 
  
            imgl.to_blob 
   | 
  | 442 | 
   | 
  
          end if Object.const_defined?(:Magick) 
   | 
   | 
  437 | 
  
              # border 
   | 
   | 
  438 | 
  
              gc.fill('transparent')
   | 
   | 
  439 | 
  
              gc.stroke('grey')
   | 
   | 
  440 | 
  
              gc.strokewidth(1) 
   | 
   | 
  441 | 
  
              gc.draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  442 | 
  
                0, 0, subject_width + g_width, headers_height 
   | 
   | 
  443 | 
  
              ]) 
   | 
   | 
  444 | 
  
              gc.stroke('black')
   | 
   | 
  445 | 
  
              gc.draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  446 | 
  
                0, 0, subject_width + g_width, g_height + headers_height - 1 
   | 
   | 
  447 | 
  
              ]) 
   | 
   | 
  448 | 
  
              # content 
   | 
   | 
  449 | 
  
              top = headers_height + 20 
   | 
   | 
  450 | 
  
              gc.stroke('transparent')
   | 
   | 
  451 | 
  
              lines(:image => gc, :top => top, :zoom => zoom, 
   | 
   | 
  452 | 
  
                    :subject_width => subject_width, :format => :image) 
   | 
   | 
  453 | 
  
              # today red line 
   | 
   | 
  454 | 
  
              if User.current.today >= @date_from and User.current.today <= date_to 
   | 
   | 
  455 | 
  
                gc.stroke('red')
   | 
   | 
  456 | 
  
                x = (User.current.today - @date_from + 1) * zoom + subject_width 
   | 
   | 
  457 | 
  
                gc.draw('line %g,%g %g,%g' % [
   | 
   | 
  458 | 
  
                  x, headers_height, x, headers_height + g_height - 1 
   | 
   | 
  459 | 
  
                ]) 
   | 
   | 
  460 | 
  
              end 
   | 
   | 
  461 | 
  
              gc << img.path 
   | 
   | 
  462 | 
  
            end 
   | 
   | 
  463 | 
  
            img.to_blob 
   | 
   | 
  464 | 
  
          ensure 
   | 
   | 
  465 | 
  
            img.destroy! if img 
   | 
   | 
  466 | 
  
          end if Object.const_defined?(:MiniMagick) 
   | 
  | 443 | 
  467 | 
  
    
   | 
  | 444 | 
  468 | 
  
          def to_pdf 
   | 
  | 445 | 
  469 | 
  
            pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language) 
   | 
  | ... | ... |  | 
  | 735 | 
  759 | 
  
          def image_subject(params, subject, options={})
   | 
  | 736 | 
  760 | 
  
            params[:image].fill('black')
   | 
  | 737 | 
  761 | 
  
            params[:image].stroke('transparent')
   | 
  | 738 | 
   | 
  
            params[:image].stroke_width(1) 
   | 
  | 739 | 
   | 
  
            params[:image].text(params[:indent], params[:top] + 2, subject) 
   | 
   | 
  762 | 
  
            params[:image].strokewidth(1) 
   | 
   | 
  763 | 
  
            params[:image].draw('text %d,%d %s' % [
   | 
   | 
  764 | 
  
              params[:indent], params[:top] + 2, Redmine::Utils::Shell::shell_quote(subject) 
   | 
   | 
  765 | 
  
            ]) 
   | 
  | 740 | 
  766 | 
  
          end 
   | 
  | 741 | 
  767 | 
  
    
   | 
  | 742 | 
  768 | 
  
          def issue_relations(issue) 
   | 
  | ... | ... |  | 
  | 912 | 
  938 | 
  
            # Renders the task bar, with progress and late 
   | 
  | 913 | 
  939 | 
  
            if coords[:bar_start] && coords[:bar_end] 
   | 
  | 914 | 
  940 | 
  
              params[:image].fill('#aaa')
   | 
  | 915 | 
   | 
  
              params[:image].rectangle(params[:subject_width] + coords[:bar_start], 
   | 
  | 916 | 
   | 
  
                                       params[:top], 
   | 
  | 917 | 
   | 
  
                                       params[:subject_width] + coords[:bar_end], 
   | 
  | 918 | 
   | 
  
                                       params[:top] - height) 
   | 
   | 
  941 | 
  
              params[:image].draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  942 | 
  
                params[:subject_width] + coords[:bar_start], 
   | 
   | 
  943 | 
  
                params[:top], 
   | 
   | 
  944 | 
  
                params[:subject_width] + coords[:bar_end], 
   | 
   | 
  945 | 
  
                params[:top] - height 
   | 
   | 
  946 | 
  
              ]) 
   | 
  | 919 | 
  947 | 
  
              if coords[:bar_late_end] 
   | 
  | 920 | 
  948 | 
  
                params[:image].fill('#f66')
   | 
  | 921 | 
   | 
  
                params[:image].rectangle(params[:subject_width] + coords[:bar_start], 
   | 
  | 922 | 
   | 
  
                                         params[:top], 
   | 
  | 923 | 
   | 
  
                                         params[:subject_width] + coords[:bar_late_end], 
   | 
  | 924 | 
   | 
  
                                         params[:top] - height) 
   | 
   | 
  949 | 
  
                params[:image].draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  950 | 
  
                  params[:subject_width] + coords[:bar_start], 
   | 
   | 
  951 | 
  
                  params[:top], 
   | 
   | 
  952 | 
  
                  params[:subject_width] + coords[:bar_late_end], 
   | 
   | 
  953 | 
  
                  params[:top] - height 
   | 
   | 
  954 | 
  
                ]) 
   | 
  | 925 | 
  955 | 
  
              end 
   | 
  | 926 | 
  956 | 
  
              if coords[:bar_progress_end] 
   | 
  | 927 | 
  957 | 
  
                params[:image].fill('#00c600')
   | 
  | 928 | 
   | 
  
                params[:image].rectangle(params[:subject_width] + coords[:bar_start], 
   | 
  | 929 | 
   | 
  
                                         params[:top], 
   | 
  | 930 | 
   | 
  
                                         params[:subject_width] + coords[:bar_progress_end], 
   | 
  | 931 | 
   | 
  
                                         params[:top] - height) 
   | 
   | 
  958 | 
  
                params[:image].draw('rectangle %d,%d %d,%d' % [
   | 
   | 
  959 | 
  
                  params[:subject_width] + coords[:bar_start], 
   | 
   | 
  960 | 
  
                  params[:top], 
   | 
   | 
  961 | 
  
                  params[:subject_width] + coords[:bar_progress_end], 
   | 
   | 
  962 | 
  
                  params[:top] - height 
   | 
   | 
  963 | 
  
                ]) 
   | 
  | 932 | 
  964 | 
  
              end 
   | 
  | 933 | 
  965 | 
  
            end 
   | 
  | 934 | 
  966 | 
  
            # Renders the markers 
   | 
  | ... | ... |  | 
  | 937 | 
  969 | 
  
                x = params[:subject_width] + coords[:start] 
   | 
  | 938 | 
  970 | 
  
                y = params[:top] - height / 2 
   | 
  | 939 | 
  971 | 
  
                params[:image].fill('blue')
   | 
  | 940 | 
   | 
  
                params[:image].polygon(x - 4, y, x, y - 4, x + 4, y, x, y + 4) 
   | 
   | 
  972 | 
  
                params[:image].draw('polygon %d,%d %d,%d %d,%d %d,%d' % [
   | 
   | 
  973 | 
  
                  x - 4, y, 
   | 
   | 
  974 | 
  
                  x, y - 4, 
   | 
   | 
  975 | 
  
                  x + 4, y, 
   | 
   | 
  976 | 
  
                  x, y + 4 
   | 
   | 
  977 | 
  
                ]) 
   | 
  | 941 | 
  978 | 
  
              end 
   | 
  | 942 | 
  979 | 
  
              if coords[:end] 
   | 
  | 943 | 
  980 | 
  
                x = params[:subject_width] + coords[:end] + params[:zoom] 
   | 
  | 944 | 
  981 | 
  
                y = params[:top] - height / 2 
   | 
  | 945 | 
  982 | 
  
                params[:image].fill('blue')
   | 
  | 946 | 
   | 
  
                params[:image].polygon(x - 4, y, x, y - 4, x + 4, y, x, y + 4) 
   | 
   | 
  983 | 
  
                params[:image].draw('polygon %d,%d %d,%d %d,%d %d,%d' % [
   | 
   | 
  984 | 
  
                  x - 4, y, 
   | 
   | 
  985 | 
  
                  x, y - 4, 
   | 
   | 
  986 | 
  
                  x + 4, y, 
   | 
   | 
  987 | 
  
                  x, y + 4 
   | 
   | 
  988 | 
  
                ]) 
   | 
  | 947 | 
  989 | 
  
              end 
   | 
  | 948 | 
  990 | 
  
            end 
   | 
  | 949 | 
  991 | 
  
            # Renders the label on the right 
   | 
  | 950 | 
  992 | 
  
            if label 
   | 
  | 951 | 
  993 | 
  
              params[:image].fill('black')
   | 
  | 952 | 
   | 
  
              params[:image].text(params[:subject_width] + (coords[:bar_end] || 0) + 5, 
   | 
  | 953 | 
   | 
  
                                  params[:top] + 1, 
   | 
  | 954 | 
   | 
  
                                  label) 
   | 
   | 
  994 | 
  
              params[:image].draw('text %d,%d %s' % [
   | 
   | 
  995 | 
  
                params[:subject_width] + (coords[:bar_end] || 0) + 5, params[:top] + 1, Redmine::Utils::Shell::shell_quote(label) 
   | 
   | 
  996 | 
  
              ]) 
   | 
  | 955 | 
  997 | 
  
            end 
   | 
  | 956 | 
  998 | 
  
          end 
   | 
  | 957 | 
  999 | 
  
        end 
   |