jekyll-og-image 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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