445 lines
13 KiB
C++
445 lines
13 KiB
C++
#include <array>
|
|
#include "container_cairo_pango.h"
|
|
|
|
container_cairo_pango::container_cairo_pango()
|
|
{
|
|
m_temp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2);
|
|
m_temp_cr = cairo_create(m_temp_surface);
|
|
cairo_save(m_temp_cr);
|
|
PangoLayout *layout = pango_cairo_create_layout(m_temp_cr);
|
|
PangoContext *context = pango_layout_get_context(layout);
|
|
PangoFontFamily** families;
|
|
int n;
|
|
pango_context_list_families(context, &families, &n);
|
|
for(int i = 0; i < n; i++)
|
|
{
|
|
PangoFontFamily* family = families[i];
|
|
if (!PANGO_IS_FONT_FAMILY(family)) continue;
|
|
std::string font_name = pango_font_family_get_name(family);
|
|
litehtml::lcase(font_name);
|
|
m_all_fonts.insert(font_name);
|
|
}
|
|
g_free(families);
|
|
cairo_restore(m_temp_cr);
|
|
g_object_unref(layout);
|
|
}
|
|
|
|
container_cairo_pango::~container_cairo_pango()
|
|
{
|
|
clear_images();
|
|
cairo_surface_destroy(m_temp_surface);
|
|
cairo_destroy(m_temp_cr);
|
|
}
|
|
|
|
litehtml::uint_ptr container_cairo_pango::create_font(const litehtml::font_description& descr, const litehtml::document* doc, litehtml::font_metrics *fm)
|
|
{
|
|
litehtml::string_vector tokens;
|
|
litehtml::split_string(descr.family, tokens, ",");
|
|
std::string fonts;
|
|
|
|
for(auto& font : tokens)
|
|
{
|
|
litehtml::trim(font, " \t\r\n\f\v\"\'");
|
|
if(litehtml::value_in_list(font, "serif;sans-serif;monospace;cursive;fantasy;"))
|
|
{
|
|
fonts = font + ",";
|
|
break;
|
|
}
|
|
litehtml::lcase(font);
|
|
if(m_all_fonts.find(font) != m_all_fonts.end())
|
|
{
|
|
fonts = font;
|
|
fonts += ",";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(fonts.empty())
|
|
{
|
|
fonts = "serif,";
|
|
}
|
|
|
|
PangoFontDescription *desc = pango_font_description_from_string (fonts.c_str());
|
|
pango_font_description_set_absolute_size(desc, descr.size * PANGO_SCALE);
|
|
if(descr.style == litehtml::font_style_italic)
|
|
{
|
|
pango_font_description_set_style(desc, PANGO_STYLE_ITALIC);
|
|
} else
|
|
{
|
|
pango_font_description_set_style(desc, PANGO_STYLE_NORMAL);
|
|
}
|
|
|
|
pango_font_description_set_weight(desc, (PangoWeight) descr.weight);
|
|
|
|
cairo_font* ret = nullptr;
|
|
|
|
if(fm)
|
|
{
|
|
fm->font_size = descr.size;
|
|
|
|
cairo_save(m_temp_cr);
|
|
PangoLayout *layout = pango_cairo_create_layout(m_temp_cr);
|
|
PangoContext *context = pango_layout_get_context(layout);
|
|
PangoLanguage *language = pango_language_get_default();
|
|
pango_layout_set_font_description(layout, desc);
|
|
PangoFontMetrics *metrics = pango_context_get_metrics(context, desc, language);
|
|
|
|
fm->ascent = PANGO_PIXELS((double)pango_font_metrics_get_ascent(metrics));
|
|
fm->height = PANGO_PIXELS((double)pango_font_metrics_get_height(metrics));
|
|
fm->descent = fm->height - fm->ascent;
|
|
fm->x_height = fm->height;
|
|
fm->draw_spaces = (descr.decoration_line != litehtml::text_decoration_line_none);
|
|
fm->sub_shift = descr.size / 5;
|
|
fm->super_shift = descr.size / 3;
|
|
|
|
pango_layout_set_text(layout, "x", 1);
|
|
|
|
PangoRectangle ink_rect;
|
|
PangoRectangle logical_rect;
|
|
pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect);
|
|
fm->x_height = ink_rect.height;
|
|
if(fm->x_height == fm->height) fm->x_height = fm->x_height * 4 / 5;
|
|
|
|
pango_layout_set_text(layout, "0", 1);
|
|
|
|
pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect);
|
|
fm->ch_width = logical_rect.width;
|
|
|
|
cairo_restore(m_temp_cr);
|
|
|
|
ret = new cairo_font;
|
|
ret->font = desc;
|
|
ret->size = descr.size;
|
|
ret->strikeout = (descr.decoration_line & litehtml::text_decoration_line_line_through) != 0;
|
|
ret->underline = (descr.decoration_line & litehtml::text_decoration_line_underline) != 0;
|
|
ret->overline = (descr.decoration_line & litehtml::text_decoration_line_overline) != 0;
|
|
ret->ascent = fm->ascent;
|
|
ret->descent = fm->descent;
|
|
ret->decoration_color = descr.decoration_color;
|
|
ret->decoration_style = descr.decoration_style;
|
|
|
|
auto thinkness = descr.decoration_thickness;
|
|
if(!thinkness.is_predefined())
|
|
{
|
|
litehtml::css_length one_em(1.0, litehtml::css_units_em);
|
|
doc->cvt_units(one_em, *fm, 0);
|
|
doc->cvt_units(thinkness, *fm, (int) one_em.val());
|
|
}
|
|
|
|
|
|
ret->underline_position = -pango_font_metrics_get_underline_position(metrics);
|
|
if(thinkness.is_predefined())
|
|
{
|
|
ret->underline_thickness = pango_font_metrics_get_underline_thickness(metrics);
|
|
} else
|
|
{
|
|
ret->underline_thickness = (int)(thinkness.val() * PANGO_SCALE);
|
|
}
|
|
pango_quantize_line_geometry(&ret->underline_thickness, &ret->underline_position);
|
|
ret->underline_thickness = PANGO_PIXELS(ret->underline_thickness);
|
|
ret->underline_position = PANGO_PIXELS(ret->underline_position);
|
|
|
|
ret->strikethrough_position = pango_font_metrics_get_strikethrough_position(metrics);
|
|
if(thinkness.is_predefined())
|
|
{
|
|
ret->strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness(metrics);
|
|
} else
|
|
{
|
|
ret->strikethrough_thickness = (int)(thinkness.val() * PANGO_SCALE);
|
|
}
|
|
pango_quantize_line_geometry(&ret->strikethrough_thickness, &ret->strikethrough_position);
|
|
ret->strikethrough_thickness = PANGO_PIXELS(ret->strikethrough_thickness);
|
|
ret->strikethrough_position = PANGO_PIXELS(ret->strikethrough_position);
|
|
|
|
ret->overline_position = fm->ascent * PANGO_SCALE;
|
|
if(thinkness.is_predefined())
|
|
{
|
|
ret->overline_thickness = pango_font_metrics_get_underline_thickness(metrics);
|
|
} else
|
|
{
|
|
ret->overline_thickness = (int)(thinkness.val() * PANGO_SCALE);
|
|
}
|
|
pango_quantize_line_geometry(&ret->overline_thickness, &ret->overline_position);
|
|
ret->overline_thickness = PANGO_PIXELS(ret->overline_thickness);
|
|
ret->overline_position = PANGO_PIXELS(ret->overline_position);
|
|
|
|
g_object_unref(layout);
|
|
pango_font_metrics_unref(metrics);
|
|
}
|
|
|
|
return (litehtml::uint_ptr) ret;
|
|
}
|
|
|
|
void container_cairo_pango::delete_font(litehtml::uint_ptr hFont)
|
|
{
|
|
auto* fnt = (cairo_font*) hFont;
|
|
if(fnt)
|
|
{
|
|
pango_font_description_free(fnt->font);
|
|
delete fnt;
|
|
}
|
|
}
|
|
|
|
int container_cairo_pango::text_width(const char *text, litehtml::uint_ptr hFont)
|
|
{
|
|
auto* fnt = (cairo_font*) hFont;
|
|
|
|
cairo_save(m_temp_cr);
|
|
|
|
PangoLayout *layout = pango_cairo_create_layout(m_temp_cr);
|
|
pango_layout_set_font_description(layout, fnt->font);
|
|
|
|
pango_layout_set_text(layout, text, -1);
|
|
pango_cairo_update_layout (m_temp_cr, layout);
|
|
|
|
int x_width, x_height;
|
|
pango_layout_get_pixel_size(layout, &x_width, &x_height);
|
|
|
|
cairo_restore(m_temp_cr);
|
|
|
|
g_object_unref(layout);
|
|
|
|
return (int) x_width;
|
|
}
|
|
|
|
enum class draw_type
|
|
{
|
|
DRAW_OVERLINE,
|
|
DRAW_STRIKETHROUGH,
|
|
DRAW_UNDERLINE
|
|
};
|
|
|
|
static inline void draw_single_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type)
|
|
{
|
|
double top;
|
|
switch (type)
|
|
{
|
|
case draw_type::DRAW_UNDERLINE:
|
|
top = y + (double)thickness / 2.0;
|
|
break;
|
|
case draw_type::DRAW_OVERLINE:
|
|
top = y - (double)thickness / 2.0;
|
|
break;
|
|
case draw_type::DRAW_STRIKETHROUGH:
|
|
top = y + 0.5;
|
|
break;
|
|
default:
|
|
top = y;
|
|
break;
|
|
}
|
|
cairo_move_to(cr, x, top);
|
|
cairo_line_to(cr, x + width, top);
|
|
}
|
|
|
|
static void draw_solid_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color)
|
|
{
|
|
draw_single_line(cr, x, y, width, thickness, type);
|
|
|
|
cairo_set_source_rgba(cr, (double) color.red / 255.0,
|
|
(double) color.green / 255.0,
|
|
(double) color.blue / 255.0,
|
|
(double) color.alpha / 255.0);
|
|
cairo_set_line_width(cr, thickness);
|
|
cairo_stroke(cr);
|
|
}
|
|
|
|
static void draw_dotted_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color)
|
|
{
|
|
draw_single_line(cr, x, y, width, thickness, type);
|
|
|
|
std::array<double, 2> dashes {0, thickness * 2.0};
|
|
if(thickness == 1) dashes[1] += thickness / 2.0;
|
|
cairo_set_line_width(cr, thickness);
|
|
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
|
cairo_set_dash(cr, dashes.data(), 2, x);
|
|
|
|
cairo_set_source_rgba(cr, (double) color.red / 255.0,
|
|
(double) color.green / 255.0,
|
|
(double) color.blue / 255.0,
|
|
(double) color.alpha / 255.0);
|
|
cairo_stroke(cr);
|
|
}
|
|
|
|
static void draw_dashed_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color)
|
|
{
|
|
draw_single_line(cr, x, y, width, thickness, type);
|
|
|
|
std::array<double, 2> dashes {thickness * 2.0, thickness * 3.0};
|
|
cairo_set_line_width(cr, thickness);
|
|
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
|
cairo_set_dash(cr, dashes.data(), 2, x);
|
|
|
|
cairo_set_source_rgba(cr, (double) color.red / 255.0,
|
|
(double) color.green / 255.0,
|
|
(double) color.blue / 255.0,
|
|
(double) color.alpha / 255.0);
|
|
cairo_stroke(cr);
|
|
}
|
|
|
|
static void draw_wavy_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color)
|
|
{
|
|
double h_pad = 1.0;
|
|
int brush_height = (int) thickness * 3 + h_pad * 2;
|
|
int brush_width = brush_height * 2 - 2 * thickness;
|
|
|
|
double top;
|
|
switch (type)
|
|
{
|
|
case draw_type::DRAW_UNDERLINE:
|
|
top = y + (double)brush_height / 2.0;
|
|
break;
|
|
case draw_type::DRAW_OVERLINE:
|
|
top = y - (double)brush_height / 2.0;
|
|
break;
|
|
default:
|
|
top = y;
|
|
break;
|
|
}
|
|
|
|
cairo_surface_t* brush_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, brush_width, brush_height);
|
|
cairo_t* brush_cr = cairo_create(brush_surface);
|
|
|
|
cairo_set_source_rgba(brush_cr, (double) color.red / 255.0,
|
|
(double) color.green / 255.0,
|
|
(double) color.blue / 255.0,
|
|
(double) color.alpha / 255.0);
|
|
cairo_set_line_width(brush_cr, thickness);
|
|
double w = thickness / 2.0;
|
|
cairo_move_to(brush_cr, 0, brush_height - (double) thickness / 2.0 - h_pad);
|
|
cairo_line_to(brush_cr, w, brush_height - (double) thickness / 2.0 - h_pad);
|
|
cairo_line_to(brush_cr, brush_width / 2.0 - w, (double) thickness / 2.0 + h_pad);
|
|
cairo_line_to(brush_cr, brush_width / 2.0 + w, (double) thickness / 2.0 + h_pad);
|
|
cairo_line_to(brush_cr, brush_width - w, brush_height - (double) thickness / 2.0 - h_pad);
|
|
cairo_line_to(brush_cr, brush_width, brush_height - (double) thickness / 2.0 - h_pad);
|
|
cairo_stroke(brush_cr);
|
|
cairo_destroy(brush_cr);
|
|
|
|
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(brush_surface);
|
|
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
|
|
cairo_matrix_t patterm_matrix;
|
|
cairo_matrix_init_translate(&patterm_matrix, 0, -top + brush_height / 2.0);
|
|
cairo_pattern_set_matrix(pattern, &patterm_matrix);
|
|
cairo_set_source(cr, pattern);
|
|
|
|
cairo_set_line_width(cr, brush_height);
|
|
cairo_move_to(cr, x, top);
|
|
cairo_line_to(cr, x + width, top);
|
|
cairo_stroke(cr);
|
|
|
|
cairo_pattern_destroy(pattern);
|
|
cairo_surface_destroy(brush_surface);
|
|
}
|
|
|
|
static void draw_double_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color)
|
|
{
|
|
cairo_set_line_width(cr, thickness);
|
|
double top1;
|
|
double top2;
|
|
switch (type)
|
|
{
|
|
case draw_type::DRAW_UNDERLINE:
|
|
top1 = y + (double)thickness / 2.0;
|
|
top2 = top1 + (double)thickness + (double)thickness / 2.0 + 0.5;
|
|
break;
|
|
case draw_type::DRAW_OVERLINE:
|
|
top1 = y - (double)thickness / 2.0;
|
|
top2 = top1 - (double)thickness - (double)thickness / 2.0 - 0.5;
|
|
break;
|
|
case draw_type::DRAW_STRIKETHROUGH:
|
|
top1 = y - (double)thickness + 0.5;
|
|
top2 = y + (double)thickness + 0.5;
|
|
break;
|
|
default:
|
|
top1 = y;
|
|
top2 = y;
|
|
break;
|
|
}
|
|
cairo_move_to(cr, x, top1);
|
|
cairo_line_to(cr, x + width, top1);
|
|
cairo_stroke(cr);
|
|
cairo_move_to(cr, x, top2);
|
|
cairo_line_to(cr, x + width, top2);
|
|
cairo_set_source_rgba(cr, (double) color.red / 255.0,
|
|
(double) color.green / 255.0,
|
|
(double) color.blue / 255.0,
|
|
(double) color.alpha / 255.0);
|
|
cairo_stroke(cr);
|
|
}
|
|
|
|
void container_cairo_pango::draw_text(litehtml::uint_ptr hdc, const char *text, litehtml::uint_ptr hFont,
|
|
litehtml::web_color color, const litehtml::position &pos)
|
|
{
|
|
auto* fnt = (cairo_font*) hFont;
|
|
auto* cr = (cairo_t*) hdc;
|
|
cairo_save(cr);
|
|
|
|
apply_clip(cr);
|
|
|
|
set_color(cr, color);
|
|
|
|
litehtml::web_color decoration_color = color;
|
|
|
|
PangoLayout *layout = pango_cairo_create_layout(cr);
|
|
pango_layout_set_font_description (layout, fnt->font);
|
|
pango_layout_set_text (layout, text, -1);
|
|
|
|
auto font_options = get_font_options();
|
|
if(font_options)
|
|
{
|
|
auto ctx = pango_layout_get_context(layout);
|
|
pango_cairo_context_set_font_options(ctx, font_options);
|
|
}
|
|
|
|
PangoRectangle ink_rect, logical_rect;
|
|
pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect);
|
|
|
|
int text_baseline = pos.height - fnt->descent;
|
|
|
|
int x = pos.left() + logical_rect.x;
|
|
int y = pos.top();
|
|
|
|
cairo_move_to(cr, x, y);
|
|
pango_cairo_update_layout (cr, layout);
|
|
pango_cairo_show_layout (cr, layout);
|
|
|
|
int tw = 0;
|
|
|
|
if(fnt->underline || fnt->strikeout || fnt->overline)
|
|
{
|
|
tw = text_width(text, hFont);
|
|
}
|
|
|
|
if(!fnt->decoration_color.is_current_color)
|
|
{
|
|
decoration_color = fnt->decoration_color;
|
|
}
|
|
|
|
std::array<decltype(&draw_solid_line), litehtml::text_decoration_style_max> draw_funcs {
|
|
draw_solid_line, // text_decoration_style_solid
|
|
draw_double_line, // text_decoration_style_double
|
|
draw_dotted_line, // text_decoration_style_dotted
|
|
draw_dashed_line, // text_decoration_style_dashed
|
|
draw_wavy_line, // text_decoration_style_wavy
|
|
};
|
|
|
|
if(fnt->underline)
|
|
{
|
|
draw_funcs[fnt->decoration_style](cr, x, pos.top() + text_baseline + fnt->underline_position, tw, fnt->underline_thickness, draw_type::DRAW_UNDERLINE, decoration_color);
|
|
}
|
|
|
|
if(fnt->strikeout)
|
|
{
|
|
draw_funcs[fnt->decoration_style](cr, x, pos.top() + text_baseline - fnt->strikethrough_position, tw, fnt->strikethrough_thickness, draw_type::DRAW_STRIKETHROUGH, decoration_color);
|
|
}
|
|
|
|
if(fnt->overline)
|
|
{
|
|
draw_funcs[fnt->decoration_style](cr, x, pos.top() + text_baseline - fnt->overline_position, tw, fnt->overline_thickness, draw_type::DRAW_OVERLINE, decoration_color);
|
|
}
|
|
|
|
cairo_restore(cr);
|
|
|
|
g_object_unref(layout);
|
|
}
|
|
|