Feature #22481 » Render-PDF-thumbnail-using-ImageMagick-GhostScript-20170504.patch
| app/controllers/admin_controller.rb (working copy) | ||
|---|---|---|
| 79 | 79 |
[:text_file_repository_writable, File.writable?(Attachment.storage_path)], |
| 80 | 80 |
["#{l :text_plugin_assets_writable} (./public/plugin_assets)", File.writable?(Redmine::Plugin.public_directory)],
|
| 81 | 81 |
[:text_rmagick_available, Object.const_defined?(:Magick)], |
| 82 |
[:text_convert_available, Redmine::Thumbnail.convert_available?] |
|
| 82 |
[:text_convert_available, Redmine::Thumbnail.convert_available?], |
|
| 83 |
[:text_gs_available, Redmine::Thumbnail.gs_available?] |
|
| 83 | 84 |
] |
| 84 | 85 |
end |
| 85 | 86 |
end |
| app/controllers/attachments_controller.rb (working copy) | ||
|---|---|---|
| 73 | 73 |
if stale?(:etag => tbnail) |
| 74 | 74 |
send_file tbnail, |
| 75 | 75 |
:filename => filename_for_content_disposition(@attachment.filename), |
| 76 |
:type => detect_content_type(@attachment), |
|
| 76 |
:type => detect_content_type(@attachment, true),
|
|
| 77 | 77 |
:disposition => 'inline' |
| 78 | 78 |
end |
| 79 | 79 |
else |
| ... | ... | |
| 210 | 210 |
@attachment.deletable? ? true : deny_access |
| 211 | 211 |
end |
| 212 | 212 | |
| 213 |
def detect_content_type(attachment) |
|
| 213 |
def detect_content_type(attachment, is_thumb = false)
|
|
| 214 | 214 |
content_type = attachment.content_type |
| 215 | 215 |
if content_type.blank? || content_type == "application/octet-stream" |
| 216 | 216 |
content_type = Redmine::MimeType.of(attachment.filename) |
| 217 | 217 |
end |
| 218 |
content_type.to_s |
|
| 218 |
content_type = content_type.to_s |
|
| 219 | ||
| 220 |
if is_thumb and content_type == "application/pdf" |
|
| 221 |
# PDF previews are stored in PNG format |
|
| 222 |
content_type = "image/png" |
|
| 223 |
end |
|
| 224 | ||
| 225 |
content_type |
|
| 219 | 226 |
end |
| 220 | 227 | |
| 221 | 228 |
def disposition(attachment) |
| app/models/attachment.rb (working copy) | ||
|---|---|---|
| 200 | 200 |
end |
| 201 | 201 | |
| 202 | 202 |
def thumbnailable? |
| 203 |
image? |
|
| 203 |
image? || (is_pdf? && Redmine::Thumbnail.gs_available?)
|
|
| 204 | 204 |
end |
| 205 | 205 | |
| 206 | 206 |
# Returns the full path the attachment thumbnail, or nil |
| ... | ... | |
| 220 | 220 |
target = File.join(self.class.thumbnails_storage_path, "#{id}_#{digest}_#{size}.thumb")
|
| 221 | 221 | |
| 222 | 222 |
begin |
| 223 |
Redmine::Thumbnail.generate(self.diskfile, target, size) |
|
| 223 |
Redmine::Thumbnail.generate(self.diskfile, target, size, is_pdf?)
|
|
| 224 | 224 |
rescue => e |
| 225 | 225 |
logger.error "An error occured while generating thumbnail for #{disk_filename} to #{target}\nException was: #{e.message}" if logger
|
| 226 | 226 |
return nil |
| config/locales/de.yml (working copy) | ||
|---|---|---|
| 1059 | 1059 |
text_caracters_minimum: "Muss mindestens %{count} Zeichen lang sein."
|
| 1060 | 1060 |
text_comma_separated: Mehrere Werte erlaubt (durch Komma getrennt). |
| 1061 | 1061 |
text_convert_available: ImageMagick-Konvertierung verfügbar (optional) |
| 1062 |
text_gs_available: ImageMagick PDF-Unterstützung verfügbar (optional) |
|
| 1062 | 1063 |
text_custom_field_possible_values_info: 'Eine Zeile pro Wert' |
| 1063 | 1064 |
text_default_administrator_account_changed: Administrator-Passwort geändert |
| 1064 | 1065 |
text_destroy_time_entries: Gebuchte Aufwände löschen |
| config/locales/en-GB.yml (working copy) | ||
|---|---|---|
| 1090 | 1090 |
current password |
| 1091 | 1091 |
setting_mail_handler_excluded_filenames: Exclude attachments by name |
| 1092 | 1092 |
text_convert_available: ImageMagick convert available (optional) |
| 1093 |
text_gs_available: ImageMagick PDF support available (optional) |
|
| 1093 | 1094 |
label_link: Link |
| 1094 | 1095 |
label_only: only |
| 1095 | 1096 |
label_drop_down_list: drop-down list |
| config/locales/en.yml (working copy) | ||
|---|---|---|
| 1130 | 1130 |
text_plugin_assets_writable: Plugin assets directory writable |
| 1131 | 1131 |
text_rmagick_available: RMagick available (optional) |
| 1132 | 1132 |
text_convert_available: ImageMagick convert available (optional) |
| 1133 |
text_gs_available: ImageMagick PDF support available (optional) |
|
| 1133 | 1134 |
text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?"
|
| 1134 | 1135 |
text_destroy_time_entries: Delete reported hours |
| 1135 | 1136 |
text_assign_time_entries_to_project: Assign reported hours to the project |
| lib/redmine/thumbnail.rb (working copy) | ||
|---|---|---|
| 23 | 23 |
extend Redmine::Utils::Shell |
| 24 | 24 | |
| 25 | 25 |
CONVERT_BIN = (Redmine::Configuration['imagemagick_convert_command'] || 'convert').freeze |
| 26 |
ALLOWED_TYPES = %w(image/bmp image/gif image/jpeg image/png) |
|
| 26 |
ALLOWED_TYPES = %w(image/bmp image/gif image/jpeg image/png application/pdf)
|
|
| 27 | 27 | |
| 28 | 28 |
# Generates a thumbnail for the source image to target |
| 29 |
def self.generate(source, target, size) |
|
| 29 |
def self.generate(source, target, size, is_pdf = false)
|
|
| 30 | 30 |
return nil unless convert_available? |
| 31 |
return nil if is_pdf && !gs_available? |
|
| 31 | 32 |
unless File.exists?(target) |
| 33 |
mime_type = File.open(source) {|f| MimeMagic.by_magic(f).try(:type) }
|
|
| 34 |
return nil if mime_type.nil? |
|
| 35 |
return nil if !ALLOWED_TYPES.include? mime_type |
|
| 36 |
return nil if is_pdf && mime_type != "application/pdf" |
|
| 37 | ||
| 32 | 38 |
# Make sure we only invoke Imagemagick if the file type is allowed |
| 33 | 39 |
unless File.open(source) {|f| ALLOWED_TYPES.include? MimeMagic.by_magic(f).try(:type) }
|
| 34 | 40 |
return nil |
| ... | ... | |
| 38 | 44 |
FileUtils.mkdir_p directory |
| 39 | 45 |
end |
| 40 | 46 |
size_option = "#{size}x#{size}>"
|
| 41 |
cmd = "#{shell_quote CONVERT_BIN} #{shell_quote source} -thumbnail #{shell_quote size_option} #{shell_quote target}"
|
|
| 47 | ||
| 48 |
if is_pdf |
|
| 49 |
cmd = "#{shell_quote CONVERT_BIN} #{shell_quote "#{source}[0]"} -thumbnail #{shell_quote size_option} #{shell_quote "png:#{target}"}"
|
|
| 50 |
else |
|
| 51 |
cmd = "#{shell_quote CONVERT_BIN} #{shell_quote source} -thumbnail #{shell_quote size_option} #{shell_quote target}"
|
|
| 52 |
end |
|
| 53 | ||
| 42 | 54 |
unless system(cmd) |
| 43 | 55 |
logger.error("Creating thumbnail failed (#{$?}):\nCommand: #{cmd}")
|
| 44 | 56 |
return nil |
| ... | ... | |
| 54 | 66 |
@convert_available |
| 55 | 67 |
end |
| 56 | 68 | |
| 69 |
def self.gs_available? |
|
| 70 |
return @gs_available if defined?(@gs_available) |
|
| 71 | ||
| 72 |
@gs_available = system("gs -version") rescue false
|
|
| 73 |
@gs_available ||= system("gswin32 -version") rescue false
|
|
| 74 |
@gs_available ||= system("gswin64 -version") rescue false
|
|
| 75 | ||
| 76 |
@gs_available |
|
| 77 |
end |
|
| 78 | ||
| 57 | 79 |
def self.logger |
| 58 | 80 |
Rails.logger |
| 59 | 81 |
end |