Feature #22481 » Render-PDF-thumbnail-using-ImageMagick-GhostScript-20190326.patch
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 | 242 |
content_type = Redmine::MimeType.of(attachment.filename) |
243 | 243 |
end |
244 |
content_type.to_s |
|
244 |
content_type = content_type.to_s |
|
245 | ||
246 |
if is_thumb and content_type == "application/pdf" |
|
247 |
# PDF previews are stored in PNG format |
|
248 |
content_type = "image/png" |
|
249 |
end |
|
250 | ||
251 |
content_type |
|
245 | 252 |
end |
246 | 253 | |
247 | 254 |
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 | ||
---|---|---|
1162 | 1162 |
text_plugin_assets_writable: Plugin assets directory writable |
1163 | 1163 |
text_rmagick_available: RMagick available (optional) |
1164 | 1164 |
text_convert_available: ImageMagick convert available (optional) |
1165 |
text_gs_available: ImageMagick PDF support available (optional) |
|
1165 | 1166 |
text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?" |
1166 | 1167 |
text_destroy_time_entries: Delete reported hours |
1167 | 1168 |
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 |
... | ... | |
56 | 67 |
@convert_available |
57 | 68 |
end |
58 | 69 | |
70 |
def self.gs_available? |
|
71 |
return @gs_available if defined?(@gs_available) |
|
72 | ||
73 |
if Redmine::Platform.mswin? |
|
74 |
@gs_available = false |
|
75 |
else |
|
76 |
@gs_available = system("gs -version") rescue false |
|
77 |
end |
|
78 | ||
79 |
@gs_available |
|
80 |
end |
|
81 | ||
59 | 82 |
def self.logger |
60 | 83 |
Rails.logger |
61 | 84 |
end |