Index: app/controllers/issues_controller.rb
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+># Redmine - project management software\n# Copyright (C) 2006-2012  Jean-Philippe Lang\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\nclass IssuesController < ApplicationController\n  menu_item :new_issue, :only => [:new, :create]\n  default_search_scope :issues\n\n  before_filter :find_issue, :only => [:show, :edit, :update]\n  before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]\n  before_filter :find_project, :only => [:new, :create]\n  before_filter :authorize, :except => [:index, :set_private, :unset_private]\n  before_filter :find_optional_project, :only => [:index]\n  before_filter :check_for_default_issue_status, :only => [:new, :create]\n  before_filter :build_new_issue_from_params, :only => [:new, :create]\n  accept_rss_auth :index, :show\n  accept_api_auth :index, :show, :create, :update, :destroy\n\n  rescue_from Query::StatementInvalid, :with => :query_statement_invalid\n\n  helper :journals\n  helper :projects\n  include ProjectsHelper\n  helper :custom_fields\n  include CustomFieldsHelper\n  helper :issue_relations\n  include IssueRelationsHelper\n  helper :watchers\n  include WatchersHelper\n  helper :attachments\n  include AttachmentsHelper\n  helper :queries\n  include QueriesHelper\n  helper :repositories\n  include RepositoriesHelper\n  helper :sort\n  include SortHelper\n  include IssuesHelper\n  helper :timelog\n  include Redmine::Export::PDF\n\n  def index\n    retrieve_query\n    sort_init(@query.sort_criteria.empty? ? [%w(id desc)] : @query.sort_criteria)\n    sort_update(@query.sortable_columns)\n\n    if @query.valid?\n      case params[:format]\n      when 'csv', 'pdf'\n        @limit = Setting.issues_export_limit.to_i\n      when 'atom'\n        @limit = Setting.feeds_limit.to_i\n      when 'xml', 'json'\n        @offset, @limit = api_offset_and_limit\n      else\n        @limit = per_page_option\n      end\n\n      @issue_count = @query.issue_count\n      @issue_pages = Paginator.new self, @issue_count, @limit, params['page']\n      @offset ||= @issue_pages.current.offset\n      @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],\n                              :order => sort_clause,\n                              :offset => @offset,\n                              :limit => @limit)\n      @issue_count_by_group = @query.issue_count_by_group\n\n      respond_to do |format|\n        format.html { render :template => 'issues/index', :layout => !request.xhr? }\n        format.api  {\n          Issue.load_relations(@issues) if include_in_api_response?('relations')\n        }\n        format.atom { render_feed(@issues, :title => \"#{@project || Setting.app_title}: #{l(:label_issue_plural)}\") }\n        format.csv  { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }\n        format.pdf  { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }\n      end\n    else\n      respond_to do |format|\n        format.html { render(:template => 'issues/index', :layout => !request.xhr?) }\n        format.any(:atom, :csv, :pdf) { render(:nothing => true) }\n        format.api { render_validation_errors(@query) }\n      end\n    end\n  rescue ActiveRecord::RecordNotFound\n    render_404\n  end\n\n  def show\n    @journals = @issue.journals.includes([:user, :details]).order(\"`journals`.`created_on` ASC\") if User.current.allowed_to?(:view_private_messages, @issue.project)\n    @journals = @issue.journals.includes([:user, :details]).order(\"`journals`.`created_on` ASC\").where(\"`journals`.`private` IS NULL\") unless User.current.allowed_to?(:view_private_messages, @issue.project)\n    @journals.each_with_index {|j,i| j.indice = i+1; r = IncomingEmail.where(:journal_id => j.id); j.email ||= r[0]['id'] if r.present?}\n    @journals.reverse! if User.current.wants_comments_in_reverse_order?\n\n    @changesets = @issue.changesets.visible.all\n    @changesets.reverse! if User.current.wants_comments_in_reverse_order?\n\n    @email = IncomingEmail.where(:journal_id => nil, :issue_id => @issue.id)\n    @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }\n    @allowed_statuses = @issue.new_statuses_allowed_to(User.current)\n    @edit_allowed = User.current.allowed_to?(:edit_issues, @project)\n    @priorities = IssuePriority.active\n    @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)\n    respond_to do |format|\n      format.html {\n        retrieve_previous_and_next_issue_ids\n        render :template => 'issues/show'\n      }\n      format.api\n      format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }\n      format.pdf  { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => \"#{@project.identifier}-#{@issue.id}.pdf\") }\n    end\n  end\n\n  # Add a new issue\n  # The new issue will be created from an existing one if copy_from parameter is given\n  def new\n    respond_to do |format|\n      format.html { render :action => 'new', :layout => !request.xhr? }\n      format.js {\n        render(:update) { |page|\n          if params[:project_change]\n            page.replace_html 'all_attributes', :partial => 'form'\n          else\n            page.replace_html 'attributes', :partial => 'attributes'\n          end\n          m = User.current.allowed_to?(:log_time, @issue.project) ? 'show' : 'hide'\n          page << \"if ($('log_time')) {Element.#{m}('log_time');}\"\n        }\n      }\n    end\n  end\n\n  def create\n    call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })\n    @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))\n    if @issue.save\n      call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})\n      respond_to do |format|\n        format.html {\n          render_attachment_warning_if_needed(@issue)\n          flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to(\"##{@issue.id}\", issue_path(@issue)))\n          redirect_to(params[:continue] ?  { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :\n                      { :action => 'show', :id => @issue })\n        }\n        format.api  { render :action => 'show', :status => :created, :location => issue_url(@issue) }\n      end\n    else\n      respond_to do |format|\n        format.html { render :action => 'new' }\n        format.api  { render_validation_errors(@issue) }\n      end\n    end\n  end\n\n  def edit\n    return unless update_issue_from_params\n\n    respond_to do |format|\n      format.html { }\n      format.xml  { }\n    end\n  end\n\n  def update\n    return unless update_issue_from_params\n    @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))\n    saved = false\n    begin\n      saved = @issue.save_issue_with_child_records(params, @time_entry)\n    rescue ActiveRecord::StaleObjectError\n      @conflict = true\n      if params[:last_journal_id]\n        if params[:last_journal_id].present?\n          last_journal_id = params[:last_journal_id].to_i\n          @conflict_journals = @issue.journals.all(:conditions => [\"#{Journal.table_name}.id > ?\", last_journal_id])\n        else\n          @conflict_journals = @issue.journals.all\n        end\n      end\n    end\n\n    if saved\n      render_attachment_warning_if_needed(@issue)\n      flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?\n\n      respond_to do |format|\n        format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }\n        format.api  { head :ok }\n        format.js do\n          render :update do |page|\n            page.replace_html 'issue-form', :partial => 'issues/edit', :locals => {:issue => @issue}\n          end\n        end\n      end\n    else\n      respond_to do |format|\n        format.html { render :action => 'edit' }\n        format.api  { render_validation_errors(@issue) }\n      end\n    end\n  end\n\n  # Bulk edit/copy a set of issues\n  def bulk_edit\n    @issues.sort!\n    @copy = params[:copy].present?\n    @notes = params[:notes]\n\n    if User.current.allowed_to?(:move_issues, @projects)\n      @allowed_projects = Issue.allowed_target_projects_on_move\n      if params[:issue]\n        @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}\n        if @target_project\n          target_projects = [@target_project]\n        end\n      end\n    end\n    target_projects ||= @projects\n\n    if @copy\n      @available_statuses = [IssueStatus.default]\n    else\n      @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)\n    end\n    @custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)\n    @assignables = target_projects.map(&:assignable_users).reduce(:&)\n    @trackers = target_projects.map(&:trackers).reduce(:&)\n    @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)\n    @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)\n    if @copy\n      @attachments_present = @issues.detect {|i| i.attachments.any?}.present?\n    end\n\n    @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)\n    render :layout => false if request.xhr?\n  end\n\n  def bulk_update\n    @issues.sort!\n    @copy = params[:copy].present?\n    attributes = parse_params_for_bulk_issue_attributes(params)\n\n    unsaved_issue_ids = []\n    moved_issues = []\n    @issues.each do |issue|\n      issue.reload\n      if @copy\n        issue = issue.copy({}, :attachments => params[:copy_attachments].present?)\n      end\n      journal = issue.init_journal(User.current, params[:notes])\n      issue.safe_attributes = attributes\n      call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })\n      if issue.save\n        moved_issues << issue\n      else\n        # Keep unsaved issue ids to display them in flash error\n        unsaved_issue_ids << issue.id\n      end\n    end\n    set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)\n\n    if params[:follow]\n      if @issues.size == 1 && moved_issues.size == 1\n        redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first\n      elsif moved_issues.map(&:project).uniq.size == 1\n        redirect_to :controller => 'issues', :action => 'index', :project_id => moved_issues.map(&:project).first\n      end\n    else\n      redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})\n    end\n  end\n\n  def destroy\n    @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f\n    if @hours > 0\n      case params[:todo]\n      when 'destroy'\n        # nothing to do\n      when 'nullify'\n        TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])\n      when 'reassign'\n        reassign_to = @project.issues.find_by_id(params[:reassign_to_id])\n        if reassign_to.nil?\n          flash.now[:error] = l(:error_issue_not_found_in_project)\n          return\n        else\n          TimeEntry.update_all(\"issue_id = #{reassign_to.id}\", ['issue_id IN (?)', @issues])\n        end\n      else\n        # display the destroy form if it's a user request\n        return unless api_request?\n      end\n    end\n    @issues.each do |issue|\n      begin\n        issue.reload.destroy\n      rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists\n        # nothing to do, issue was already deleted (eg. by a parent)\n      end\n    end\n    respond_to do |format|\n      format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }\n      format.api  { head :ok }\n    end\n  end\n\n  def set_private\n    @issue = Issue.find(params[:id])\n    @issue.set_private\n    redirect_to issue_path(@issue)\n  end\n\n  def unset_private\n    @issue = Issue.find(params[:id])\n    @issue.unset_private\n    redirect_to issue_path(@issue)\n  end\n\nprivate\n  def find_issue\n    # Issue.visible.find(...) can not be used to redirect user to the login form\n    # if the issue actually exists but requires authentication\n    @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])\n    unless @issue.visible?\n      deny_access\n      return\n    end\n    @project = @issue.project\n  rescue ActiveRecord::RecordNotFound\n    render_404\n  end\n\n  def find_project\n    project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])\n    @project = Project.find(project_id)\n  rescue ActiveRecord::RecordNotFound\n    render_404\n  end\n\n  def retrieve_previous_and_next_issue_ids\n    retrieve_query_from_session\n    if @query\n      sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)\n      sort_update(@query.sortable_columns, 'issues_index_sort')\n      limit = 500\n      issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])\n      if (idx = issue_ids.index(@issue.id)) && idx < limit\n        if issue_ids.size < 500\n          @issue_position = idx + 1\n          @issue_count = issue_ids.size\n        end\n        @prev_issue_id = issue_ids[idx - 1] if idx > 0\n        @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)\n      end\n    end\n  end\n\n  # Used by #edit and #update to set some common instance variables\n  # from the params\n  # TODO: Refactor, not everything in here is needed by #edit\n  def update_issue_from_params\n    @edit_allowed = User.current.allowed_to?(:edit_issues, @project)\n    @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)\n    @time_entry.attributes = params[:time_entry]\n\n    @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)\n    @private_message = params[:private_message]\n    @issue.init_journal(User.current, @notes, @private_message)\n\n    issue_attributes = params[:issue]\n    if issue_attributes && params[:conflict_resolution]\n      case params[:conflict_resolution]\n      when 'overwrite'\n        issue_attributes = issue_attributes.dup\n        issue_attributes.delete(:lock_version)\n      when 'add_notes'\n        issue_attributes = {}\n      when 'cancel'\n        redirect_to issue_path(@issue)\n        return false\n      end\n    end\n    @issue.safe_attributes = issue_attributes\n    @priorities = IssuePriority.active\n    @allowed_statuses = @issue.new_statuses_allowed_to(User.current)\n    true\n  end\n\n  # TODO: Refactor, lots of extra code in here\n  # TODO: Changing tracker on an existing issue should not trigger this\n  def build_new_issue_from_params\n    if params[:id].blank?\n      @issue = Issue.new\n      if params[:copy_from]\n        begin\n          @copy_from = Issue.visible.find(params[:copy_from])\n          @copy_attachments = params[:copy_attachments].present? || request.get?\n          @issue.copy_from(@copy_from, :attachments => @copy_attachments)\n        rescue ActiveRecord::RecordNotFound\n          render_404\n          return\n        end\n      end\n      @issue.project = @project\n    else\n      @issue = @project.issues.visible.find(params[:id])\n    end\n\n    @issue.project = @project\n    @issue.author = User.current\n    # Tracker must be set before custom field values\n    @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)\n    if @issue.tracker.nil?\n      render_error l(:error_no_tracker_in_project)\n      return false\n    end\n    @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?\n    @issue.safe_attributes = params[:issue]\n\n    @priorities = IssuePriority.active\n    @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)\n    @available_watchers = (@issue.project.users.sort + @issue.watcher_users).uniq\n  end\n\n  def check_for_default_issue_status\n    if IssueStatus.default.nil?\n      render_error l(:error_no_default_issue_status)\n      return false\n    end\n  end\n\n  def parse_params_for_bulk_issue_attributes(params)\n    attributes = (params[:issue] || {}).reject {|k,v| v.blank?}\n    attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}\n    if custom = attributes[:custom_field_values]\n      custom.reject! {|k,v| v.blank?}\n      custom.keys.each do |k|\n        if custom[k].is_a?(Array)\n          custom[k] << '' if custom[k].delete('__none__')\n        else\n          custom[k] = '' if custom[k] == '__none__'\n        end\n      end\n    end\n    attributes\n  end\nend\n
===================================================================
--- app/controllers/issues_controller.rb	(revision 23ba9754d4c5240c1c3f2600a96100e91d4c571f)
+++ app/controllers/issues_controller.rb	(revision )
@@ -99,8 +99,7 @@
   end
 
   def show
