Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate social images #1725

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
### In cases where you want to specify the cache key, enable the above 2 inputs
### Follows the format here https://github.com/actions/cache
#
custom_opts: '--future --config _config.yml,_only_latest_guides_config.yml'
custom_opts: '--future --config _config.yml,_only_latest_guides_config.yml,_nosocialimages_config.yml'
### If you need to specify any Jekyll build options, enable the above input
### Flags accepted can be found here https://jekyllrb.com/docs/configuration/options/#build-command-options

Expand Down
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ gem "wdm", "~> 0.1.0" if Gem.win_platform?


gem "webrick", "~> 1.7"

# Used in _plugins/social_images.rb
gem "chunky_png", "~> 1.4.0"
gem 'rsvg2', '~> 4.1.7'
gem "cairo", "~> 1.17.9"
gem "rake", "~> 13.0.1"
72 changes: 52 additions & 20 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
asciidoctor (2.0.15)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
asciidoctor (2.0.20)
cairo (1.17.9)
native-package-installer (>= 1.0.3)
pkg-config (>= 1.2.2)
red-colors
cairo-gobject (4.1.7)
cairo (>= 1.16.2)
glib2 (= 4.1.7)
chunky_png (1.4.0)
colorator (1.1.0)
concurrent-ruby (1.1.8)
em-websocket (0.5.2)
concurrent-ruby (1.2.2)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.15.0)
ffi (1.15.5)
fiddle (1.1.1)
forwardable-extended (2.6.0)
http_parser.rb (0.6.0)
i18n (1.8.10)
gdk_pixbuf2 (4.1.7)
gio2 (= 4.1.7)
gio2 (4.1.7)
fiddle
gobject-introspection (= 4.1.7)
glib2 (4.1.7)
native-package-installer (>= 1.0.3)
pkg-config (>= 1.3.5)
gobject-introspection (4.1.7)
glib2 (= 4.1.7)
http_parser.rb (0.8.0)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
jekyll (4.1.1)
addressable (~> 2.4)
Expand All @@ -35,57 +54,70 @@ GEM
jekyll-asciidoc (3.0.0)
asciidoctor (>= 1.5.0)
jekyll (>= 3.0.0)
jekyll-feed (0.15.1)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-paginate-v2 (3.0.0)
jekyll (>= 3.0, < 5.0)
jekyll-sass-converter (2.1.0)
jekyll-sass-converter (2.2.0)
sassc (> 2.0.1, < 3.0)
jekyll-seo-tag (2.7.1)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (2.3.1)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.5.1)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
matrix (0.4.2)
mercenary (0.4.0)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
native-package-installer (1.1.5)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.6)
rb-fsevent (0.11.0)
pkg-config (1.5.1)
public_suffix (5.0.1)
rake (13.0.6)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
red-colors (0.3.0)
matrix
rexml (3.2.5)
rouge (3.26.0)
rouge (3.30.0)
rsvg2 (4.1.7)
cairo-gobject (= 4.1.7)
gdk_pixbuf2 (= 4.1.7)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
unicode-display_width (1.7.0)
webrick (1.7.0)
unicode-display_width (1.8.0)
webrick (1.8.1)

PLATFORMS
ruby

DEPENDENCIES
cairo (~> 1.17.9)
chunky_png (~> 1.4.0)
jekyll (~> 4.1.1)
jekyll-archives
jekyll-asciidoc
jekyll-feed (~> 0.6)
jekyll-paginate-v2
minima (~> 2.0)
rake (~> 13.0.1)
rsvg2 (~> 4.1.7)
tzinfo-data
webrick (~> 1.7)

BUNDLED WITH
2.2.16
2.4.10
2 changes: 1 addition & 1 deletion _layouts/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<meta property="og:url" content="{{ page.url | prepend: site.url }}" />
<meta property="og:title" content="{{ page.title }}{{ page_title_version_suffix }}" />
<meta property="og:description" content="{% if page.description %}{{ page.description }}{% else %}{{ site.description }}{% endif %}" />
<meta property="og:image" content="{{ '/assets/images/quarkus_card.png' | prepend: site.url }}" />
<meta property="og:image" content="{{ page.social_image | social_image: page.path | prepend: site.url }}" />
{% if page.layout == 'guides' or page.layout == 'guides-index' %}
{%assign canonical_url = page.url | replace_regex: '^/version/[^/]+', '' %}
{% else %}
Expand Down
1 change: 1 addition & 0 deletions _nosocialimages_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
skip_social_images: true
Binary file added _plugins/assets/quarkus_card_blank.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 154 additions & 0 deletions _plugins/social_images.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
require 'chunky_png'
require 'cairo'
require 'rsvg2'

