Project

General

Profile

Feature #22481 » Render-PDF-thumbnail-using-ImageMagick-GhostScript-20190511.patch

Go MAEDA, 2019-05-11 06:54

View differences:

app/controllers/admin_controller.rb
77 77
      [:text_file_repository_writable, File.writable?(Attachment.storage_path)],
78 78
      ["#{l :text_plugin_assets_writable} (./public/plugin_assets)",   File.writable?(Redmine::Plugin.public_directory)],
79 79
      [:text_rmagick_available,        Object.const_defined?(:Magick)],
80
      [:text_convert_available,        Redmine::Thumbnail.convert_available?]
80
      [:text_convert_available,        Redmine::Thumbnail.convert_available?],
81
      [:text_gs_available,             Redmine::Thumbnail.gs_available?]
81 82
    ]
82 83
  end
83 84
end
app/controllers/attachments_controller.rb
83 83
      if stale?(:etag => tbnail)
84 84
        send_file tbnail,
85 85
          :filename => filename_for_content_disposition(@attachment.filename),
86
          :type => detect_content_type(@attachment),
86
          :type => detect_content_type(@attachment, true),
87 87
          :disposition => 'inline'
88 88
      end
89 89
    else
......
236 236
    @attachment.deletable? ? true : deny_access
237 237
  end
238 238

  
239
  def detect_content_type(attachment)
239
  def detect_content_type(attachment, is_thumb = false)
240 240
    content_type = attachment.content_type
241 241
    if content_type.blank? || content_type == "application/octet-stream"
242
      content_type = Redmine::MimeType.of(attachment.filename)
242
      content_type = 
243
        Redmine::MimeType.of(attachment.filename).presence ||
244
        "application/octet-stream"
243 245
    end
244
    content_type.presence || "application/octet-stream"
246

  
247
    if is_thumb && content_type == "application/pdf"
248
      # PDF previews are stored in PNG format
249
      content_type = "image/png"
250
    end
251

  
252
    content_type
245 253
  end
246 254

  
247 255
  def disposition(attachment)
app/models/attachment.rb
201 201
  end
202 202

  
203 203
  def thumbnailable?
204
    image?
204
    image? || (is_pdf? && Redmine::Thumbnail.gs_available?)
205 205
  end
206 206

  
207 207
  # Returns the full path the attachment thumbnail, or nil
......
221 221
      target = thumbnail_path(size)
222 222

  
223 223
      begin
224
        Redmine::Thumbnail.generate(self.diskfile, target, size)
224
        Redmine::Thumbnail.generate(self.diskfile, target, size, is_pdf?)
225 225
      rescue => e
226 226
        logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
227 227
        return nil
config/locales/de.yml
1067 1067
  text_caracters_minimum: "Muss mindestens %{count} Zeichen lang sein."
1068 1068
  text_comma_separated: Mehrere Werte erlaubt (durch Komma getrennt).
1069 1069
  text_convert_available: ImageMagick-Konvertierung verfügbar (optional)
1070
  text_gs_available: ImageMagick PDF-Unterstützung verfügbar (optional)
1070 1071
  text_custom_field_possible_values_info: 'Eine Zeile pro Wert'
1071 1072
  text_default_administrator_account_changed: Administrator-Passwort geändert
1072 1073
  text_destroy_time_entries: Gebuchte Aufwände löschen
config/locales/en-GB.yml
1088 1088
    current password
1089 1089
  setting_mail_handler_excluded_filenames: Exclude attachments by name
1090 1090
  text_convert_available: ImageMagick convert available (optional)
1091
  text_gs_available: ImageMagick PDF support available (optional)
1091 1092
  label_link: Link
1092 1093
  label_only: only
1093 1094
  label_drop_down_list: drop-down list
config/locales/en.yml
1170 1170
  text_plugin_assets_writable: Plugin assets directory writable
1171 1171
  text_rmagick_available: RMagick available (optional)
1172 1172
  text_convert_available: ImageMagick convert available (optional)
1173
  text_gs_available: ImageMagick PDF support available (optional)
1173 1174
  text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?"
1174 1175
  text_destroy_time_entries: Delete reported hours
1175 1176
  text_assign_time_entries_to_project: Assign reported hours to the project
lib/redmine/thumbnail.rb
25 25
    extend Redmine::Utils::Shell
26 26

  
27 27
    CONVERT_BIN = (Redmine::Configuration['imagemagick_convert_command'] || 'convert').freeze
28
    ALLOWED_TYPES = %w(image/bmp image/gif image/jpeg image/png)
28
    ALLOWED_TYPES = %w(image/bmp image/gif image/jpeg image/png application/pdf)
29 29

  
30 30
    # Generates a thumbnail for the source image to target
31
    def self.generate(source, target, size)
31
    def self.generate(source, target, size, is_pdf = false)
32 32
      return nil unless convert_available?
33
      return nil if is_pdf && !gs_available?
33 34
      unless File.exists?(target)
35
        mime_type = File.open(source) {|f| MimeMagic.by_magic(f).try(:type) }
36
        return nil if mime_type.nil?
37
        return nil if !ALLOWED_TYPES.include? mime_type
38
        return nil if is_pdf && mime_type != "application/pdf"
39

  
34 40
        # Make sure we only invoke Imagemagick if the file type is allowed
35 41
        unless File.open(source) {|f| ALLOWED_TYPES.include? MimeMagic.by_magic(f).try(:type) }
36 42
          return nil
......
40 46
          FileUtils.mkdir_p directory
41 47
        end
42 48
        size_option = "#{size}x#{size}>"
43
        cmd = "#{shell_quote CONVERT_BIN} #{shell_quote source} -auto-orient -thumbnail #{shell_quote size_option} #{shell_quote target}"
49

  
50
        if is_pdf
51
          cmd = "#{shell_quote CONVERT_BIN} #{shell_quote "#{source}[0]"} -thumbnail #{shell_quote size_option} #{shell_quote "png:#{target}"}"
52
        else
53
          cmd = "#{shell_quote CONVERT_BIN} #{shell_quote source} -auto-orient -thumbnail #{shell_quote size_option} #{shell_quote target}"
54
        end
44 55
        unless system(cmd)
45 56
          logger.error("Creating thumbnail failed (#{$?}):\nCommand: #{cmd}")
46 57
          return nil
......
61 72
      @convert_available
62 73
    end
63 74

  
75
    def self.gs_available?
76
      return @gs_available if defined?(@gs_available)
77

  
78
      if Redmine::Platform.mswin?
79
        @gs_available = false
80
      else
81
        begin
82
          `gs -version`
83
          @gs_available = $?.success?
84
        rescue
85
          @gs_available = false
86
        end
87
      end
88
      @gs_available
89
    end
90

  
64 91
    def self.logger
65 92
      Rails.logger
66 93
    end
(6-6/9)