-    @journals = @issue.journals.includes([:user, :details]).order("`journals`.`created_on` ASC") if User.current.allowed_to?(:view_private_messages, @issue.project)
-    @journals = @issue.journals.includes([:user, :details]).order("`journals`.`created_on` ASC").where("`journals`.`private` IS NULL") unless User.current.allowed_to?(:view_private_messages, @issue.project)
+    @journals = @issue.journals.includes([:user, :details]).order("`journals`.`created_on` ASC")
     @journals.each_with_index {|j,i| j.indice = i+1; r = IncomingEmail.where(:journal_id => j.id); j.email ||= r[0]['id'] if r.present?}
     @journals.reverse! if User.current.wants_comments_in_reverse_order?
 
Index: app/views/journals/index.builder
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+>xml.instruct!\nxml.feed \"xmlns\" => \"http://www.w3.org/2005/Atom\" do\n  xml.title   @title\n  xml.link    \"rel\" => \"self\", \"href\" => url_for(:format => 'atom', :key => User.current.rss_key, :only_path => false)\n  xml.link    \"rel\" => \"alternate\", \"href\" => home_url(:only_path => false)\n  xml.id      url_for(:controller => 'welcome', :only_path => false)\n  xml.updated((@journals.first ? @journals.first.event_datetime : Time.now).xmlschema)\n  xml.author  { xml.name \"#{Setting.app_title}\" }\n  @journals.each do |change|\n    issue = change.issue\n    xml.entry do\n      xml.title   \"#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}\"\n      xml.link    \"rel\" => \"alternate\", \"href\" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)\n      xml.id      url_for(:controller => 'issues' , :action => 'show', :id => issue, :journal_id => change, :only_path => false)\n      xml.updated change.created_on.xmlschema\n      xml.author do\n        xml.name change.user.name\n        xml.email(change.user.mail) if change.user.is_a?(User) && !change.user.mail.blank? && !change.user.pref.hide_mail\n      end\n      xml.content \"type\" => \"html\" do\n        xml.text! '<ul>'\n        details_to_strings(change.details, false).each do |string|\n          xml.text! '<li>' + string + '</li>'\n        end\n        xml.text! '</ul>'\n        xml.text! textilizable(change, :notes, :only_path => false) unless change.notes.blank?\n      end\n    end\n  end\nend\n
===================================================================
--- app/views/journals/index.builder	(revision 23ba9754d4c5240c1c3f2600a96100e91d4c571f)
+++ app/views/journals/index.builder	(revision )
@@ -6,12 +6,20 @@
   xml.id      url_for(:controller => 'welcome', :only_path => false)
   xml.updated((@journals.first ? @journals.first.event_datetime : Time.now).xmlschema)
   xml.author  { xml.name "#{Setting.app_title}" }
