jekyll-og-image 1.0.3 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e885b900b05e7a8b46a221a0850e682815081837e102044dfa02141d5c86b41c
4
- data.tar.gz: 7665d1a4ce49f9a422d1823513f1c6a5cc4a0bbb7e532904288ad3777e95129d
3
+ metadata.gz: 9fc4622681cf9b4953d6cbd7de3b661ebd217de44c08205656e395386b14e4c9
4
+ data.tar.gz: 26b864c6981a1b353de1e8e97de690870e2f2d9c531d56dac1bf97bc2efc0cde
5
5
  SHA512:
6
- metadata.gz: abf1c34206e4f72af335710c9c2d3178039057208b8a123d640e2a270bb48e19c1a205c8ce41e18a0bc2b5d58b2c8bc33a37a5052d706d44d9b6035548a0e62e
7
- data.tar.gz: e08fb4973bacb416395934631f63b8d4e293fa73d6e7a360b1126d4403851e73084aa6443ab979b1070a090faebb4919a27d2d61c0461fd156f3e82a28a5179f
6
+ metadata.gz: 7285bbd3562d0e2fa67a1f92c639d2ffa505e73d0d6b58c705217e8e7eefbb5930b9c01fec2c5d267f3448cd012e76ed5f2748b639d62bde290d796ab87d31d8
7
+ data.tar.gz: beb92471fe217bc7c105fb3924940fee58031306cf628aac36ff8a4b61841ebaee406c8a85ce6aa903563fe3ca8dd489d68d0b3018f9797e77cb76da209d08ef
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  A Jekyll plugin to automatically generate open graph images for posts.
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/jekyll-og-image.svg)](https://badge.fury.io/rb/jekyll-og-image)
5
6
  [![Lint](https://github.com/igor-alexandrov/jekyll-og-image/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/igor-alexandrov/jekyll-og-image/actions/workflows/lint.yml)
6
7
  [![Specs](https://github.com/igor-alexandrov/jekyll-og-image/actions/workflows/specs.yml/badge.svg?branch=main)](https://github.com/igor-alexandrov/jekyll-og-image/actions/workflows/specs.yml)
7
8
 
@@ -46,6 +47,7 @@ og_image:
46
47
  ```yaml
47
48
  og_image:
48
49
  output_dir: "assets/images/og"
50
+ image: "/assets/images/igor.jpeg"
49
51
  domain: "igor.works"
50
52
  border_bottom:
51
53
  width: 20
data/examples/1.png CHANGED
Binary file
data/examples/2.png CHANGED
Binary file
@@ -5,12 +5,15 @@ require "anyway_config"
5
5
  class JekyllOgImage::Config < Anyway::Config
6
6
  attr_config output_dir: "assets/images/og"
7
7
  attr_config force: false
8
+ attr_config :image
8
9
  attr_config :domain
9
10
  attr_config border_bottom: {
10
11
  width: 20,
11
12
  fill: [ "#211F1F", "#F4CBB2", "#AD5C51", "#9CDAF1", "#7DBBE6" ]
12
13
  }
13
14
 
15
+ coerce_types image: { type: :string }
16
+
14
17
  coerce_types border_bottom: {
15
18
  width: { type: :integer },
16
19
  fill: { type: :string, array: true }
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class JekyllOgImage::Element::Base
4
+ VALID_GRAVITY = %i[nw ne sw se].freeze
5
+
4
6
  private
5
7
 
6
8
  def hex_to_rgb(input)
@@ -13,4 +15,26 @@ class JekyllOgImage::Element::Base
13
15
  raise ArgumentError, "Unknown input #{input.inspect}"
14
16
  end
15
17
  end
18
+
19
+ def validate_gravity!
20
+ unless VALID_GRAVITY.include?(@gravity)
21
+ raise ArgumentError, "Invalid gravity: #{@gravity.inspect}"
22
+ end
23
+ end
24
+
25
+ def composite_with_gravity(canvas, overlay, x, y)
26
+ if gravity_nw?
27
+ canvas.composite(overlay, :over, x: [ x ], y: [ y ]).flatten
28
+ elsif gravity_ne?
29
+ x = canvas.width - overlay.width - x
30
+ canvas.composite(overlay, :over, x: [ x ], y: [ y ]).flatten
31
+ elsif gravity_sw?
32
+ y = canvas.height - overlay.height - y
33
+ canvas.composite(overlay, :over, x: [ x ], y: [ y ]).flatten
34
+ elsif gravity_se?
35
+ x = canvas.width - overlay.width - x
36
+ y = canvas.height - overlay.height - y
37
+ canvas.composite(overlay, :over, x: [ x ], y: [ y ]).flatten
38
+ end
39
+ end
16
40
  end
@@ -4,43 +4,57 @@ class JekyllOgImage::Element::Border < JekyllOgImage::Element::Base
4
4
  class Part < Data.define(:rgb, :width, :height, :offset)
5
5
  end
6
6
 
7
- def initialize(canvas, size, position: :bottom, fill: "#000000")
8
- @canvas = canvas
7
+ def initialize(size, position: :bottom, fill: "#000000")
9
8
  @size = size
10
9
  @position = position
11
10
  @fill = fill
12
11
 
13
12
  validate_position!
13
+ end
14
14
 
15
- @border = Vips::Image.black(*dimensions)
15
+ def apply_to(canvas, &block)
16
+ border = Vips::Image.black(*dimensions(canvas))
16
17
 
17
- parts.each.with_index do |part, index|
18
- border = Vips::Image.black(part.width, part.height).ifthenelse([ 0, 0, 0 ], part.rgb)
18
+ parts(canvas).each.with_index do |part, index|
19
+ image = Vips::Image.black(part.width, part.height).ifthenelse([ 0, 0, 0 ], part.rgb)
19
20
 
20
21
  if vertical?
21
- @border = @border.composite(border, :over, x: [ 0 ], y: [ part.offset ]).flatten
22
+ border = border.composite(image, :over, x: [ 0 ], y: [ part.offset ]).flatten
22
23
  else
23
- @border = @border.composite(border, :over, x: [ part.offset ], y: [ 0 ]).flatten
24
+ border = border.composite(image, :over, x: [ part.offset ], y: [ 0 ]).flatten
24
25
  end
25
26
  end
26
- end
27
27
 
28
- def apply(&block)
29
- result = block.call(@canvas, @border) if block_given?
28
+ result = block.call(canvas, border) if block_given?
29
+
30
30
  x, y = result ? [ result.fetch(:x, 0), result.fetch(:y, 0) ] : [ 0, 0 ]
31
31
 
32
32
  if vertical?
33
- x = @position == :left ? x : @canvas.width - @size - x
34
- @canvas.composite(@border, :over, x: [ x ], y: [ 0 ]).flatten
33
+ x = @position == :left ? x : canvas.width - @size - x
34
+ canvas.composite(border, :over, x: [ x ], y: [ 0 ]).flatten
35
35
  else
36
- y = @position == :top ? y : @canvas.height - @size - y
37
- @canvas.composite(@border, :over, x: [ 0 ], y: [ y ]).flatten
36
+ y = @position == :top ? y : canvas.height - @size - y
37
+ canvas.composite(border, :over, x: [ 0 ], y: [ y ]).flatten
38
38
  end
39
39
  end
40
40
 
41
- def parts
41
+ private
42
+
43
+ def hex_to_rgb(hex)
44
+ hex.match(/#(..)(..)(..)/)[1..3].map { |x| x.hex }
45
+ end
46
+
47
+ def dimensions(canvas)
48
+ if vertical?
49
+ [ @size, canvas.height ]
50
+ else
51
+ [ canvas.width, @size ]
52
+ end
53
+ end
54
+
55
+ def parts(canvas)
42
56
  if @fill.is_a?(Array)
43
- width, height = vertical? ? [ @size, (@canvas.height / @fill.size) ] : [ (@canvas.width / @fill.size), @size ]
57
+ width, height = vertical? ? [ @size, (canvas.height / @fill.size) ] : [ (canvas.width / @fill.size), @size ]
44
58
 
45
59
  @fill.map.with_index do |item, index|
46
60
  Part.new(
@@ -51,26 +65,12 @@ class JekyllOgImage::Element::Border < JekyllOgImage::Element::Base
51
65
  )
52
66
  end
53
67
  else
54
- length = vertical? ? @canvas.height : @canvas.width
68
+ length = vertical? ? canvas.height : canvas.width
55
69
 
56
70
  [ Part.new(rgb: hex_to_rgb(@fill), length: length, offset: 0) ]
57
71
  end
58
72
  end
59
73
 
60
- private
61
-
62
- def hex_to_rgb(hex)
63
- hex.match(/#(..)(..)(..)/)[1..3].map { |x| x.hex }
64
- end
65
-
66
- def dimensions
67
- if vertical?
68
- [ @size, @canvas.height ]
69
- else
70
- [ @canvas.width, @size ]
71
- end
72
- end
73
-
74
74
  def vertical?
75
75
  @position == :left || @position == :right
76
76
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "vips"
4
+
5
+ class JekyllOgImage::Element::Canvas < JekyllOgImage::Element::Base
6
+ def initialize(width, height, color: "#ffffff")
7
+ @canvas = Vips::Image.black(width, height).ifthenelse([ 0, 0, 0 ], hex_to_rgb(color))
8
+ end
9
+
10
+ def image(source, **opts, &block)
11
+ image = JekyllOgImage::Element::Image.new(source, **opts)
12
+ @canvas = image.apply_to(@canvas, &block)
13
+
14
+ self
15
+ end
16
+
17
+ def text(message, **opts, &block)
18
+ text = JekyllOgImage::Element::Text.new(message, **opts)
19
+ @canvas = text.apply_to(@canvas, &block)
20
+
21
+ self
22
+ end
23
+
24
+ def border(width, **opts, &block)
25
+ border = JekyllOgImage::Element::Border.new(width, **opts)
26
+ @canvas = border.apply_to(@canvas, &block)
27
+
28
+ self
29
+ end
30
+
31
+ def save(filename)
32
+ @canvas.write_to_file(filename)
33
+ end
34
+ end
@@ -1,33 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "vips"
4
-
5
3
  class JekyllOgImage::Element::Image < JekyllOgImage::Element::Base
6
- def initialize(width, height, color: "#ffffff")
7
- @canvas = Vips::Image.black(width, height).ifthenelse([ 0, 0, 0 ], hex_to_rgb(color))
4
+ def initialize(source, gravity: :nw, width:, height:, radius:)
5
+ @gravity = gravity
6
+ @source = source
7
+ @width = width
8
+ @height = height
9
+ @radius = radius
10
+
11
+ validate_gravity!
8
12
  end
9
13
 
10
- def text(message, **opts, &block)
11
- text = JekyllOgImage::Element::Text.new(
12
- @canvas, message, **opts
13
- )
14
+ def apply_to(canvas, &block)
15
+ image = Vips::Image.new_from_buffer(@source, "")
16
+ image = round_corners(image) if @radius
17
+
18
+ result = block.call(canvas, image) if block_given?
19
+
20
+ if @width && @height
21
+ ratio = calculate_ratio(image, @width, @height, :min)
22
+ image = image.resize(ratio)
23
+ end
14
24
 
15
- @canvas = text.apply(&block)
25
+ x, y = result ? [ result.fetch(:x, 0), result.fetch(:y, 0) ] : [ 0, 0 ]
16
26
 
17
- self
27
+ composite_with_gravity(canvas, image, x, y)
18
28
  end
19
29
 
20
- def border(width, position: :bottom, fill: "#000000")
21
- @canvas = JekyllOgImage::Element::Border.new(
22
- @canvas, width,
23
- position: position,
24
- fill: fill
25
- ).apply
30
+ private
31
+
32
+ VALID_GRAVITY.each do |gravity|
33
+ define_method("gravity_#{gravity}?") do
34
+ @gravity == gravity
35
+ end
36
+ end
37
+
38
+ def round_corners(image)
39
+ mask = %(
40
+ <svg viewBox="0 0 #{image.width} #{image.height}">
41
+ <rect
42
+ rx="#{@radius}"
43
+ ry="#{@radius}"
44
+ x="0"
45
+ y="0"
46
+ width="#{image.width}"
47
+ height="#{image.height}"
48
+ fill="#fff"
49
+ />
50
+ </svg>
51
+ )
26
52
 
27
- self
53
+ mask = Vips::Image.new_from_buffer(mask, "")
54
+ image.bandjoin mask[3]
28
55
  end
29
56
 
30
- def save(filename)
31
- @canvas.write_to_file(filename)
57
+ def calculate_ratio(image, width, height, mode)
58
+ if mode == :min
59
+ [ width.to_f / image.width, height.to_f / image.height ].min
60
+ else
61
+ [ width.to_f / image.width, height.to_f / image.height ].max
62
+ end
32
63
  end
33
64
  end
@@ -1,45 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class JekyllOgImage::Element::Text < JekyllOgImage::Element::Base
4
- VALID_GRAVITY = %i[nw ne sw se].freeze
5
-
6
- def initialize(canvas, message, gravity: :nw, width: nil, dpi: nil, color: "#000000", font: nil)
7
- @canvas = canvas
4
+ def initialize(message, gravity: :nw, width: nil, dpi: nil, color: "#000000", font: nil)
5
+ @message = message
8
6
  @gravity = gravity
7
+ @width = width
8
+ @dpi = dpi
9
+ @color = color
10
+ @font = font
9
11
 
10
12
  validate_gravity!
13
+ end
11
14
 
12
- text = Vips::Image.text(message,
13
- font: font,
14
- width: width,
15
- dpi: dpi,
15
+ def apply_to(canvas, &block)
16
+ text = Vips::Image.text(@message,
17
+ font: @font,
18
+ width: @width,
19
+ dpi: @dpi,
16
20
  wrap: :word,
17
21
  align: :low
18
22
  )
19
23
 
20
- @text = text
21
- .new_from_image(hex_to_rgb(color))
24
+ text = text
25
+ .new_from_image(hex_to_rgb(@color))
22
26
  .copy(interpretation: :srgb)
23
27
  .bandjoin(text)
24
- end
25
28
 
26
- def apply(&block)
27
- result = block.call(@canvas, @text) if block_given?
29
+ result = block.call(canvas, text) if block_given?
30
+
28
31
  x, y = result ? [ result.fetch(:x, 0), result.fetch(:y, 0) ] : [ 0, 0 ]
29
32
 
30
- if gravity_nw?
31
- @canvas.composite(@text, :over, x: [ x ], y: [ y ]).flatten
32
- elsif gravity_ne?
33
- x = @canvas.width - @text.width - x
34
- @canvas.composite(@text, :over, x: [ x ], y: [ y ]).flatten
35
- elsif gravity_sw?
36
- y = @canvas.height - @text.height - y
37
- @canvas.composite(@text, :over, x: [ x ], y: [ y ]).flatten
38
- elsif gravity_se?
39
- x = @canvas.width - @text.width - x
40
- y = @canvas.height - @text.height - y
41
- @canvas.composite(@text, :over, x: [ x ], y: [ y ]).flatten
42
- end
33
+ composite_with_gravity(canvas, text, x, y)
43
34
  end
44
35
 
45
36
  private
@@ -49,10 +40,4 @@ class JekyllOgImage::Element::Text < JekyllOgImage::Element::Base
49
40
  @gravity == gravity
50
41
  end
51
42
  end
52
-
53
- def validate_gravity!
54
- unless VALID_GRAVITY.include?(@gravity)
55
- raise ArgumentError, "Invalid gravity: #{@gravity.inspect}"
56
- end
57
- end
58
43
  end
@@ -35,37 +35,49 @@ class JekyllOgImage::Generator < Jekyll::Generator
35
35
  def generate_image_for_post(site, post, path)
36
36
  date = post.date.strftime("%B %d, %Y")
37
37
 
38
- image = JekyllOgImage::Element::Image.new(1200, 600)
38
+ canvas = JekyllOgImage::Element::Canvas.new(1200, 600)
39
39
  .border(JekyllOgImage.config.border_bottom["width"],
40
40
  position: :bottom,
41
41
  fill: JekyllOgImage.config.border_bottom["fill"]
42
42
  )
43
- .text(post.data["title"], width: 1040, color: "#2f313d", dpi: 500, font: "Helvetica, Bold") do |_canvas, _text|
44
- {
45
- x: 80,
46
- y: 100
47
- }
48
- end
49
- .text(date, gravity: :sw, color: "#535358", dpi: 150, font: "Helvetica, Regular") do |_canvas, _text|
50
- {
51
- x: 80,
52
- y: post.data["tags"].any? ? 150 : 100
53
- }
54
- end
43
+
44
+ if JekyllOgImage.config.image
45
+ canvas = canvas.image(
46
+ File.read(File.join(site.config["source"], JekyllOgImage.config.image)),
47
+ gravity: :ne,
48
+ width: 150,
49
+ height: 150,
50
+ radius: 50
51
+ ) { |_canvas, _text| { x: 80, y: 100 } }
52
+ end
53
+
54
+ canvas = canvas.text(post.data["title"],
55
+ width: JekyllOgImage.config.image ? 870 : 1040,
56
+ color: "#2f313d",
57
+ dpi: 400,
58
+ font: "Helvetica, Bold"
59
+ ) { |_canvas, _text| { x: 80, y: 100 } }
60
+
61
+ canvas = canvas.text(date,
62
+ gravity: :sw,
63
+ color: "#535358",
64
+ dpi: 150,
65
+ font: "Helvetica, Regular"
66
+ ) { |_canvas, _text| { x: 80, y: post.data["tags"].any? ? 150 : 100 } }
55
67
 
56
68
  if post.data["tags"].any?
57
69
  tags = post.data["tags"].map { |tag| "##{tag}" }.join(" ")
58
70
 
59
- image = image.text(tags, gravity: :sw, color: "#535358", dpi: 150, font: "Helvetica, Regular") do |_canvas, _text|
60
- {
61
- x: 80,
62
- y: 100
63
- }
64
- end
71
+ canvas = canvas.text(tags,
72
+ gravity: :sw,
73
+ color: "#535358",
74
+ dpi: 150,
75
+ font: "Helvetica, Regular"
76
+ ) { |_canvas, _text| { x: 80, y: 100 } }
65
77
  end
66
78
 
67
79
  if JekyllOgImage.config.domain
68
- image = image.text(JekyllOgImage.config.domain, gravity: :se, color: "#535358", dpi: 150, font: "Helvetica, Regular") do |_canvas, _text|
80
+ canvas = canvas.text(JekyllOgImage.config.domain, gravity: :se, color: "#535358", dpi: 150, font: "Helvetica, Regular") do |_canvas, _text|
69
81
  {
70
82
  x: 80,
71
83
  y: post.data["tags"].any? ? 150 : 100
@@ -73,6 +85,6 @@ class JekyllOgImage::Generator < Jekyll::Generator
73
85
  end
74
86
  end
75
87
 
76
- image.save(path)
88
+ canvas.save(path)
77
89
  end
78
90
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllOgImage
4
- VERSION = "1.0.3"
4
+ VERSION = "1.0.5"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-og-image
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Alexandrov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-05 00:00:00.000000000 Z
11
+ date: 2024-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll-seo-tag
@@ -108,6 +108,7 @@ files:
108
108
  - lib/jekyll_og_image/config.rb
109
109
  - lib/jekyll_og_image/element/base.rb
110
110
  - lib/jekyll_og_image/element/border.rb
111
+ - lib/jekyll_og_image/element/canvas.rb
111
112
  - lib/jekyll_og_image/element/image.rb
112
113
  - lib/jekyll_og_image/element/text.rb
113
114
  - lib/jekyll_og_image/generator.rb