Cała oryginalna treść jest tworzona po ukraińsku. Nie wszystkie treści zostały jeszcze przetłumaczone. Niektóre posty mogą być dostępne tylko po ukraińsku.Dowiedz się więcej

Generowanie obrazów Open Graph w Rails za pomocą szablonów SVG

Ta treść została automatycznie przetłumaczona z ukraińskiego.
Jako szablon do generowania obrazów JPG wybrałem SVG ze względu na prostotę i szybkość pracy z tym formatem. Na przykład HTML jako szablon daje więcej możliwości, ale jest bardziej skomplikowany w realizacji i utrzymaniu.
Zadanie - generacja obrazu JPG dla oznaczenia OpenGraph (to obrazek/podgląd dla niektórych mediów społecznościowych). Aplikacja Rails znajduje się na Heroku. Ilość RAM nie jest zbyt duża, więc zadanie generacji obrazu trzeba gdzieś przenieść. Konfigurowanie i płacenie za sidekiq dla projektu pet nie ma sensu. Dobrym rozwiązaniem dla mnie okazał się Heroku Scheduler. Ten standardowy (bezpłatny) dodatek będzie uruchamiał nasze zadanie rake zgodnie z harmonogramem (raz dziennie). A samo zadanie będzie znajdować posty, które jeszcze nie mają obrazu OG, i będzie je generować i dołączać (używane jest ActiveStorage, obraz zostanie przesłany na aws) do modelu.
Pierwsze, co trzeba zrobić, to stworzyć szablon SVG. Na początku bawimy się z SVG. Obraz OG ma mieć 1200px x 630px.
Oto mniej więcej jak będzie wyglądał szablon SVG:
<svg width="1200" height="630" viewBox="0 0 1200 630" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <!-- Tło -->
  <rect width="1200" height="630" fill="black"/>
  <rect x="56" y="56" width="1088" height="518" rx="13" stroke="white" fill="black" stroke-width="13"/>
  
  <!-- Tytuł -->
  <text x="160" y="150" fill="white" font-size="50px" font-weight="bold" line-height="1.2" margin="0">
    Tytuł artykułu
  </text>

  <!-- Autor -->
  <text x="160" y="510" fill="white" font-size="30px">
    Nazwa autora
  </text>

  <!-- URL strony internetowej -->
  <text x="860" y="510" fill="white" font-size="30px">
    adres url
  </text>
</svg>
Ten szablon nie ma żadnych zmiennych. Zmienne będą interpretowane w szablonie .erb. Umieścimy ten szablon SVG w naszej aplikacji rails i dodamy rozszerzenie .erb, na przykład lib/templates/og_image_template.svg.erb.
Używam biblioteki mini_magick. Więc już mam w Gemfile:
gem "mini_magick"
Dalej sam kod lib/cover_image_generator.rb.
require 'mini_magick'
require 'erb'

module CoverImageGenerator
  class Generator
    def generate_and_attach(entry, entry_title)
      begin
        # Dynamicznie wypełnij szablon SVG
        @entry_title = entry_title.truncate(160)
        @blog_name = entry.blog.slug
        svg_template = File.read(File.expand_path('./templates/cover_image_template.svg.erb', __dir__))
        svg_content = ERB.new(svg_template).result(binding)
        
        # Stwórz obraz MiniMagick z zawartości SVG
        cover_image = MiniMagick::Image.read(svg_content)

        # Konwertuj na JPG
        cover_image.format('jpg')
        temp_file_path = entry.slug + '.jpg'
        cover_image.write(temp_file_path)

        # Dołącz obraz do wpisu
        entry.cover_image.attach(io: File.open(temp_file_path), filename: "#{entry.slug}.jpg", content_type: 'image/jpeg')

        # Wyczyść plik tymczasowy
        File.delete(temp_file_path)
      rescue => e
        # Tutaj prawdopodobnie będą problemy z tekstem, który ma specyficzne znaki, które nie mogą być konwertowane na JPG.
        # Na razie takie tytuły po prostu ignoruję - trzeba znaleźć rozwiązanie dla tego problemu.
        # Obecnie, jeśli model nie ma dodanego obrazu OG, pokazuję domyślny obraz.
        puts "Błąd na: #{@entry_title}, ID: #{entry.id}"
        puts e
        puts "=========================================="
      end
      puts "Obraz dołączony do: #{@entry_title}, ID: #{entry.id}"
      puts "=========================================="
    end
  end
