Skip to content

Commit 6983902

Browse files
committed
Generate social images
- Fixes #1723
1 parent 393de61 commit 6983902

File tree

5 files changed

+160
-2
lines changed

5 files changed

+160
-2
lines changed

Diff for: Gemfile

+5
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,8 @@ gem "wdm", "~> 0.1.0" if Gem.win_platform?
3535

3636

3737
gem "webrick", "~> 1.7"
38+
39+
# Used in _plugins/social_images.rb
40+
gem "chunky_png"
41+
gem "cairo"
42+
gem "rsvg2"

Diff for: Gemfile.lock

+31-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,33 @@ GEM
44
addressable (2.8.0)
55
public_suffix (>= 2.0.2, < 5.0)
66
asciidoctor (2.0.15)
7+
cairo (1.17.9)
8+
native-package-installer (>= 1.0.3)
9+
pkg-config (>= 1.2.2)
10+
red-colors
11+
cairo-gobject (4.1.7)
12+
cairo (>= 1.16.2)
13+
glib2 (= 4.1.7)
14+
chunky_png (1.4.0)
715
colorator (1.1.0)
816
concurrent-ruby (1.1.8)
917
em-websocket (0.5.2)
1018
eventmachine (>= 0.12.9)
1119
http_parser.rb (~> 0.6.0)
1220
eventmachine (1.2.7)
1321
ffi (1.15.0)
22+
fiddle (1.1.1)
1423
forwardable-extended (2.6.0)
24+
gdk_pixbuf2 (4.1.7)
25+
gio2 (= 4.1.7)
26+
gio2 (4.1.7)
27+
fiddle
28+
gobject-introspection (= 4.1.7)
29+
glib2 (4.1.7)
30+
native-package-installer (>= 1.0.3)
31+
pkg-config (>= 1.3.5)
32+
gobject-introspection (4.1.7)
33+
glib2 (= 4.1.7)
1534
http_parser.rb (0.6.0)
1635
i18n (1.8.10)
1736
concurrent-ruby (~> 1.0)
@@ -53,19 +72,27 @@ GEM
5372
listen (3.5.1)
5473
rb-fsevent (~> 0.10, >= 0.10.3)
5574
rb-inotify (~> 0.9, >= 0.9.10)
75+
matrix (0.4.2)
5676
mercenary (0.4.0)
5777
minima (2.5.1)
5878
jekyll (>= 3.5, < 5.0)
5979
jekyll-feed (~> 0.9)
6080
jekyll-seo-tag (~> 2.1)
81+
native-package-installer (1.1.5)
6182
pathutil (0.16.2)
6283
forwardable-extended (~> 2.6)
84+
pkg-config (1.5.1)
6385
public_suffix (4.0.6)
6486
rb-fsevent (0.11.0)
6587
rb-inotify (0.10.1)
6688
ffi (~> 1.0)
89+
red-colors (0.3.0)
90+
matrix
6791
rexml (3.2.5)
6892
rouge (3.26.0)
93+
rsvg2 (4.1.7)
94+
cairo-gobject (= 4.1.7)
95+
gdk_pixbuf2 (= 4.1.7)
6996
safe_yaml (1.0.5)
7097
sassc (2.4.0)
7198
ffi (~> 1.9)
@@ -78,14 +105,17 @@ PLATFORMS
78105
ruby
79106

80107
DEPENDENCIES
108+
cairo
109+
chunky_png
81110
jekyll (~> 4.1.1)
82111
jekyll-archives
83112
jekyll-asciidoc
84113
jekyll-feed (~> 0.6)
85114
jekyll-paginate-v2
86115
minima (~> 2.0)
116+
rsvg2
87117
tzinfo-data
88118
webrick (~> 1.7)
89119

90120
BUNDLED WITH
91-
2.2.16
121+
2.3.7

Diff for: _layouts/base.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<meta property="og:url" content="{{ page.url | prepend: site.url }}" />
3535
<meta property="og:title" content="{{ page.title }}{{ page_title_version_suffix }}" />
3636
<meta property="og:description" content="{% if page.description %}{{ page.description }}{% else %}{{ site.description }}{% endif %}" />
37-
<meta property="og:image" content="{{ '/assets/images/quarkus_card.png' | prepend: site.url }}" />
37+
<meta property="og:image" content="{{ page.social_image | social_image: page.path | prepend: site.url }}" />
3838
{% if page.layout == 'guides' or page.layout == 'guides-index' %}
3939
{%assign canonical_url = page.url | replace_regex: '^/version/[^/]+', '' %}
4040
{% else %}

