mirror of
https://github.com/redmine/redmine.git
synced 2025-11-14 17:26:06 +01:00
Render PDF thumbnail using ImageMagick/GhostScript (#22481).
Patch by Gregor Schmidt. git-svn-id: http://svn.redmine.org/redmine/trunk@18158 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -77,7 +77,8 @@ class AdminController < ApplicationController
|
|||||||
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
|
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
|
||||||
["#{l :text_plugin_assets_writable} (./public/plugin_assets)", File.writable?(Redmine::Plugin.public_directory)],
|
["#{l :text_plugin_assets_writable} (./public/plugin_assets)", File.writable?(Redmine::Plugin.public_directory)],
|
||||||
[:text_rmagick_available, Object.const_defined?(:Magick)],
|
[: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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class AttachmentsController < ApplicationController
|
|||||||
if stale?(:etag => tbnail)
|
if stale?(:etag => tbnail)
|
||||||
send_file tbnail,
|
send_file tbnail,
|
||||||
:filename => filename_for_content_disposition(@attachment.filename),
|
:filename => filename_for_content_disposition(@attachment.filename),
|
||||||
:type => detect_content_type(@attachment),
|
:type => detect_content_type(@attachment, true),
|
||||||
:disposition => 'inline'
|
:disposition => 'inline'
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -236,12 +236,20 @@ class AttachmentsController < ApplicationController
|
|||||||
@attachment.deletable? ? true : deny_access
|
@attachment.deletable? ? true : deny_access
|
||||||
end
|
end
|
||||||
|
|
||||||
def detect_content_type(attachment)
|
def detect_content_type(attachment, is_thumb = false)
|
||||||
content_type = attachment.content_type
|
content_type = attachment.content_type
|
||||||
if content_type.blank? || content_type == "application/octet-stream"
|
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
|
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
|
end
|
||||||
|
|
||||||
def disposition(attachment)
|
def disposition(attachment)
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ class Attachment < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def thumbnailable?
|
def thumbnailable?
|
||||||
image?
|
image? || (is_pdf? && Redmine::Thumbnail.gs_available?)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the full path the attachment thumbnail, or nil
|
# Returns the full path the attachment thumbnail, or nil
|
||||||
@@ -221,7 +221,7 @@ class Attachment < ActiveRecord::Base
|
|||||||
target = thumbnail_path(size)
|
target = thumbnail_path(size)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Redmine::Thumbnail.generate(self.diskfile, target, size)
|
Redmine::Thumbnail.generate(self.diskfile, target, size, is_pdf?)
|
||||||
rescue => e
|
rescue => e
|
||||||
logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
|
logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1066,6 +1066,7 @@ de:
|
|||||||
text_caracters_minimum: "Muss mindestens %{count} Zeichen lang sein."
|
text_caracters_minimum: "Muss mindestens %{count} Zeichen lang sein."
|
||||||
text_comma_separated: Mehrere Werte erlaubt (durch Komma getrennt).
|
text_comma_separated: Mehrere Werte erlaubt (durch Komma getrennt).
|
||||||
text_convert_available: ImageMagick-Konvertierung verfügbar (optional)
|
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_custom_field_possible_values_info: 'Eine Zeile pro Wert'
|
||||||
text_default_administrator_account_changed: Administrator-Passwort geändert
|
text_default_administrator_account_changed: Administrator-Passwort geändert
|
||||||
text_destroy_time_entries: Gebuchte Aufwände löschen
|
text_destroy_time_entries: Gebuchte Aufwände löschen
|
||||||
|
|||||||
@@ -1087,6 +1087,7 @@ en-GB:
|
|||||||
current password
|
current password
|
||||||
setting_mail_handler_excluded_filenames: Exclude attachments by name
|
setting_mail_handler_excluded_filenames: Exclude attachments by name
|
||||||
text_convert_available: ImageMagick convert available (optional)
|
text_convert_available: ImageMagick convert available (optional)
|
||||||
|
text_gs_available: ImageMagick PDF support available (optional)
|
||||||
label_link: Link
|
label_link: Link
|
||||||
label_only: only
|
label_only: only
|
||||||
label_drop_down_list: drop-down list
|
label_drop_down_list: drop-down list
|
||||||
|
|||||||
@@ -1169,6 +1169,7 @@ en:
|
|||||||
text_plugin_assets_writable: Plugin assets directory writable
|
text_plugin_assets_writable: Plugin assets directory writable
|
||||||
text_rmagick_available: RMagick available (optional)
|
text_rmagick_available: RMagick available (optional)
|
||||||
text_convert_available: ImageMagick convert 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_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_destroy_time_entries: Delete reported hours
|
||||||
text_assign_time_entries_to_project: Assign reported hours to the project
|
text_assign_time_entries_to_project: Assign reported hours to the project
|
||||||
|
|||||||
@@ -25,12 +25,18 @@ module Redmine
|
|||||||
extend Redmine::Utils::Shell
|
extend Redmine::Utils::Shell
|
||||||
|
|
||||||
CONVERT_BIN = (Redmine::Configuration['imagemagick_convert_command'] || 'convert').freeze
|
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
|
# 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 unless convert_available?
|
||||||
|
return nil if is_pdf && !gs_available?
|
||||||
unless File.exists?(target)
|
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
|
# 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) }
|
unless File.open(source) {|f| ALLOWED_TYPES.include? MimeMagic.by_magic(f).try(:type) }
|
||||||
return nil
|
return nil
|
||||||
@@ -40,7 +46,12 @@ module Redmine
|
|||||||
FileUtils.mkdir_p directory
|
FileUtils.mkdir_p directory
|
||||||
end
|
end
|
||||||
size_option = "#{size}x#{size}>"
|
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)
|
unless system(cmd)
|
||||||
logger.error("Creating thumbnail failed (#{$?}):\nCommand: #{cmd}")
|
logger.error("Creating thumbnail failed (#{$?}):\nCommand: #{cmd}")
|
||||||
return nil
|
return nil
|
||||||
@@ -61,6 +72,22 @@ module Redmine
|
|||||||
@convert_available
|
@convert_available
|
||||||
end
|
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
|
def self.logger
|
||||||
Rails.logger
|
Rails.logger
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user