]> source.dussan.org Git - redmine.git/commitdiff
Render PDF thumbnail using ImageMagick/GhostScript (#22481).
authorGo MAEDA <maeda@farend.jp>
Sun, 12 May 2019 03:55:56 +0000 (03:55 +0000)
committerGo MAEDA <maeda@farend.jp>
Sun, 12 May 2019 03:55:56 +0000 (03:55 +0000)
Patch by Gregor Schmidt.

git-svn-id: http://svn.redmine.org/redmine/trunk@18158 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/admin_controller.rb
app/controllers/attachments_controller.rb
app/models/attachment.rb
config/locales/de.yml
config/locales/en-GB.yml
config/locales/en.yml
lib/redmine/thumbnail.rb

index 91d3b071218c72b821973f5fb0c25ad693958ce8..eb2288eff71a32139ccf22a16da98cba88ca8e25 100644 (file)
@@ -77,7 +77,8 @@ class AdminController < ApplicationController
       [:text_file_repository_writable, File.writable?(Attachment.storage_path)],
       ["#{l :text_plugin_assets_writable} (./public/plugin_assets)",   File.writable?(Redmine::Plugin.public_directory)],
       [:text_rmagick_available,        Object.const_defined?(:Magick)],
-      [:text_convert_available,        Redmine::Thumbnail.convert_available?]
+      [:text_convert_available,        Redmine::Thumbnail.convert_available?],
+      [:text_gs_available,             Redmine::Thumbnail.gs_available?]
     ]
   end
 end
index bc558fdc05e3d834172b865cb6e66a0f99338f8b..4817149a4853c3a6f230036c468eacaf469e0d1b 100644 (file)
@@ -83,7 +83,7 @@ class AttachmentsController < ApplicationController
       if stale?(:etag => tbnail)
         send_file tbnail,
           :filename => filename_for_content_disposition(@attachment.filename),
-          :type => detect_content_type(@attachment),
+          :type => detect_content_type(@attachment, true),
           :disposition => 'inline'
       end
     else
@@ -236,12 +236,20 @@ class AttachmentsController < ApplicationController
     @attachment.deletable? ? true : deny_access
   end
 
-  def detect_content_type(attachment)
+  def detect_content_type(attachment, is_thumb = false)
     content_type = attachment.content_type
     if content_type.blank? || content_type == "application/octet-stream"
-      content_type = Redmine::MimeType.of(attachment.filename)
+      content_type = 
+        Redmine::MimeType.of(attachment.filename).presence ||
+        "application/octet-stream"
     end
-    content_type.presence || "application/octet-stream"
+
+    if is_thumb && content_type == "application/pdf"
+      # PDF previews are stored in PNG format
+      content_type = "image/png"
+    end
+
+    content_type
   end
 
   def disposition(attachment)
index 362ac1fde2b8af7676b8bd200704591fcbd6b7e6..a0d712a348568411add590902a1936d9567bfad7 100644 (file)
@@ -201,7 +201,7 @@ class Attachment < ActiveRecord::Base
   end
 
   def thumbnailable?
-    image?
+    image? || (is_pdf? && Redmine::Thumbnail.gs_available?)
   end
 
   # Returns the full path the attachment thumbnail, or nil
@@ -221,7 +221,7 @@ class Attachment < ActiveRecord::Base
       target = thumbnail_path(size)
 
       begin
-        Redmine::Thumbnail.generate(self.diskfile, target, size)
+        Redmine::Thumbnail.generate(self.diskfile, target, size, is_pdf?)
       rescue => e
         logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
         return nil
index c0dec9fbbff6c69f61cfa9852aaff5627ab187a0..01a9701e39ddc074be047c61f2aea0dcf5363439 100644 (file)
@@ -1066,6 +1066,7 @@ de:
   text_caracters_minimum: "Muss mindestens %{count} Zeichen lang sein."
   text_comma_separated: Mehrere Werte erlaubt (durch Komma getrennt).
   text_convert_available: ImageMagick-Konvertierung verfügbar (optional)