Diff for: _plugins/assets/quarkus_card_blank.png

237 KB
Loading

Diff for: _plugins/social_images.rb

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
require 'chunky_png'
2+
require 'cairo'
3+
require 'rsvg2'
4+
5+
module Jekyll
6+
# Generates social images for blog posts and guides
7+
module SocialImages
8+
def social_image(text, page_path)
9+
# If text is not empty, return it
10+
if text.nil? || text.empty?
11+
# If page_path contains "guides/", return the social image path
12+
if page_path.include?('guides/')
13+
return "/assets/images/social/#{File.basename(page_path, '.adoc')}.png"
14+
else
15+
return "/assets/images/quarkus_card.png"
16+
end
17+
else
18+
text
19+
end
20+
end
21+
end
22+
23+
class GenerateSocialImagesGenerator < Generator
24+
def generate(site)
25+
guides = Dir.glob(File.join(site.source, '_guides', '*.adoc'))
26+
output_dir = 'assets/images/social'
27+
FileUtils.mkdir_p(File.join(site.dest, output_dir))
28+
29+
guides.each do |guide_file|
30+
basename = File.basename(guide_file, '.adoc')
31+
if basename.start_with?('_')
32+
next
33+
end
34+
title = extract_title(guide_file)
35+
output_file = File.join(site.dest, output_dir, "#{basename}.png")
36+
# Skip if the file already exists
37+
if File.exist?(output_file)
38+
next
39+
end
40+
41+
# Generate the SVG image
42+
svg_image_str = generate_svg_string(title)
43+
44+
# Create a Cairo surface and context for the PNG image (must be smaller than 600x330)
45+
surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, 600, 250)
46+
context = Cairo::Context.new(surface)
47+
48+
# Load and render the SVG onto the Cairo context
49+
svg = RSVG::Handle.new_from_data(svg_image_str)
50+
context.render_rsvg_handle(svg)
51+
52+
# Save the Cairo surface to a PNG file
53+
b = StringIO.new
54+
surface.write_to_png(b)
55+
56+
# Compose the generated image with the template image
57+
png_image = ChunkyPNG::Image.from_file('_plugins/assets/quarkus_card_blank.png')
58+
# Change the last parameters to change the position of the generated image
59+
png_image.compose!(ChunkyPNG::Image.from_blob(b.string), 0, 80)
60+
61+
Jekyll.logger.info("Generating social image to #{output_file}")
62+
# Save the composed image to the output file
63+
png_image.save(output_file)
64+
end
65+
end
66+
67+
def split_text_into_lines(text)
68+
lines = []
69+
words = text.split(' ')
70+
current_line = ''
71+
72+
words.each do |word|
73+
if current_line.length + word.length <= 32
74+
current_line += (current_line == '' ? '' : ' ') + word
75+
else
76+
lines.push(current_line)
77+
current_line = word
78+
end
79+
end
80+
81+
lines.push(current_line) unless current_line.empty?
82+
83+
lines
84+
end
85+
86+
private
87+
88+
def generate_svg_string(title)
89+
idx = 90
90+
font_size = 30
91+
tspan_elements = ''
92+
split_text_into_lines(title).each_with_index do |line, index|
93+
tspan_elements += "<tspan x='50%' y='#{idx}'>#{line}</tspan>"
94+
idx += font_size + 10
95+
end
96+
"
97+
<svg width=\"600\" height=\"330\">
98+
<style>
99+
.title { fill: white; font-size: #{font_size}px; font-weight: bold; font-family:'Open Sans'}
100+
</style>
101+
<text x=\"50%\" y=\"50%\" text-anchor=\"middle\" class=\"title\" >
102+
#{tspan_elements}
103+
</text>
104+
</svg>
105+
"
106+
end
107+
108+
def extract_title(adoc_file)
109+
title = ''
110+
File.open(adoc_file, 'r') do |file|
111+
file.each_line do |line|
112+
if line.start_with? '='
113+
title = line[2..].strip
114+
break
115+
end
116+
end
117+
end
118+
title
119+
end
120+
end
121+
end
122+
123+
Liquid::Template.register_filter(Jekyll::SocialImages)

0 commit comments

Comments
 (0)