module Jekyll
# Generates social images for blog posts and guides
module SocialImages
def social_image(text, page_path)
# If text is not empty, return it
if text.nil? || text.empty?
if File.exist?("./assets/images/social/#{File.basename(page_path, '.adoc')}.png")
return "/assets/images/social/#{File.basename(page_path, '.adoc')}.png"
else
return "/assets/images/quarkus_card.png"
end
else
text
end
end
end

class GenerateSocialImagesGenerator < Generator
def generate(site)
# Check if skip_social_images is set to true
# If so, skip generating social images
# This is useful when running the site locally
if site.config['skip_social_images']
Jekyll.logger.info('Skipping social image generation')
return
end
generate_images(Dir.glob(File.join(site.source, '_posts', '*.adoc')), site)
generate_images(Dir.glob(File.join(site.source, '_guides', '*.adoc')), site)
end

def split_text_into_lines(text)
lines = []
words = text.split(' ')
current_line = ''

words.each do |word|
if current_line.length + word.length <= 32
current_line += (current_line == '' ? '' : ' ') + word
else
lines.push(current_line)
current_line = word
end
end

lines.push(current_line) unless current_line.empty?

lines
end

private

def generate_images(files, site)
output_dir = 'assets/images/social'
FileUtils.mkdir_p(File.join(site.source, output_dir))

files.each do |guide_file|
basename = File.basename(guide_file, '.adoc')
if basename.start_with?('_')
next
end
title = extract_title(guide_file)
# Skip if title is empty
if (title.nil? || title.empty?)
next
end
output_file = File.join(site.source, output_dir, "#{basename}.png")
# Skip if the file already exists
if File.exist?(output_file)
next
end

Jekyll.logger.info("Generating social image for '#{title}' in #{output_file}")

# Generate the SVG image
svg_image_str = generate_svg_string(title)

# Create a Cairo surface and context for the PNG image (must be smaller than 600x330)
surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, 600, 250)
context = Cairo::Context.new(surface)

# Load and render the SVG onto the Cairo context
svg = RSVG::Handle.new_from_data(svg_image_str)
context.render_rsvg_handle(svg)

# Save the Cairo surface to a PNG file
b = StringIO.new
surface.write_to_png(b)

# Compose the generated image with the template image
png_image = ChunkyPNG::Image.from_file('_plugins/assets/quarkus_card_blank.png')
# Change the last parameters to change the position of the generated image
png_image.compose!(ChunkyPNG::Image.from_blob(b.string), 0, 80)

# Save the composed image to the output file
png_image.save(output_file)
end
end

def generate_svg_string(title)
idx = 90
font_size = 30
tspan_elements = ''
# Sanitize title
title = title.gsub(/&/, '&amp;')
title = title.gsub(/</, '&lt;')
title = title.gsub(/>/, '&gt;')

split_text_into_lines(title).each_with_index do |line, index|
tspan_elements += "<tspan x='50%' y='#{idx}'>#{line}</tspan>"
idx += font_size + 10
end
"
<svg width=\"600\" height=\"330\">
<style>
.title { fill: white; font-size: #{font_size}px; font-weight: bold; font-family:'Open Sans'}
</style>
<text x=\"50%\" y=\"50%\" text-anchor=\"middle\" class=\"title\" >
#{tspan_elements}
</text>
</svg>
"
end

def extract_title(adoc_file)
line_nr = 0
File.readlines(adoc_file).each do |line|
if line_nr == 0
# If line does not start with --- break
unless line.strip.start_with?('---')
break
end
end
if line_nr > 0 && line.strip.start_with?('---')
break;
end
if line.strip.start_with?('title:')
title = line.strip.sub('title:', '').strip
# Remove quotes
title = title.gsub(/\A[\"']|[\"']\z/, '')
return title
end
line_nr += 1
end
doc = Asciidoctor.load_file(adoc_file, header_only: true, logger: NullLogger.new)
doc.doctitle
end
end
end

Liquid::Template.register_filter(Jekyll::SocialImages)
Loading