FIX: Perform crop using user-specified image sizes (#9224)

* FIX: Perform crop using user-specified image sizes

It used to resize the images to max width and height first and then
perform the crop operation. This is wrong because it ignored the user
specified image sizes from the Markdown.

* DEV: Use real images in test
This commit is contained in:
Bianca Nenciu 2020-03-26 16:40:00 +02:00 committed by GitHub
parent ba1a08510e
commit 7952cbb9a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 42 deletions

View File

@ -187,6 +187,10 @@
&.site-icon {
padding-bottom: 0;
}
&.resizable {
object-fit: cover;
object-position: top;
}
}
.d-editor-preview .image-wrapper {

View File

@ -308,29 +308,34 @@ class CookedPostProcessor
end
def convert_to_link!(img)
w, h = img["width"].to_i, img["height"].to_i
user_width, user_height = (w > 0 && h > 0 && [w, h]) ||
get_size_from_attributes(img) ||
get_size_from_image_sizes(img["src"], @opts[:image_sizes])
limit_size!(img)
src = img["src"]
return if src.blank? || is_a_hyperlink?(img) || is_svg?(img)
width, height = img["width"].to_i, img["height"].to_i
# TODO: store original dimentions in db
original_width, original_height = (get_size(src) || [0, 0]).map(&:to_i)
# can't reach the image...
if original_width == 0 || original_height == 0
Rails.logger.info "Can't reach '#{src}' to get its dimension."
return
end
return if original_width <= width && original_height <= height
return if original_width <= SiteSetting.max_image_width && original_height <= SiteSetting.max_image_height
crop = SiteSetting.min_ratio_to_crop > 0
crop &&= original_width.to_f / original_height.to_f < SiteSetting.min_ratio_to_crop
user_width, user_height = [original_width, original_height] if user_width.to_i <= 0 && user_height.to_i <= 0
width, height = user_width, user_height
crop = SiteSetting.min_ratio_to_crop > 0 && width.to_f / height.to_f < SiteSetting.min_ratio_to_crop
if crop
width, height = ImageSizer.crop(original_width, original_height)
img["width"] = width
img["height"] = height
width, height = ImageSizer.crop(width, height)
img["width"], img["height"] = width, height
else
width, height = ImageSizer.resize(width, height)
end
# if the upload already exists and is attached to a different post,
@ -700,10 +705,7 @@ class CookedPostProcessor
def post_process_images
extract_images.each do |img|
unless add_image_placeholder!(img)
limit_size!(img)
convert_to_link!(img)
end
convert_to_link!(img) unless add_image_placeholder!(img)
end
end

View File

@ -416,12 +416,17 @@ describe CookedPostProcessor do
end
context "with unsized images" do
fab!(:upload) { Fabricate(:image_upload, width: 123, height: 456) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="#{upload.url}">
HTML
end
fab!(:post) { Fabricate(:post_with_unsized_images) }
let(:cpp) { CookedPostProcessor.new(post) }
it "adds the width and height to images that don't have them" do
FastImage.expects(:size).returns([123, 456])
cpp.post_process
expect(cpp.html).to match(/width="123" height="456"/)
expect(cpp).to be_dirty
@ -430,6 +435,8 @@ describe CookedPostProcessor do
end
context "with large images" do
fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="#{upload.url}">
@ -441,13 +448,9 @@ describe CookedPostProcessor do
before do
SiteSetting.max_image_height = 2000
SiteSetting.create_thumbnails = true
FastImage.expects(:size).returns([1750, 2000])
end
it "generates overlay information" do
OptimizedImage.expects(:resize).returns(true)
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
cpp.post_process
expect(cpp.html).to match_html <<~HTML
@ -468,6 +471,8 @@ describe CookedPostProcessor do
end
it 'should not add lightbox' do
FastImage.expects(:size).returns([1750, 2000])
cpp.post_process
expect(cpp.html).to match_html <<~HTML
@ -482,6 +487,8 @@ describe CookedPostProcessor do
end
it 'should not add lightbox' do
FastImage.expects(:size).returns([1750, 2000])
cpp.post_process
expect(cpp.html).to match_html <<~HTML
@ -495,6 +502,8 @@ describe CookedPostProcessor do
end
it 'should not add lightbox' do
FastImage.expects(:size).returns([1750, 2000])
SiteSetting.crawl_images = true
cpp.post_process
@ -537,8 +546,8 @@ describe CookedPostProcessor do
context "when the upload is attached to the correct post" do
before do
FastImage.expects(:size).returns([1750, 2000])
OptimizedImage.expects(:resize).returns(true)
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
Discourse.store.class.any_instance.expects(:has_been_uploaded?).at_least_once.returns(true)
upload.update(secure: true, access_control_post: post)
end
@ -568,6 +577,8 @@ describe CookedPostProcessor do
end
context "with tall images" do
fab!(:upload) { Fabricate(:image_upload, width: 860, height: 2000) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="#{upload.url}">
@ -578,10 +589,6 @@ describe CookedPostProcessor do
before do
SiteSetting.create_thumbnails = true
FastImage.expects(:size).returns([860, 2000])
OptimizedImage.expects(:resize).never
OptimizedImage.expects(:crop).returns(true)
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
end
it "crops the image" do
@ -594,6 +601,8 @@ describe CookedPostProcessor do
end
context "with iPhone X screenshots" do
fab!(:upload) { Fabricate(:image_upload, width: 1125, height: 2436) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="#{upload.url}">
@ -604,10 +613,6 @@ describe CookedPostProcessor do
before do
SiteSetting.create_thumbnails = true
FastImage.expects(:size).returns([1125, 2436])
OptimizedImage.expects(:resize).returns(true)
OptimizedImage.expects(:crop).never
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
end
it "crops the image" do
@ -625,6 +630,8 @@ describe CookedPostProcessor do
end
context "with large images when using subfolders" do
fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="/subfolder#{upload.url}">
@ -635,13 +642,10 @@ describe CookedPostProcessor do
before do
set_subfolder "/subfolder"
stub_request(:get, "http://#{Discourse.current_hostname}/subfolder#{upload.url}").to_return(status: 200, body: File.new(Discourse.store.path_for(upload)))
SiteSetting.max_image_height = 2000
SiteSetting.create_thumbnails = true
FastImage.expects(:size).returns([1750, 2000])
OptimizedImage.expects(:resize).returns(true)
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
end
it "generates overlay information" do
@ -670,6 +674,8 @@ describe CookedPostProcessor do
end
context "with title and alt" do
fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="#{upload.url}" title="WAT" alt="RED">
@ -681,9 +687,6 @@ describe CookedPostProcessor do
before do
SiteSetting.max_image_height = 2000
SiteSetting.create_thumbnails = true
FastImage.expects(:size).returns([1750, 2000])
OptimizedImage.expects(:resize).returns(true)
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
end
it "generates overlay information using image title and ignores alt" do
@ -701,6 +704,8 @@ describe CookedPostProcessor do
end
context "with title only" do
fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="#{upload.url}" title="WAT">
@ -712,9 +717,6 @@ describe CookedPostProcessor do
before do
SiteSetting.max_image_height = 2000
SiteSetting.create_thumbnails = true
FastImage.expects(:size).returns([1750, 2000])
OptimizedImage.expects(:resize).returns(true)
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
end
it "generates overlay information using image title" do
@ -732,6 +734,8 @@ describe CookedPostProcessor do
end
context "with alt only" do
fab!(:upload) { Fabricate(:image_upload, width: 1750, height: 2000) }
fab!(:post) do
Fabricate(:post, raw: <<~HTML)
<img src="#{upload.url}" alt="RED">
@ -743,9 +747,6 @@ describe CookedPostProcessor do
before do
SiteSetting.max_image_height = 2000
SiteSetting.create_thumbnails = true
FastImage.expects(:size).returns([1750, 2000])
OptimizedImage.expects(:resize).returns(true)
FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0)
end
it "generates overlay information using image alt" do

View File

@ -21,6 +21,20 @@ Fabricator(:upload) do
extension "png"
end
Fabricator(:image_upload, from: :upload) do
after_create do |upload|
file = Tempfile.new(['fabricated', '.png'])
`convert -size #{upload.width}x#{upload.height} xc:white "#{file.path}"`
upload.url = Discourse.store.store_upload(file, upload)
upload.sha1 = Upload.generate_digest(file.path)
WebMock
.stub_request(:get, "http://#{Discourse.current_hostname}#{upload.url}")
.to_return(status: 200, body: File.new(file.path))
end
end
Fabricator(:video_upload, from: :upload) do
original_filename "video.mp4"
width nil