Fetch the resized image

Instead of getting the full size image, the image can be fetched with a
width and height parameter so that only the resized data is
transferred. The url looks like this:

https://cdn-images-1.medium.com/fit/c/<width>/<height>/<media-id>

I picked a max image width of 800px. If the image width is more than
that, it scales the width down to 800, then applies that ratio to the
height. If it's smaller than that, the image is displayed as the
original.
This commit is contained in:
Edward Loveall 2021-07-05 14:54:58 -04:00
parent f7a72fd2b5
commit d863cc27a5
No known key found for this signature in database
GPG key ID: 789A4AE983AC8901
6 changed files with 61 additions and 14 deletions

View file

@ -180,14 +180,14 @@ describe ParagraphConverter do
"layout": "INSET_CENTER", "layout": "INSET_CENTER",
"metadata": { "metadata": {
"id": "image.png", "id": "image.png",
"originalWidth": 618, "originalWidth": 1000,
"originalHeight": 682 "originalHeight": 600
} }
} }
JSON JSON
expected = [ expected = [
Figure.new(children: [ Figure.new(children: [
Image.new(src: "image.png"), Image.new(src: "image.png", originalWidth: 1000, originalHeight: 600),
FigureCaption.new(children: [ FigureCaption.new(children: [
Text.new("Image by "), Text.new("Image by "),
Anchor.new(href: "https://unsplash.com/@someuser", text: "someuser"), Anchor.new(href: "https://unsplash.com/@someuser", text: "someuser"),
@ -303,7 +303,7 @@ describe ParagraphConverter do
UnorderedList.new([ListItem.new([Text.new("text")] of Child)] of Child), UnorderedList.new([ListItem.new([Text.new("text")] of Child)] of Child),
OrderedList.new([ListItem.new([Text.new("text")] of Child)] of Child), OrderedList.new([ListItem.new([Text.new("text")] of Child)] of Child),
Figure.new(children: [ Figure.new(children: [
Image.new(src: "1*miroimage.png"), Image.new(src: "1*miroimage.png", originalWidth: 618, originalHeight: 682),
FigureCaption.new(children: [Text.new("text")] of Child), FigureCaption.new(children: [Text.new("text")] of Child),
] of Child), ] of Child),
IFrame.new(href: "https://example.com"), IFrame.new(href: "https://example.com"),

View file

@ -90,7 +90,7 @@ describe PageContent do
it "renders a figure and figure caption" do it "renders a figure and figure caption" do
page = Page.new(nodes: [ page = Page.new(nodes: [
Figure.new(children: [ Figure.new(children: [
Image.new(src: "image.png"), Image.new(src: "image.png", originalWidth: 100, originalHeight: 100),
FigureCaption.new(children: [ FigureCaption.new(children: [
Text.new("A caption") Text.new("A caption")
] of Child), ] of Child),
@ -101,7 +101,7 @@ describe PageContent do
html.should eq stripped_html <<-HTML html.should eq stripped_html <<-HTML
<figure> <figure>
<img src="https://cdn-images-1.medium.com/image.png"> <img src="https://cdn-images-1.medium.com/fit/c/100/100/image.png" width="100" height="100">
<figcaption>A caption</figcaption> <figcaption>A caption</figcaption>
</figure> </figure>
HTML HTML
@ -134,13 +134,17 @@ describe PageContent do
it "renders an image" do it "renders an image" do
page = Page.new(nodes: [ page = Page.new(nodes: [
Paragraph.new(children: [ Paragraph.new(children: [
Image.new(src: "image.png"), Image.new(src: "image.png", originalWidth: 100, originalHeight: 100),
] of Child) ] of Child)
] of Child) ] of Child)
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
html.should eq %(<p><img src="https://cdn-images-1.medium.com/image.png"></p>) html.should eq stripped_html <<-HTML
<p>
<img src="https://cdn-images-1.medium.com/fit/c/100/100/image.png" width="100" height="100">
</p>
HTML
end end
it "renders an iframe container" do it "renders an iframe container" do

18
spec/models/nodes_spec.cr Normal file
View file

@ -0,0 +1,18 @@
require "../spec_helper"
module Nodes
describe Image do
it "adjusts the width and height proportionally" do
image = Image.new(src: "image.png", originalWidth: 1000, originalHeight: 603)
image.width.should eq("800")
image.height.should eq("482")
end
it "includes the adjusted width and height in the src" do
image = Image.new(src: "image.png", originalWidth: 1000, originalHeight: 603)
image.src.should eq("https://cdn-images-1.medium.com/fit/c/800/482/image.png")
end
end
end

View file

@ -77,7 +77,11 @@ class ParagraphConverter
if metadata = paragraph.metadata if metadata = paragraph.metadata
caption_markup = MarkupConverter.convert(paragraph.text, paragraph.markups) caption_markup = MarkupConverter.convert(paragraph.text, paragraph.markups)
Figure.new(children: [ Figure.new(children: [
Image.new(src: metadata.id), Image.new(
src: metadata.id,
originalWidth: metadata.originalWidth,
originalHeight: metadata.originalHeight
),
FigureCaption.new(children: caption_markup) FigureCaption.new(children: caption_markup)
] of Child) ] of Child)
else else

View file

@ -63,7 +63,7 @@ class PageContent < BaseComponent
end end
def render_child(child : Image) def render_child(child : Image)
img src: child.src img src: child.src, width: child.width, height: child.height
end end
def render_child(node : ListItem) def render_child(node : ListItem)

View file

@ -79,18 +79,39 @@ module Nodes
end end
class Image class Image
IMAGE_HOST = "https://cdn-images-1.medium.com" IMAGE_HOST = "https://cdn-images-1.medium.com/fit/c"
MAX_WIDTH = 800
getter src : String getter originalHeight : Int32
getter originalWidth : Int32
def initialize(src : String) def initialize(@src : String, @originalWidth : Int32, @originalHeight : Int32)
@src = "#{IMAGE_HOST}/#{src}"
end end
def ==(other : Image) def ==(other : Image)
other.src == src other.src == src
end end
def src
[IMAGE_HOST, width, height, @src].join("/")
end
def width
[originalWidth, MAX_WIDTH].min.to_s
end
def height
if originalWidth > MAX_WIDTH
(originalHeight * ratio).round.to_i.to_s
else
originalHeight.to_s
end
end
private def ratio
MAX_WIDTH / originalWidth
end
def empty? def empty?
false false
end end