+  text_gs_available: ImageMagick PDF-Unterstützung verfügbar (optional)
   text_custom_field_possible_values_info: 'Eine Zeile pro Wert'
   text_default_administrator_account_changed: Administrator-Passwort geändert
   text_destroy_time_entries: Gebuchte Aufwände löschen
index 8b744afbba6ed42230323d77262c68f344621514..67a7f4b4bce6d9360916d2a37a57841193d033e9 100644 (file)
@@ -1087,6 +1087,7 @@ en-GB:
     current password
   setting_mail_handler_excluded_filenames: Exclude attachments by name
   text_convert_available: ImageMagick convert available (optional)
+  text_gs_available: ImageMagick PDF support available (optional)
   label_link: Link
   label_only: only
   label_drop_down_list: drop-down list
index 17f9c7458a6c61c5a22f30ceb0fd67da5b1bb6ab..7b25de4a26d3cbcb66ece023571146b432474cd5 100644 (file)
@@ -1169,6 +1169,7 @@ en:
   text_plugin_assets_writable: Plugin assets directory writable
   text_rmagick_available: RMagick available (optional)
   text_convert_available: ImageMagick convert available (optional)
+  text_gs_available: ImageMagick PDF support available (optional)
   text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?"
   text_destroy_time_entries: Delete reported hours
   text_assign_time_entries_to_project: Assign reported hours to the project
index 31ac62272ccec83b251b89631e80740f1649de3b..27871727380be208c3778eb7ac4d3108d22db262 100644 (file)
@@ -25,12 +25,18 @@ module Redmine
     extend Redmine::Utils::Shell
 
     CONVERT_BIN = (Redmine::Configuration['imagemagick_convert_command'] || 'convert').freeze
-    ALLOWED_TYPES = %w(image/bmp image/gif image/jpeg image/png)
+    ALLOWED_TYPES = %w(image/bmp image/gif image/jpeg image/png application/pdf)
 
     # Generates a thumbnail for the source image to target
-    def self.generate(source, target, size)
+    def self.generate(source, target, size, is_pdf = false)
       return nil unless convert_available?
+      return nil if is_pdf && !gs_available?
       unless File.exists?(target)
+        mime_type = File.open(source) {|f| MimeMagic.by_magic(f).try(:type) }
+        return nil if mime_type.nil?
+        return nil if !ALLOWED_TYPES.include? mime_type
+        return nil if is_pdf && mime_type != "application/pdf"
+
         # Make sure we only invoke Imagemagick if the file type is allowed
         unless File.open(source) {|f| ALLOWED_TYPES.include? MimeMagic.by_magic(f).try(:type) }
           return nil
@@ -40,7 +46,12 @@ module Redmine
           FileUtils.mkdir_p directory
         end
         size_option = "#{size}x#{size}>"
-        cmd = "#{shell_quote CONVERT_BIN} #{shell_quote source} -auto-orient -thumbnail #{shell_quote size_option} #{shell_quote target}"
+
+        if is_pdf
+          cmd = "#{shell_quote CONVERT_BIN} #{shell_quote "#{source}[0]"} -thumbnail #{shell_quote size_option} #{shell_quote "png:#{target}"}"
+        else
+          cmd = "#{shell_quote CONVERT_BIN} #{shell_quote source} -auto-orient -thumbnail #{shell_quote size_option} #{shell_quote target}"
+        end
         unless system(cmd)
           logger.error("Creating thumbnail failed (#{$?}):\nCommand: #{cmd}")
           return nil
@@ -61,6 +72,22 @@ module Redmine
       @convert_available
     end
 
+    def self.gs_available?
+      return @gs_available if defined?(@gs_available)
+
+      if Redmine::Platform.mswin?
+        @gs_available = false
+      else
+        begin
+          `gs -version`
+          @gs_available = $?.success?
+        rescue
+          @gs_available = false
+        end
+      end
+      @gs_available
+    end
+
     def self.logger
       Rails.logger
     end