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 +4 -4
- data/README.md +45 -2
- data/lib/jekyll_og_image/configuration.rb +27 -3
- data/lib/jekyll_og_image/generator.rb +97 -35
- data/lib/jekyll_og_image/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7498d6e0fca6a90a0c70efb97a4514293b396e20af0900cfa5c734d88ed2cbf0
|
|
4
|
+
data.tar.gz: fea25610cb98a2b2954440c796800087a5379142b796c503ac7822034950fb17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
[](https://badge.fury.io/rb/jekyll-og-image)
|
|
6
6
|
[](https://github.com/igor-alexandrov/jekyll-og-image/actions/workflows/lint.yml)
|
|
7
|
-
[](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` –
|
|
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
|

|
|
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 =
|
|
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,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
|
132
|
-
|
|
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
|
-
|
|
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(
|
|
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:
|
|
171
|
+
) { |_canvas, _text| { x: 80, y: config.margin_bottom } }
|
|
143
172
|
end
|
|
144
173
|
|
|
145
|
-
def
|
|
146
|
-
|
|
147
|
-
|
|
174
|
+
def metadata_text_for(item, config)
|
|
175
|
+
metadata_parts = []
|
|
176
|
+
metadata_width = metadata_width_for(config)
|
|
148
177
|
|
|
149
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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,
|
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.
|
|
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:
|
|
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: []
|