jekyll-og-image 2.0.0 → 2.1.0

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: ea51d4a05adcc40de23c17b92e31139f154176500960782aa592864c71cbd334
4
- data.tar.gz: a31cc539176deac7ad1e8fd0dea3b4fd45efd906a02d439766e8df82d80adceb
3
+ metadata.gz: 7498d6e0fca6a90a0c70efb97a4514293b396e20af0900cfa5c734d88ed2cbf0
4
+ data.tar.gz: fea25610cb98a2b2954440c796800087a5379142b796c503ac7822034950fb17
5
5
  SHA512:
6
- metadata.gz: 5e3929c8ddee908ae78375186bacffab40da5813432d4a563ab4f61b5767b011cf69959842a0e2f05f4b6d899029d08aec3c9d531154162ef4de6cb2a4ba4c99
7
- data.tar.gz: 2470a7946363160ebfebc258c5326089976b91a620d2bde2edc35c76149eec1a6d6f0d739812e8917e96b1910fa13a53c7c9e15198688207ebaef5f61a04cce2
6
+ metadata.gz: b6177ac6d1493b968a2eb2d33a13f84429d90be409839d2e08407a20e8b4296a5e9a50dc38d630f867ad113a931efb6fd9d6302d4a5732a167b82aec0e08dfa2
7
+ data.tar.gz: 3661a2e8dcb52b274d73d2ef713bc6fa365b9a3102bc652098bf7759e0babe3b6c253133fafdcc3c7c0ef6d57272da47c78d1d937ac111efa1abff4231d25860
data/README.md CHANGED
@@ -4,7 +4,7 @@ A Jekyll plugin to automatically generate open graph images for posts.
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/jekyll-og-image.svg)](https://badge.fury.io/rb/jekyll-og-image)
6
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)
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
+ [![Tests](https://github.com/igor-alexandrov/jekyll-og-image/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/igor-alexandrov/jekyll-og-image/actions/workflows/tests.yml)
8
8
 
9
9
  ## Installation
10
10
 
@@ -103,6 +103,8 @@ The following configuration options are available:
103
103
  * `header` – The header configuration options:
104
104
  * `font_family` – The font family of the header text. Default: `Helvetica, Bold`
105
105
  * `color` – The color of the header text. Default: `#2f313d`
106
+ * `prefix` – Text to prepend to the title. Default: `""`
107
+ * `suffix` – Text to append to the title. Default: `""`
106
108
 
107
109
  * `content` – The content configuration options:
108
110
  * `font_family` – The font family of the content text. Default: `Helvetica, Regular`
@@ -114,7 +116,18 @@ The following configuration options are available:
114
116
 
115
117
  * `domain` – The domain name to use in the image. Default: `nil`
116
118
 
117
- * `image` – Path to the image to use as the logo. Default: `nil`
119
+ * `image` – Logo/image configuration options. Can be a string (legacy format) or an object with the following options:
120
+ * `path` – Path to the image file. **Note: Use JPEG format for best compatibility. PNG images with transparency may not render correctly.** Default: `nil`
121
+ * `width` – The width of the logo in pixels. Default: `150`
122
+ * `height` – The height of the logo in pixels. Default: `150`
123
+ * `radius` – The radius for rounded corners on the logo. Default: `50`
124
+ * `position` – The position of the logo as `{x, y}` coordinates. Default: `{x: 80, y: 100}`
125
+ * `gravity` – The gravity anchor for logo positioning (nw, n, ne, w, e, sw, s, se). Default: `ne`
126
+
127
+ * `metadata` – The metadata configuration options:
128
+ * `fields` – Array of metadata fields to display. Available options: `"date"`, `"tags"`, or any custom front matter field. Default: `["date", "tags"]`
129
+ * `separator` – Text to separate multiple metadata fields. Default: `" • "`
130
+ * `date_format` – Date format string for the date field. Default: `"%B %d, %Y"`
118
131
 
119
132
  ## Examples
120
133
 
@@ -216,6 +229,36 @@ og_image:
216
229
 
217
230
  ![Example 4](examples/4.png)
218
231
 
232
+ ### Custom Metadata and Logo Size
233
+
234
+ ```yaml
235
+ # _config.yml
236
+ og_image:
237
+ domain: "igor.works"
238
+ image:
239
+ path: "/assets/images/logo.png"
240
+ width: 100
241
+ height: 100
242
+ radius: 20
243
+ position:
244
+ x: 60
245
+ y: 60
246
+ header:
247
+ prefix: "📖 "
248
+ suffix: " - My Blog"
249
+ metadata:
250
+ fields: ["author", "date", "reading_time"]
251
+ separator: " | "
252
+ date_format: "%d %B %Y"
253
+ ```
254
+
255
+ This configuration:
256
+ - Adjusts the logo size to 100x100px with less rounded corners
257
+ - Adds a book emoji prefix and " - My Blog" suffix to titles
258
+ - Shows author, date, and reading_time in the metadata section
259
+ - Uses a pipe separator between metadata fields
260
+ - Formats dates as "15 February 2024"
261
+
219
262
 
220
263
  ## Contributing
221
264
 
@@ -9,8 +9,8 @@ class JekyllOgImage::Configuration
9
9
  end
10
10
  end
11
11
 
12
- Header = Data.define(:font_family, :color) do
13
- def initialize(font_family: "Helvetica, Bold", color: "#2f313d")
12
+ Header = Data.define(:font_family, :color, :prefix, :suffix) do
13
+ def initialize(font_family: "Helvetica, Bold", color: "#2f313d", prefix: "", suffix: "")
14
14
  super
15
15
  end
16
16
  end
@@ -28,6 +28,18 @@ class JekyllOgImage::Configuration
28
28
  end
29
29
  end
30
30
 
31
+ Image = Data.define(:path, :width, :height, :radius, :position, :gravity) do
32
+ def initialize(path: nil, width: 150, height: 150, radius: 50, position: { x: 80, y: 100 }, gravity: :ne)
33
+ super
34
+ end
35
+ end
36
+
37
+ Metadata = Data.define(:fields, :separator, :date_format) do
38
+ def initialize(fields: [ "date", "tags" ], separator: " • ", date_format: "%B %d, %Y")
39
+ super
40
+ end
41
+ end
42
+
31
43
  def initialize(raw_config)
32
44
  @raw_config = raw_config
33
45
  end
@@ -86,7 +98,14 @@ class JekyllOgImage::Configuration
86
98
  end
87
99
 
88
100
  def image
89
- @raw_config["image"]
101
+ if @raw_config["image"].is_a?(String)
102
+ # Legacy support: if image is just a string, convert it to the new format
103
+ Image.new(path: @raw_config["image"])
104
+ elsif @raw_config["image"]
105
+ Image.new(**Jekyll::Utils.symbolize_hash_keys(@raw_config["image"]))
106
+ else
107
+ Image.new
108
+ end
90
109
  end
91
110
 
92
111
  def domain
@@ -100,4 +119,9 @@ class JekyllOgImage::Configuration
100
119
  def margin_bottom
101
120
  80 + (border_bottom&.width || 0)
102
121
  end
122
+
123
+
124
+ def metadata
125
+ @raw_config["metadata"] ? Metadata.new(**Jekyll::Utils.symbolize_hash_keys(@raw_config["metadata"])) : Metadata.new
126
+ end
103
127
  end
@@ -49,6 +49,8 @@ class JekyllOgImage::Generator < Jekyll::Generator
49
49
  Jekyll.logger.info "Jekyll Og Image:", "Skipping image generation for #{relative_image_path} as it already exists." if config.verbose?
50
50
  end
51
51
 
52
+ register_static_file(site, base_output_dir, image_filename, config) if File.exist?(absolute_image_path)
53
+
52
54
  item.data["image"] ||= {
53
55
  "path" => relative_image_path,
54
56
  "width" => JekyllOgImage.config.canvas.width,
@@ -74,6 +76,21 @@ class JekyllOgImage::Generator < Jekyll::Generator
74
76
  end
75
77
  end
76
78
 
79
+ def register_static_file(site, base_output_dir, image_filename, config)
80
+ relative_path = File.join("/", base_output_dir, image_filename)
81
+ return if site.static_files.any? { |file| file.relative_path == relative_path }
82
+
83
+ static_file = Jekyll::StaticFile.new(
84
+ site,
85
+ site.source,
86
+ base_output_dir,
87
+ image_filename
88
+ )
89
+
90
+ site.static_files << static_file
91
+ Jekyll.logger.info "Jekyll Og Image:", "Added #{base_output_dir}/#{image_filename} to static files" if config.verbose?
92
+ end
93
+
77
94
  def generate_image_for_document(site, item, path, base_config)
78
95
  config = base_config.merge!(item.data["og_image"] || {})
79
96
 
@@ -81,10 +98,9 @@ class JekyllOgImage::Generator < Jekyll::Generator
81
98
 
82
99
  canvas = generate_canvas(site, config)
83
100
  canvas = add_border_bottom(canvas, config) if config.border_bottom
84
- canvas = add_image(canvas, File.read(File.join(site.config["source"], config.image))) if config.image
101
+ canvas = add_image(canvas, File.read(File.join(site.config["source"], config.image.path)), config) if config.image.path
85
102
  canvas = add_header(canvas, item, config)
86
- canvas = add_publish_date(canvas, item, config)
87
- canvas = add_tags(canvas, item, config) if item.data["tags"]&.any?
103
+ canvas = add_metadata(canvas, item, config)
88
104
  canvas = add_domain(canvas, item, config) if config.domain
89
105
 
90
106
  canvas.save(path)
@@ -109,61 +125,107 @@ class JekyllOgImage::Generator < Jekyll::Generator
109
125
  )
110
126
  end
111
127
 
112
- def add_image(canvas, image)
113
- canvas.image(image,
114
- gravity: :ne,
115
- width: 150,
116
- height: 150,
117
- radius: 50
118
- ) { |_canvas, _text| { x: 80, y: 100 } }
128
+ def add_image(canvas, image_data, config)
129
+ image_config = config.image
130
+ canvas.image(image_data,
131
+ gravity: image_config.gravity,
132
+ width: image_config.width,
133
+ height: image_config.height,
134
+ radius: image_config.radius
135
+ ) { |_canvas, _text| { x: image_config.position[:x], y: image_config.position[:y] } }
119
136
  end
120
137
 
121
138
  def add_header(canvas, item, config)
122
139
  title = item.data["title"] || "Untitled"
123
- canvas.text(title,
124
- width: config.image ? 870 : 1040,
140
+ full_title = "#{config.header.prefix}#{title}#{config.header.suffix}"
141
+
142
+ # Calculate available width for header text to avoid overlap with image
143
+ header_width = if config.image.path
144
+ # Canvas width - left margin - right margin - image width - spacing
145
+ # 1200 - 80 - 80 - image_width - 30 (spacing)
146
+ 1040 - config.image.width - 30
147
+ else
148
+ 1040
149
+ end
150
+
151
+ canvas.text(full_title,
152
+ width: header_width,
125
153
  color: config.header.color,
126
154
  dpi: 400,
127
155
  font: config.header.font_family
128
156
  ) { |_canvas, _text| { x: 80, y: 100 } }
129
157
  end
130
158
 
131
- def add_publish_date(canvas, item, config)
132
- return canvas unless item.respond_to?(:date) && item.date
159
+ def add_metadata(canvas, item, config)
160
+ metadata_text = metadata_text_for(item, config)
161
+ return canvas if metadata_text.empty?
133
162
 
134
- date = item.date.strftime("%B %d, %Y")
135
- y_pos = (item.data["tags"]&.any? ? config.margin_bottom + 50 : config.margin_bottom)
163
+ metadata_width = metadata_width_for(config)
136
164
 
137
- canvas.text(date,
165
+ canvas.text(metadata_text,
138
166
  gravity: :sw,
167
+ width: metadata_width,
139
168
  color: config.content.color,
140
169
  dpi: 150,
141
170
  font: config.content.font_family
142
- ) { |_canvas, _text| { x: 80, y: y_pos } }
171
+ ) { |_canvas, _text| { x: 80, y: config.margin_bottom } }
143
172
  end
144
173
 
145
- def add_tags(canvas, item, config)
146
- tags_list = item.data["tags"]
147
- return canvas unless tags_list.is_a?(Array) && tags_list.any?
174
+ def metadata_text_for(item, config)
175
+ metadata_parts = []
176
+ metadata_width = metadata_width_for(config)
148
177
 
149
- tags = tags_list.map { |tag| "##{tag}" }.join(" ")
178
+ config.metadata.fields.each do |field|
179
+ metadata_value = metadata_value_for(item, field, config)
180
+ next if metadata_value.nil? || metadata_value.empty?
150
181
 
151
- canvas.text(tags,
152
- gravity: :sw,
153
- color: config.content.color,
154
- dpi: 150,
155
- font: config.content.font_family
156
- ) { |_canvas, _text| { x: 80, y: config.margin_bottom } }
182
+ if field == "description"
183
+ candidate_text = (metadata_parts + [ metadata_value ]).join(config.metadata.separator)
184
+ next unless metadata_text_fits_single_line?(candidate_text, metadata_width, config)
185
+ end
186
+
187
+ metadata_parts << metadata_value
188
+ end
189
+
190
+ metadata_parts.join(config.metadata.separator)
157
191
  end
158
192
 
159
- def add_domain(canvas, item, config)
160
- y_pos = if item.data["tags"]&.any?
161
- config.margin_bottom + 50
162
- # rubocop:disable Layout/ElseAlignment # Disabled due to RuboCop error in v3.3.0
163
- else
164
- # rubocop:enable Layout/ElseAlignment
165
- config.margin_bottom
193
+ def metadata_value_for(item, field, config)
194
+ case field
195
+ when "date"
196
+ item.respond_to?(:date) && item.date ? item.date.strftime(config.metadata.date_format) : nil
197
+ when "tags"
198
+ return nil unless item.data["tags"]&.is_a?(Array) && item.data["tags"].any?
199
+
200
+ item.data["tags"].map { |tag| "##{tag}" }.join(" ")
201
+ else
202
+ # Support custom fields from front matter
203
+ item.data[field]&.to_s
166
204
  end
205
+ end
206
+
207
+ def metadata_width_for(config)
208
+ config.domain ? 600 : 1040
209
+ end
210
+
211
+ def metadata_text_fits_single_line?(text, metadata_width, config)
212
+ options = {
213
+ width: metadata_width,
214
+ dpi: 150,
215
+ font: config.content.font_family,
216
+ align: :low
217
+ }
218
+ options[:wrap] = :word if Vips.at_least_libvips?(8, 14)
219
+
220
+ single_line_height = Vips::Image.text("Ay", **options).height
221
+ rendered_height = Vips::Image.text(text, **options).height
222
+
223
+ rendered_height <= (single_line_height * 1.6)
224
+ end
225
+
226
+
227
+ def add_domain(canvas, _item, config)
228
+ y_pos = config.margin_bottom
167
229
 
168
230
  canvas.text(config.domain,
169
231
  gravity: :se,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllOgImage
4
- VERSION = "2.0.0"
4
+ VERSION = "2.1.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-og-image
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Alexandrov
@@ -130,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
130
  - !ruby/object:Gem::Version
131
131
  version: '0'
132
132
  requirements: []
133
- rubygems_version: 3.6.7
133
+ rubygems_version: 4.0.3
134
134
  specification_version: 4
135
135
  summary: Jekyll plugin to generate GitHub-style open graph images
136
136
  test_files: []