+  view_private = User.current.allowed_to?(:view_private_messages, @project)
+  @skipped = 0
   @journals.each do |change|
     issue = change.issue
+    if change.private
+      unless view_private
+        @skipped += 1
+        next
+      end
+    end
     xml.entry do
       xml.title   "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
       xml.link    "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
-      xml.id      url_for(:controller => 'issues' , :action => 'show', :id => issue, :journal_id => change, :only_path => false)
+      xml.id      url_for(:controller => 'issues' , :action => 'show', :id => issue, :journal_id => change.indice - @skipped, :only_path => false)
       xml.updated change.created_on.xmlschema
       xml.author do
         xml.name change.user.name
Index: lib/redmine/export/pdf.rb
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+># encoding: utf-8\n#\n# Redmine - project management software\n# Copyright (C) 2006-2012  Jean-Philippe Lang\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\nrequire 'iconv'\nrequire 'tcpdf'\nrequire 'fpdf/chinese'\nrequire 'fpdf/japanese'\nrequire 'fpdf/korean'\n\nmodule Redmine\n  module Export\n    module PDF\n      include ActionView::Helpers::TextHelper\n      include ActionView::Helpers::NumberHelper\n      include IssuesHelper\n\n      class ITCPDF < TCPDF\n        include Redmine::I18n\n        attr_accessor :footer_date\n\n        def initialize(lang)\n          @@k_path_cache = Rails.root.join('tmp', 'pdf')\n          FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)\n          set_language_if_valid lang\n          pdf_encoding = l(:general_pdf_encoding).upcase\n          super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)\n          case current_language.to_s.downcase\n          when 'vi'\n            @font_for_content = 'DejaVuSans'\n            @font_for_footer  = 'DejaVuSans'\n          else\n            case pdf_encoding\n            when 'UTF-8'\n              @font_for_content = 'FreeSans'\n              @font_for_footer  = 'FreeSans'\n            when 'CP949'\n              extend(PDF_Korean)\n              AddUHCFont()\n              @font_for_content = 'UHC'\n              @font_for_footer  = 'UHC'\n            when 'CP932', 'SJIS', 'SHIFT_JIS'\n              extend(PDF_Japanese)\n              AddSJISFont()\n              @font_for_content = 'SJIS'\n              @font_for_footer  = 'SJIS'\n            when 'GB18030'\n              extend(PDF_Chinese)\n              AddGBFont()\n              @font_for_content = 'GB'\n              @font_for_footer  = 'GB'\n            when 'BIG5'\n              extend(PDF_Chinese)\n              AddBig5Font()\n              @font_for_content = 'Big5'\n              @font_for_footer  = 'Big5'\n            else\n              @font_for_content = 'Arial'\n              @font_for_footer  = 'Helvetica'\n            end\n          end\n          SetCreator(Redmine::Info.app_name)\n          SetFont(@font_for_content)\n          @outlines = []\n          @outlineRoot = nil\n        end\n\n        def SetFontStyle(style, size)\n          SetFont(@font_for_content, style, size)\n        end\n\n        def SetTitle(txt)\n          txt = begin\n            utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)\n            hextxt = \"<FEFF\"  # FEFF is BOM\n            hextxt << utf16txt.unpack(\"C*\").map {|x| sprintf(\"%02X\",x) }.join\n            hextxt << \">\"\n          rescue\n            txt\n          end || ''\n          super(txt)\n        end\n\n        def textstring(s)\n          # Format a text string\n          if s =~ /^</  # This means the string is hex-dumped.\n            return s\n          else\n            return '('+escape(s)+')'\n          end\n        end\n\n        def fix_text_encoding(txt)\n          RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))\n        end\n\n        def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')\n          Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)\n        end\n\n        def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)\n          MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)\n        end\n\n        def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)\n          @attachments = attachments\n          writeHTMLCell(w, h, x, y,\n            fix_text_encoding(\n              Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),\n            border, ln, fill)\n        end\n\n        def getImageFilename(attrname)\n          # attrname: general_pdf_encoding string file/uri name\n          atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))\n          if atta\n            return atta.diskfile\n          else\n            return nil\n          end\n        end\n\n        def Footer\n          SetFont(@font_for_footer, 'I', 8)\n          SetY(-15)\n          SetX(15)\n          RDMCell(0, 5, @footer_date, 0, 0, 'L')\n          SetY(-15)\n          SetX(-30)\n          RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')\n        end\n\n        def Bookmark(txt, level=0, y=0)\n          if (y == -1)\n            y = GetY()\n          end\n          @outlines << {:t => txt, :l => level, :p => PageNo(), :y => (@h - y)*@k}\n        end\n\n        def bookmark_title(txt)\n          txt = begin\n            utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)\n            hextxt = \"<FEFF\"  # FEFF is BOM\n            hextxt << utf16txt.unpack(\"C*\").map {|x| sprintf(\"%02X\",x) }.join\n            hextxt << \">\"\n          rescue\n            txt\n          end || ''\n        end\n\n        def putbookmarks\n          nb=@outlines.size\n          return if (nb==0)\n          lru=[]\n          level=0\n          @outlines.each_with_index do |o, i|\n            if(o[:l]>0)\n              parent=lru[o[:l]-1]\n              #Set parent and last pointers\n              @outlines[i][:parent]=parent\n              @outlines[parent][:last]=i\n              if (o[:l]>level)\n                #Level increasing: set first pointer\n                @outlines[parent][:first]=i\n              end\n            else\n              @outlines[i][:parent]=nb\n            end\n            if (o[:l]<=level && i>0)\n              #Set prev and next pointers\n              prev=lru[o[:l]]\n              @outlines[prev][:next]=i\n              @outlines[i][:prev]=prev\n            end\n            lru[o[:l]]=i\n            level=o[:l]\n          end\n          #Outline items\n          n=self.n+1\n          @outlines.each_with_index do |o, i|\n            newobj()\n            out('<</Title '+bookmark_title(o[:t]))\n            out(\"/Parent #{n+o[:parent]} 0 R\")\n            if (o[:prev])\n              out(\"/Prev #{n+o[:prev]} 0 R\")\n            end\n            if (o[:next])\n              out(\"/Next #{n+o[:next]} 0 R\")\n            end\n            if (o[:first])\n              out(\"/First #{n+o[:first]} 0 R\")\n            end\n            if (o[:last])\n              out(\"/Last #{n+o[:last]} 0 R\")\n            end\n            out(\"/Dest [%d 0 R /XYZ 0 %.2f null]\" % [1+2*o[:p], o[:y]])\n            out('/Count 0>>')\n            out('endobj')\n          end\n          #Outline root\n          newobj()\n          @outlineRoot=self.n\n          out(\"<</Type /Outlines /First #{n} 0 R\");\n          out(\"/Last #{n+lru[0]} 0 R>>\");\n          out('endobj');\n        end\n\n        def putresources()\n          super\n          putbookmarks()\n        end\n\n        def putcatalog()\n          super\n          if(@outlines.size > 0)\n            out(\"/Outlines #{@outlineRoot} 0 R\");\n            out('/PageMode /UseOutlines');\n          end\n        end\n      end\n\n      # fetch row values\n      def fetch_row_values(issue, query, level)\n        query.columns.collect do |column|\n          s = if column.is_a?(QueryCustomFieldColumn)\n            cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}\n            show_value(cv)\n          else\n            value = issue.send(column.name)\n            if column.name == :subject\n              value = \"  \" * level + value\n            end\n            if value.is_a?(Date)\n              format_date(value)\n            elsif value.is_a?(Time)\n              format_time(value)\n            else\n              value\n            end\n          end\n          s.to_s\n        end\n      end\n\n      # calculate columns width\n      def calc_col_width(issues, query, table_width, pdf)\n        # calculate statistics\n        #  by captions\n        pdf.SetFontStyle('B',8)\n        col_padding = pdf.GetStringWidth('OO')\n        col_width_min = query.columns.map {|v| pdf.GetStringWidth(v.caption) + col_padding}\n        col_width_max = Array.new(col_width_min)\n        col_width_avg = Array.new(col_width_min)\n        word_width_max = query.columns.map {|c|\n          n = 10\n          c.caption.split.each {|w|\n            x = pdf.GetStringWidth(w) + col_padding\n            n = x if n < x\n          }\n          n\n        }\n\n        #  by properties of issues\n        pdf.SetFontStyle('',8)\n        col_padding = pdf.GetStringWidth('OO')\n        k = 1\n        issue_list(issues) {|issue, level|\n          k += 1\n          values = fetch_row_values(issue, query, level)\n          values.each_with_index {|v,i|\n            n = pdf.GetStringWidth(v) + col_padding\n            col_width_max[i] = n if col_width_max[i] < n\n            col_width_min[i] = n if col_width_min[i] > n\n            col_width_avg[i] += n\n            v.split.each {|w|\n              x = pdf.GetStringWidth(w) + col_padding\n              word_width_max[i] = x if word_width_max[i] < x\n            }\n          }\n        }\n        col_width_avg.map! {|x| x / k}\n\n        # calculate columns width\n        ratio = table_width / col_width_avg.inject(0) {|s,w| s += w}\n        col_width = col_width_avg.map {|w| w * ratio}\n\n        # correct max word width if too many columns\n        ratio = table_width / word_width_max.inject(0) {|s,w| s += w}\n        word_width_max.map! {|v| v * ratio} if ratio < 1\n\n        # correct and lock width of some columns\n        done = 1\n        col_fix = []\n        col_width.each_with_index do |w,i|\n          if w > col_width_max[i]\n            col_width[i] = col_width_max[i]\n            col_fix[i] = 1\n            done = 0\n          elsif w < word_width_max[i]\n            col_width[i] = word_width_max[i]\n            col_fix[i] = 1\n            done = 0\n          else\n            col_fix[i] = 0\n          end\n        end\n\n        # iterate while need to correct and lock coluns width\n        while done == 0\n          # calculate free & locked columns width\n          done = 1\n          fix_col_width = 0\n          free_col_width = 0\n          col_width.each_with_index do |w,i|\n            if col_fix[i] == 1\n              fix_col_width += w\n            else\n              free_col_width += w\n            end\n          end\n\n          # calculate column normalizing ratio\n          if free_col_width == 0\n            ratio = table_width / col_width.inject(0) {|s,w| s += w}\n          else\n            ratio = (table_width - fix_col_width) / free_col_width\n          end\n\n          # correct columns width\n          col_width.each_with_index do |w,i|\n            if col_fix[i] == 0\n              col_width[i] = w * ratio\n\n              # check if column width less then max word width\n              if col_width[i] < word_width_max[i]\n                col_width[i] = word_width_max[i]\n                col_fix[i] = 1\n                done = 0\n              elsif col_width[i] > col_width_max[i]\n                col_width[i] = col_width_max[i]\n                col_fix[i] = 1\n                done = 0\n              end\n            end\n          end\n        end\n        col_width\n      end\n\n      def render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)\n        # headers\n        pdf.SetFontStyle('B',8)\n        pdf.SetFillColor(230, 230, 230)\n\n        # render it background to find the max height used\n        base_x = pdf.GetX\n        base_y = pdf.GetY\n        max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)\n        pdf.Rect(base_x, base_y, table_width + col_id_width, max_height, 'FD');\n        pdf.SetXY(base_x, base_y);\n\n        # write the cells on page\n        pdf.RDMCell(col_id_width, row_height, \"#\", \"T\", 0, 'C', 1)\n        issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)\n        issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)\n        pdf.SetY(base_y + max_height);\n\n        # rows\n        pdf.SetFontStyle('',8)\n        pdf.SetFillColor(255, 255, 255)\n      end\n\n      # Returns a PDF string of a list of issues\n      def issues_to_pdf(issues, project, query)\n        pdf = ITCPDF.new(current_language)\n        title = query.new_record? ? l(:label_issue_plural) : query.name\n        title = \"#{project} - #{title}\" if project\n        pdf.SetTitle(title)\n        pdf.alias_nb_pages\n        pdf.footer_date = format_date(Date.today)\n        pdf.SetAutoPageBreak(false)\n        pdf.AddPage(\"L\")\n\n        # Landscape A4 = 210 x 297 mm\n        page_height   = 210\n        page_width    = 297\n        right_margin  = 10\n        bottom_margin = 20\n        col_id_width  = 10\n        row_height    = 4\n\n        # column widths\n        table_width = page_width - right_margin - 10  # fixed left margin\n        col_width = []\n        unless query.columns.empty?\n          col_width = calc_col_width(issues, query, table_width - col_id_width, pdf)\n          table_width = col_width.inject(0) {|s,v| s += v}\n        end\n\n        # title\n        pdf.SetFontStyle('B',11)\n        pdf.RDMCell(190,10, title)\n        pdf.Ln\n        render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)\n        previous_group = false\n        issue_list(issues) do |issue, level|\n          if query.grouped? &&\n               (group = query.group_by_column.value(issue)) != previous_group\n            pdf.SetFontStyle('B',10)\n            group_label = group.blank? ? 'None' : group.to_s\n            group_label << \" (#{query.issue_count_by_group[group]})\"\n            pdf.Bookmark group_label, 0, -1\n            pdf.RDMCell(table_width + col_id_width, row_height * 2, group_label, 1, 1, 'L')\n            pdf.SetFontStyle('',8)\n            previous_group = group\n          end\n\n          # fetch row values\n          col_values = fetch_row_values(issue, query, level)\n\n          # render it off-page to find the max height used\n          base_x = pdf.GetX\n          base_y = pdf.GetY\n          pdf.SetY(2 * page_height)\n          max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)\n          pdf.SetXY(base_x, base_y)\n\n          # make new page if it doesn't fit on the current one\n          space_left = page_height - base_y - bottom_margin\n          if max_height > space_left\n            pdf.AddPage(\"L\")\n            render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)\n            base_x = pdf.GetX\n            base_y = pdf.GetY\n          end\n\n          # write the cells on page\n          pdf.RDMCell(col_id_width, row_height, issue.id.to_s, \"T\", 0, 'C', 1)\n          issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)\n          issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)\n          pdf.SetY(base_y + max_height);\n        end\n\n        if issues.size == Setting.issues_export_limit.to_i\n          pdf.SetFontStyle('B',10)\n          pdf.RDMCell(0, row_height, '...')\n        end\n        pdf.Output\n      end\n\n      # Renders MultiCells and returns the maximum height used\n      def issues_to_pdf_write_cells(pdf, col_values, col_widths,\n                                    row_height, head=false)\n        base_y = pdf.GetY\n        max_height = row_height\n        col_values.each_with_index do |column, i|\n          col_x = pdf.GetX\n          if head == true\n            pdf.RDMMultiCell(col_widths[i], row_height, column.caption, \"T\", 'L', 1)\n          else\n            pdf.RDMMultiCell(col_widths[i], row_height, column, \"T\", 'L', 1)\n          end\n          max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height\n          pdf.SetXY(col_x + col_widths[i], base_y);\n        end\n        return max_height\n      end\n\n      # Draw lines to close the row (MultiCell border drawing in not uniform)\n      def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,\n                                     id_width, col_widths)\n        col_x = top_x + id_width\n        pdf.Line(col_x, top_y, col_x, lower_y)    # id right border\n        col_widths.each do |width|\n          col_x += width\n          pdf.Line(col_x, top_y, col_x, lower_y)  # columns right border\n        end\n        pdf.Line(top_x, top_y, top_x, lower_y)    # left border\n        pdf.Line(top_x, lower_y, col_x, lower_y)  # bottom border\n      end\n\n      # Returns a PDF string of a single issue\n      def issue_to_pdf(issue)\n        pdf = ITCPDF.new(current_language)\n        pdf.SetTitle(\"#{issue.project} - #{issue.tracker} ##{issue.id}\")\n        pdf.alias_nb_pages\n        pdf.footer_date = format_date(Date.today)\n        pdf.AddPage\n        pdf.SetFontStyle('B',11)\n        buf = \"#{issue.project} - #{issue.tracker} ##{issue.id}\"\n        pdf.RDMMultiCell(190, 5, buf)\n        pdf.SetFontStyle('',8)\n        base_x = pdf.GetX\n        i = 1\n        issue.ancestors.each do |ancestor|\n          pdf.SetX(base_x + i)\n          buf = \"#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}\"\n          pdf.RDMMultiCell(190 - i, 5, buf)\n          i += 1 if i < 35\n        end\n        pdf.SetFontStyle('B',11)\n        pdf.RDMMultiCell(190 - i, 5, issue.subject.to_s)\n        pdf.SetFontStyle('',8)\n        pdf.RDMMultiCell(190, 5, \"#{format_time(issue.created_on)} - #{issue.author}\")\n        pdf.Ln\n\n        left = []\n        left << [l(:field_status), issue.status]\n        left << [l(:field_priority), issue.priority]\n        left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')\n        left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')\n        left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')\n\n        right = []\n        right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')\n        right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')\n        right << [l(:field_done_ratio), \"#{issue.done_ratio}%\"] unless issue.disabled_core_fields.include?('done_ratio')\n        right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')\n        right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)\n\n        rows = left.size > right.size ? left.size : right.size\n        while left.size < rows\n          left << nil\n        end\n        while right.size < rows\n          right << nil\n        end\n\n        half = (issue.custom_field_values.size / 2.0).ceil\n        issue.custom_field_values.each_with_index do |custom_value, i|\n          (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)]\n        end\n\n        rows = left.size > right.size ? left.size : right.size\n        rows.times do |i|\n          item = left[i]\n          pdf.SetFontStyle('B',9)\n          pdf.RDMCell(35,5, item ? \"#{item.first}:\" : \"\", i == 0 ? \"LT\" : \"L\")\n          pdf.SetFontStyle('',9)\n          pdf.RDMCell(60,5, item ? item.last.to_s : \"\", i == 0 ? \"RT\" : \"R\")\n\n          item = right[i]\n          pdf.SetFontStyle('B',9)\n          pdf.RDMCell(35,5, item ? \"#{item.first}:\" : \"\", i == 0 ? \"LT\" : \"L\")\n          pdf.SetFontStyle('',9)\n          pdf.RDMCell(60,5, item ? item.last.to_s : \"\", i == 0 ? \"RT\" : \"R\")\n          pdf.Ln\n        end\n\n        pdf.SetFontStyle('B',9)\n        pdf.RDMCell(35+155, 5, l(:field_description), \"LRT\", 1)\n        pdf.SetFontStyle('',9)\n\n        # Set resize image scale\n        pdf.SetImageScale(1.6)\n        pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,\n              issue.description.to_s, issue.attachments, \"LRB\")\n\n        unless issue.leaf?\n          # for CJK\n          truncate_length = ( l(:general_pdf_encoding).upcase == \"UTF-8\" ? 90 : 65 )\n\n          pdf.SetFontStyle('B',9)\n          pdf.RDMCell(35+155,5, l(:label_subtask_plural) + \":\", \"LTR\")\n          pdf.Ln\n          issue_list(issue.descendants.sort_by(&:lft)) do |child, level|\n            buf = truncate(\"#{child.tracker} # #{child.id}: #{child.subject}\",\n                           :length => truncate_length)\n            level = 10 if level >= 10\n            pdf.SetFontStyle('',8)\n            pdf.RDMCell(35+135,5, (level >=1 ? \"  \" * level : \"\") + buf, \"L\")\n            pdf.SetFontStyle('B',8)\n            pdf.RDMCell(20,5, child.status.to_s, \"R\")\n            pdf.Ln\n          end\n        end\n\n        relations = issue.relations.select { |r| r.other_issue(issue).visible? }\n        unless relations.empty?\n          # for CJK\n          truncate_length = ( l(:general_pdf_encoding).upcase == \"UTF-8\" ? 80 : 60 )\n\n          pdf.SetFontStyle('B',9)\n          pdf.RDMCell(35+155,5, l(:label_related_issues) + \":\", \"LTR\")\n          pdf.Ln\n          relations.each do |relation|\n            buf = \"\"\n            buf += \"#{l(relation.label_for(issue))} \"\n            if relation.delay && relation.delay != 0\n              buf += \"(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) \"\n            end\n            if Setting.cross_project_issue_relations?\n              buf += \"#{relation.other_issue(issue).project} - \"\n            end\n            buf += \"#{relation.other_issue(issue).tracker}\" +\n                   \" # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}\"\n            buf = truncate(buf, :length => truncate_length)\n            pdf.SetFontStyle('', 8)\n            pdf.RDMCell(35+155-60, 5, buf, \"L\")\n            pdf.SetFontStyle('B',8)\n            pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, \"\")\n            pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), \"\")\n            pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), \"R\")\n            pdf.Ln\n          end\n        end\n        pdf.RDMCell(190,5, \"\", \"T\")\n        pdf.Ln\n\n        if issue.changesets.any? &&\n             User.current.allowed_to?(:view_changesets, issue.project)\n          pdf.SetFontStyle('B',9)\n          pdf.RDMCell(190,5, l(:label_associated_revisions), \"B\")\n          pdf.Ln\n          for changeset in issue.changesets\n            pdf.SetFontStyle('B',8)\n            csstr  = \"#{l(:label_revision)} #{changeset.format_identifier} - \"\n            csstr += format_time(changeset.committed_on) + \" - \" + changeset.author.to_s\n            pdf.RDMCell(190, 5, csstr)\n            pdf.Ln\n            unless changeset.comments.blank?\n              pdf.SetFontStyle('',8)\n              pdf.RDMwriteHTMLCell(190,5,0,0,\n                    changeset.comments.to_s, issue.attachments, \"\")\n            end\n            pdf.Ln\n          end\n        end\n\n        pdf.SetFontStyle('B',9)\n        pdf.RDMCell(190,5, l(:label_history), \"B\")\n        pdf.Ln\n        indice = 0\n\n        journals = issue.journals.includes([:user, :details]).order(\"`journals`.`created_on` ASC\") if User.current.allowed_to?(:view_private_messages, @issue.project)\n        journals = issue.journals.includes([:user, :details]).order(\"`journals`.`created_on` ASC\").where(\"`journals`.`private` IS NULL\") unless User.current.allowed_to?(:view_private_messages, @issue.project)\n        journals.each do |journal|\n          indice = indice + 1\n          pdf.SetFontStyle('B',8)\n          pdf.RDMCell(190,5,\n             \"#\" + indice.to_s +\n             \" - \" + format_time(journal.created_on) +\n             \" - \" + journal.user.name)\n          pdf.Ln\n          pdf.SetFontStyle('I',8)\n          details_to_strings(journal.details, true).each do |string|\n            pdf.RDMMultiCell(190,5, \"- \" + string)\n          end\n          if journal.notes?\n            pdf.Ln unless journal.details.empty?\n            pdf.SetFontStyle('',8)\n            pdf.RDMwriteHTMLCell(190,5,0,0,\n                  journal.notes.to_s, issue.attachments, \"\")\n          end\n          pdf.Ln\n        end\n\n        if issue.attachments.any?\n          pdf.SetFontStyle('B',9)\n          pdf.RDMCell(190,5, l(:label_attachment_plural), \"B\")\n          pdf.Ln\n          for attachment in issue.attachments\n            pdf.SetFontStyle('',8)\n            pdf.RDMCell(80,5, attachment.filename)\n            pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,\"R\")\n            pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,\"R\")\n            pdf.RDMCell(65,5, attachment.author.name,0,0,\"R\")\n            pdf.Ln\n          end\n        end\n        pdf.Output\n      end\n\n      # Returns a PDF string of a set of wiki pages\n      def wiki_pages_to_pdf(pages, project)\n        pdf = ITCPDF.new(current_language)\n        pdf.SetTitle(project.name)\n        pdf.alias_nb_pages\n        pdf.footer_date = format_date(Date.today)\n        pdf.AddPage\n        pdf.SetFontStyle('B',11)\n        pdf.RDMMultiCell(190,5, project.name)\n        pdf.Ln\n        # Set resize image scale\n        pdf.SetImageScale(1.6)\n        pdf.SetFontStyle('',9)\n        write_page_hierarchy(pdf, pages.group_by(&:parent_id))\n        pdf.Output\n      end\n\n      # Returns a PDF string of a single wiki page\n      def wiki_page_to_pdf(page, project)\n        pdf = ITCPDF.new(current_language)\n        pdf.SetTitle(\"#{project} - #{page.title}\")\n        pdf.alias_nb_pages\n        pdf.footer_date = format_date(Date.today)\n        pdf.AddPage\n        pdf.SetFontStyle('B',11)\n        pdf.RDMMultiCell(190,5,\n             \"#{project} - #{page.title} - # #{page.content.version}\")\n        pdf.Ln\n        # Set resize image scale\n        pdf.SetImageScale(1.6)\n        pdf.SetFontStyle('',9)\n        write_wiki_page(pdf, page)\n        pdf.Output\n      end\n\n      def write_page_hierarchy(pdf, pages, node=nil, level=0)\n        if pages[node]\n          pages[node].each do |page|\n            if @new_page\n              pdf.AddPage\n            else\n              @new_page = true\n            end\n            pdf.Bookmark page.title, level\n            write_wiki_page(pdf, page)\n            write_page_hierarchy(pdf, pages, page.id, level + 1) if pages[page.id]\n          end\n        end\n      end\n\n      def write_wiki_page(pdf, page)\n        pdf.RDMwriteHTMLCell(190,5,0,0,\n              page.content.text.to_s, page.attachments, 0)\n        if page.attachments.any?\n          pdf.Ln\n          pdf.SetFontStyle('B',9)\n          pdf.RDMCell(190,5, l(:label_attachment_plural), \"B\")\n          pdf.Ln\n          for attachment in page.attachments\n            pdf.SetFontStyle('',8)\n            pdf.RDMCell(80,5, attachment.filename)\n            pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,\"R\")\n            pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,\"R\")\n            pdf.RDMCell(65,5, attachment.author.name,0,0,\"R\")\n            pdf.Ln\n          end\n        end\n      end\n\n      class RDMPdfEncoding\n        def self.rdm_from_utf8(txt, encoding)\n          txt ||= ''\n          txt = Redmine::CodesetUtil.from_utf8(txt, encoding)\n          if txt.respond_to?(:force_encoding)\n            txt.force_encoding('ASCII-8BIT')\n          end\n          txt\n        end\n\n        def self.attach(attachments, filename, encoding)\n          filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)\n          atta = nil\n          if filename_utf8 =~ /^[^\\/\"]+\\.(gif|jpg|jpe|jpeg|png)$/i\n            atta = Attachment.latest_attach(attachments, filename_utf8)\n          end\n          if atta && atta.readable? && atta.visible?\n            return atta\n          else\n            return nil\n          end\n        end\n      end\n    end\n  end\nend\n
===================================================================
--- lib/redmine/export/pdf.rb	(revision 23ba9754d4c5240c1c3f2600a96100e91d4c571f)
+++ lib/redmine/export/pdf.rb	(revision )
@@ -646,11 +646,17 @@
         pdf.RDMCell(190,5, l(:label_history), "B")
         pdf.Ln
         indice = 0
+        view_private = User.current.allowed_to?(:view_private_messages, @project)
 
-        journals = issue.journals.includes([:user, :details]).order("`journals`.`created_on` ASC") if User.current.allowed_to?(:view_private_messages, @issue.project)
-        journals = issue.journals.includes([:user, :details]).order("`journals`.`created_on` ASC").where("`journals`.`private` IS NULL") unless User.current.allowed_to?(:view_private_messages, @issue.project)
+        journals = issue.journals.includes([:user, :details]).order("`journals`.`created_on` ASC")
         journals.each do |journal|
           indice = indice + 1
+          if journal.private
+            unless view_private
+              indice = indice - 1
+              next
+            end
+          end
           pdf.SetFontStyle('B',8)
           pdf.RDMCell(190,5,
              "#" + indice.to_s +
