Есть ли какой-нибудь шрифт или метод, который я могу использовать с Cairo (через привязки Ruby) для рисования текста, содержащего смайлики? Все, что я читал, говорит: «используйте Pango для реальной обработки текста», но Cairo делает все остальное, что мне нужно, поэтому я надеюсь, что есть способ избежать добавления этого в качестве зависимости.
Вот упрощенная версия динамических изображений Open Graph, которые я пытаюсь сделать:
require 'cairo'
# Create a new image surface based on recommended OpenGraph dimensions
width = 1_200
height = 630
surface = Cairo::ImageSurface.new(width, height)
# Create a Cairo Context for the surface
cr = Cairo::Context.new(surface)
# Apply a background colour to the whole thing (almost white)
cr.set_source_rgb(0.95, 0.95, 0.95)
cr.paint
# Add a heading
heading = "Hi! 👋"
cr.set_source_rgb(0.31, 0.21, 0.27)
cr.select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD)
cr.set_font_size(100.0)
extents = cr.text_extents(heading)
centred_text_left_edge = width / 2 - extents.width / 2
cr.move_to(centred_text_left_edge, 130)
cr.show_text(heading)
# Write the image surface to a PNG file
surface.write_to_png("emoji-test-#{Time.now.strftime('%Y-%m-%dT%H:%M:%S.%L%z')}.png")
К сожалению, эмодзи отображается в виде прямоугольника (работает на macOS, может отличаться на других платформах):
Могу ли я использовать собственный шрифт или есть какой-то другой метод, позволяющий отображать смайлики?
Кажется, что «правильный» вариант — использовать Pango, но актуален ли этот совет и применим ли он к Ruby? Я не смог установить драгоценный камень Pango на свой M1 Mac, и даже если бы я мог, после долгих поисков единственное упоминание, которое я могу найти об использовании Pango с Cairo от Ruby, — это очень старый образец сценария, который больше не включен в Раздача Панго.
В противном случае, я думаю, мне придется сохранить смайлики в виде изображений и нарисовать их на поверхности/контексте. Это возможно для меня, потому что я хочу использовать только 3 конкретных смайлика, но он не масштабируется для включения произвольных смайликов.
Наверное, надо было немного понятнее. Я пытаюсь создавать динамические изображения OpenGraph (предварительный просмотр в социальных сетях, когда вы делитесь ссылкой), поэтому я визуализирую изображение в формате PNG с помощью surface.write_to_png
Никогда не работал с Cairo
, но можно предположить, что файл определения шрифта, который вы выбираете с помощью cr.select_font_face
, не содержит определения для U+1F44B. Есть ли способ указать в Cairo некоторый «запасной» шрифт на случай, если в выбранном шрифте не удается найти символ UTF8?
Cairo действительно не хочет заниматься выбором шрифта. Да, есть способ получить резервные шрифты. Они могут сделать это самостоятельно, выбрав шрифт. Это означает, что вместо cairo_show_text()
вы можете играть с масштабируемыми шрифтами и API, такими как cairo_show_glyphs()
. Если вы не хотите реализовывать необходимый код самостоятельно, вы можете использовать какую-то существующую реализацию. Например, Pango делает выбор шрифта.
Я с радостью приму другой ответ, если у кого-то есть лучшее решение, но в итоге я вернулся к рисованию смайликов в PNG. Дочитайте до конца минусы этого подхода.
Сначала я сохранил в формате PNG 96x96 пикселей смайлики, которые хотел использовать (например, wave.png
). Затем я нарисовал их на поверхности. Вот ПОК:
require 'cairo'
# Create a new image surface based on recommended OpenGraph dimensions
width = 1_200
height = 630
surface = Cairo::ImageSurface.new(width, height)
# Create a Cairo Context for the surface
cr = Cairo::Context.new(surface)
# Apply a background colour to the whole thing (almost white)
cr.set_source_rgb(0.95, 0.95, 0.95)
cr.paint
# Set up the heading
string = "Hi!"
cr.set_source_rgb(0.31, 0.21, 0.27)
cr.select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD)
cr.set_font_size(100.0)
text_box = cr.text_extents(string)
# Set up the emoji image
image = Cairo::ImageSurface.from_png("wave.png")
# Position the text in the centre (taking into account the image width)
horizontal_gap = 20 # trial and error
top_of_text = 130 # trial and error
line_width = text_box.width + image.width + horizontal_gap
text_left_edge = (width - line_width) / 2 # centred on page
cr.move_to(text_left_edge, top_of_text)
cr.show_text(string)
# Position the image next to the text
top_of_image = top_of_text - 85 # trial and error
image_left_edge = text_left_edge + text_box.width + horizontal_gap
cr.set_source(image, image_left_edge, top_of_image)
cr.paint
# Write the image surface to a PNG file
surface.write_to_png("emoji-test.png")
Это генерирует мое изображение OpenGraph с текстом и эмодзи:
Недостатками такого подхода являются:
Похоже, ваш смайлик имеет кодовую точку Unicode U+1F44B, и я уверен, что ваша программа справится с этим. Однако вы не говорите, как вы собираетесь «отображать» этот вывод. Включает ли программа, которая его отображает, шрифт, содержащий определения для U+1F44B?