end
AWS jest już skonfigurowany i wszystko, co trzeba zrobić, to dodać cover_image do naszego modelu (na przykład Topic)
# app/models/topic.rb
has_one_attached :cover_image
Nasz szablon SVG ma renderować nasze zmienne. Również, biorąc pod uwagę specyfikę stylów pliku SVG, musimy podzielić zbyt długie tytuły na kilka wierszy.
<svg width="1200" height="630" viewBox="0 0 1200 630" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <!-- Tło -->
  <rect width="1200" height="630" fill="black"/>
  <rect x="56" y="56" width="1088" height="518" rx="13" stroke="white" fill="black" stroke-width="13"/>
  
  <!-- Wiele wierszy tytułu -->
  <text x="160" y="110" fill="white" font-size="50px" font-weight="bold" line-height="1.2" margin="0">
    <% lines = @entry_title.scan(/\S.{0,34}\S(?=\s|\z)/) %>
    <% lines.each_with_index do |line, index| %>
      <tspan x="160" dy="<%= index == 0 ? '1.5em' : '1.2em' %>"><%= line.strip %></tspan>
    <% end %>
  </text>

  <!-- Nazwa bloga -->
  <text x="160" y="510" fill="white" font-size="30px">
    <%= @blog_name %>
  </text>

  <!-- URL strony internetowej -->
  <text x="860" y="510" fill="white" font-size="30px">
    tseivo.com
  </text>
</svg>
I na koniec, aby wygenerować obraz:
topic = Topic.last
CoverImageGenerator::Generator.new.generate_and_attach(topic, topic.name)
Ten kod należy dodać do zadania rake, które przejdzie i wygeneruje obraz OG dla wszystkich potrzebnych obiektów. Można również użyć tego kodu do generowania obrazu po utworzeniu lub aktualizacji (tytułu) obiektu.
Wynik:
Приклад створеного JPEG з SVG шаблону
Приклад створеного JPEG з SVG шаблону
Ten przykład kodu / koncepcji nie jest ostateczną wersją. Jest jeszcze nad czym pracować.

Ten post nie ma jeszcze żadnych dodatków od autora.

29 gru 10:22

Czym jest automatyczne rozpoznawanie mowy (ASR)?

meme code
meme code@memecode
29 gru 10:30

Czym jest NLP? Do czego potrzebne jest przetwarzanie języka naturalnego?

meme code
meme code@memecode
29 gru 10:37

Czym jest Chat GPT? Do czego jest potrzebny i jak działa?

meme code
meme code@memecode
29 gru 10:48

Co to jest SI? Co oznacza "wygenerowane za pomocą SI"?

meme code
meme code@memecode
3 sty 13:16

Wystąpił błąd podczas instalacji tiny_tds (2.1.5), a Bundler nie może kontynuować na Macu z M1.

meme code
meme code@memecode
10 sty 19:41

[Własne doświadczenie] MacBook się zawiesił. W ogóle się nie włącza lub ekran od razu gaśnie.

meme code
meme code@memecode
10 lut 16:08

Co to jest Hot Potato (Gorący Ziemniak) w rozwoju oprogramowania?

meme code
meme code@memecode
5 mar 19:17

Czym jest skalowalność?

meme code
meme code@memecode
5 mar 19:18

Co oznacza HA (Wysoka Dostępność)?

meme code
meme code@memecode
5 mar 19:29

Czym różni się High Availability od Scalability?

meme code
meme code@memecode
5 mar 19:38

Czym jest Service Discovery w IT?

meme code
meme code@memecode
7 mar 18:36

Czym jest klasteryzacja / clustering w IT?

meme code
meme code@memecode