This source file includes following definitions.
- svg_font_get_glyphs
- svg_font_load_glyph
- svg_traverse_font
- svg_font_on_load
- compositor_init_svg_font
- svg_traverse_glyph
- compositor_init_svg_glyph
- svg_font_uri_check
- svg_font_uri_get_alias
- svg_font_uri_get_glyphs
- svg_font_uri_load_glyph
- svg_traverse_font_face_uri
- compositor_init_svg_font_face_uri
#include <gpac/utf.h>
#ifndef GPAC_DISABLE_SVG
#include "visual_manager.h"
#include "nodes_stacks.h"
typedef struct
{
u16 *unicode;
u16 uni_len;
GF_Glyph glyph;
GF_Font *font;
} SVG_GlyphStack;
static GF_Err svg_font_get_glyphs(void *udta, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *lang, Bool *is_rtl)
{
u32 prev_c;
size_t len;
u32 i, gl_idx;
u16 *utf_res;
GF_Node *node = (GF_Node *)udta;
GF_ChildNodeItem *child;
char *utf8 = (char*) utf_string;
len = utf_string ? (u32) strlen(utf_string) : 0;
if (!len) {
*io_glyph_buffer_size = 0;
return GF_OK;
}
if (*io_glyph_buffer_size < len+1) {
*io_glyph_buffer_size = (u32) len+1;
return GF_BUFFER_TOO_SMALL;
}
len = gf_utf8_mbstowcs((u16*) glyph_buffer, *io_glyph_buffer_size, (const char**)&utf8);
if (len == (size_t) -1) return GF_IO_ERR;
if (utf8) return GF_IO_ERR;
utf_res = (u16 *) glyph_buffer;
*is_rtl = gf_utf8_reorder_bidi(utf_res, (u32) len);
for (i=(u32)len; i>0; i--) {
glyph_buffer[i-1] = utf_res[i-1];
}
gl_idx = 0;
prev_c = 0;
for (i=0; i<len; i++) {
SVG_GlyphStack *missing_glyph = NULL;
SVG_GlyphStack *st = NULL;
child = ((GF_ParentNode *) node)->children;
while (child) {
u32 tag = gf_node_get_tag(child->node);
if (tag==TAG_SVG_missing_glyph) {
missing_glyph = gf_node_get_private(child->node);
} else if (tag ==TAG_SVG_glyph) {
Bool glyph_ok = 0;
SVGAllAttributes atts;
st = gf_node_get_private(child->node);
if (!st) {
child = child->next;
continue;
}
if (st->glyph.utf_name==glyph_buffer[i]) {
u32 j, count;
gf_svg_flatten_attributes((SVG_Element*)child->node, &atts);
if (!lang) {
glyph_ok = 1;
} else {
if (!atts.lang) {
glyph_ok = 1;
} else {
count = gf_list_count(*atts.lang);
for (j=0; j<count; j++) {
char *name = gf_list_get(*atts.lang, j);
if (!stricmp(name, lang) || strstr(lang, name)) {
glyph_ok = 1;
break;
}
}
}
}
if (atts.arabic_form) {
Bool first = (!prev_c || (prev_c==' ')) ? 1 : 0;
Bool last = ((i+1==len) || (glyph_buffer[i+1]==' ') ) ? 1 : 0;
if (!strcmp(*atts.arabic_form, "isolated")) {
if (!first || !last) glyph_ok = 0;
}
if (!strcmp(*atts.arabic_form, "initial")) {
if (!first) glyph_ok = 0;
}
if (!strcmp(*atts.arabic_form, "medial")) {
if (first || last) glyph_ok = 0;
}
if (!strcmp(*atts.arabic_form, "terminal")) {
if (!last) glyph_ok = 0;
}
}
if (glyph_ok) break;
}
else if (st->uni_len>1) {
u32 j;
for (j=0; j<st->uni_len; j++) {
if (i+j>=len) break;
if (glyph_buffer[i+j] != st->unicode[j]) break;
}
if (j==st->uni_len)
break;
}
st = NULL;
}
child = child->next;
}
prev_c = glyph_buffer[i];
if (!st)
st = missing_glyph;
glyph_buffer[gl_idx] = st ? st->glyph.ID : 0;
if (st && st->uni_len>1) i++;
gl_idx++;
}
*io_glyph_buffer_size = gl_idx;
return GF_OK;
}
static GF_Glyph *svg_font_load_glyph(void *udta, u32 glyph_name)
{
GF_ChildNodeItem *child = ((GF_ParentNode *) udta)->children;
while (child) {
if (gf_node_get_tag(child->node)==TAG_SVG_glyph) {
SVG_GlyphStack *st = gf_node_get_private(child->node);
if (st->glyph.ID==glyph_name) {
return &st->glyph;
}
}
child = child->next;
}
return NULL;
}
static void svg_traverse_font(GF_Node *node, void *rs, Bool is_destroy)
{
if (is_destroy) {
GF_Font *font = gf_node_get_private(node);
if (font) {
gf_font_manager_unregister_font(font->ft_mgr, font);
if (font->name) gf_free(font->name);
gf_free(font);
}
}
}
static void svg_font_on_load(GF_Node *handler, GF_DOM_Event *event, GF_Node *observer)
{
GF_Compositor *compositor;
GF_Font *font;
assert(event->currentTarget->ptr_type==GF_DOM_EVENT_TARGET_NODE);
assert(gf_node_get_tag((GF_Node*)event->currentTarget->ptr)==TAG_SVG_font);
font = gf_node_get_private((GF_Node*)event->currentTarget->ptr);
font->not_loaded = 0;
compositor = (GF_Compositor *)gf_node_get_private((GF_Node *)handler);
compositor->reset_fonts = 1;
gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
compositor->fonts_pending--;
}
void compositor_init_svg_font(GF_Compositor *compositor, GF_Node *node)
{
SVG_handlerElement *handler;
GF_Err e;
SVGAllAttributes atts;
GF_Font *font;
GF_Node *node_font = gf_node_get_parent(node, 0);
if (!node_font) return;
if (gf_node_get_tag(node_font)!=TAG_SVG_font) return;
gf_svg_flatten_attributes((SVG_Element*)node, &atts);
if (!atts.font_family) return;
GF_SAFEALLOC(font, GF_Font);
if (!font) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg font\n"));
return;
}
e = gf_font_manager_register_font(compositor->font_manager, font);
if (e) {
gf_free(font);
return;
}
font->ft_mgr = compositor->font_manager;
font->get_glyphs = svg_font_get_glyphs;
font->load_glyph = svg_font_load_glyph;
font->udta = node_font;
gf_node_set_private(node_font, font);
gf_node_set_callback_function(node_font, svg_traverse_font);
font->name = gf_strdup(atts.font_family->value);
font->em_size = atts.units_per_em ? FIX2INT( gf_ceil(atts.units_per_em->value) ) : 1000;
font->ascent = atts.ascent ? FIX2INT( gf_ceil(atts.ascent->value) ) : 0;
if (!font->ascent) font->ascent = font->em_size;
font->descent = atts.descent ? FIX2INT( gf_ceil(atts.descent->value) ) : 0;
font->baseline = atts.alphabetic ? FIX2INT( gf_ceil(atts.alphabetic->value) ) : 0;
font->line_spacing = font->em_size;
font->styles = 0;
if (atts.font_style) {
switch (*atts.font_style) {
case SVG_FONTSTYLE_ITALIC:
font->styles |= GF_FONT_ITALIC;
break;
case SVG_FONTSTYLE_OBLIQUE:
font->styles |= GF_FONT_OBLIQUE;
break;
}
}
if (atts.font_variant && (*atts.font_variant ==SVG_FONTVARIANT_SMALLCAPS))
font->styles |= GF_FONT_SMALLCAPS;
if (atts.font_weight) {
switch(*atts.font_weight) {
case SVG_FONTWEIGHT_100:
font->styles |= GF_FONT_WEIGHT_100;
break;
case SVG_FONTWEIGHT_LIGHTER:
font->styles |= GF_FONT_WEIGHT_LIGHTER;
break;
case SVG_FONTWEIGHT_200:
font->styles |= GF_FONT_WEIGHT_200;
break;
case SVG_FONTWEIGHT_300:
font->styles |= GF_FONT_WEIGHT_300;
break;
case SVG_FONTWEIGHT_400:
font->styles |= GF_FONT_WEIGHT_400;
break;
case SVG_FONTWEIGHT_NORMAL:
font->styles |= GF_FONT_WEIGHT_NORMAL;
break;
case SVG_FONTWEIGHT_500:
font->styles |= GF_FONT_WEIGHT_500;
break;
case SVG_FONTWEIGHT_600:
font->styles |= GF_FONT_WEIGHT_600;
break;
case SVG_FONTWEIGHT_700:
font->styles |= GF_FONT_WEIGHT_700;
break;
case SVG_FONTWEIGHT_BOLD:
font->styles |= GF_FONT_WEIGHT_BOLD;
break;
case SVG_FONTWEIGHT_800:
font->styles |= GF_FONT_WEIGHT_800;
break;
case SVG_FONTWEIGHT_900:
font->styles |= GF_FONT_WEIGHT_900;
break;
case SVG_FONTWEIGHT_BOLDER:
font->styles |= GF_FONT_WEIGHT_BOLDER;
break;
}
}
gf_svg_flatten_attributes((SVG_Element*)node_font, &atts);
font->max_advance_h = atts.horiz_adv_x ? FIX2INT( gf_ceil(atts.horiz_adv_x->value) ) : 0;
font->not_loaded = 1;
handler = gf_dom_listener_build(node_font, GF_EVENT_LOAD, 0);
handler->handle_event = svg_font_on_load;
gf_node_set_private((GF_Node *)handler, compositor);
}
static void svg_traverse_glyph(GF_Node *node, void *rs, Bool is_destroy)
{
if (is_destroy) {
GF_Font *font;
GF_Glyph *prev_glyph, *a_glyph;
SVG_GlyphStack *st = gf_node_get_private(node);
if (st->unicode) gf_free(st->unicode);
font = st->font;
prev_glyph = NULL;
a_glyph = font->glyph;
while (a_glyph) {
if (a_glyph == &st->glyph) break;
prev_glyph = a_glyph;
a_glyph = a_glyph->next;
}
if (prev_glyph) {
prev_glyph->next = st->glyph.next;
} else {
font->glyph = st->glyph.next;
}
gf_free(st);
}
}
void compositor_init_svg_glyph(GF_Compositor *compositor, GF_Node *node)
{
u16 utf_name[20];
u8 *utf8;
size_t len;
GF_Rect rc;
GF_Glyph *glyph;
GF_Font *font;
SVG_GlyphStack *st;
SVGAllAttributes atts;
GF_Node *node_font = gf_node_get_parent(node, 0);
if (node_font) node_font = gf_node_get_parent(node, 0);
if (!node_font || (gf_node_get_tag(node_font)!=TAG_SVG_font) ) return;
font = gf_node_get_private(node_font);
if (!font) return;
gf_svg_flatten_attributes((SVG_Element*)node, &atts);
if (gf_node_get_tag(node)==TAG_SVG_missing_glyph) {
GF_SAFEALLOC(st, SVG_GlyphStack);
if (!st) return;
goto reg_common;
}
if (!atts.unicode) return;
GF_SAFEALLOC(st, SVG_GlyphStack);
if (!st) return;
utf8 = (u8 *) *atts.unicode;
len = gf_utf8_mbstowcs(utf_name, 200, (const char **) &utf8);
if (len==1) {
st->glyph.utf_name = utf_name[0];
st->uni_len = 1;
} else {
st->glyph.utf_name = (u32) (PTR_TO_U_CAST st);
st->unicode = gf_malloc(sizeof(u16)*len);
st->uni_len = (u16) len;
memcpy(st->unicode, utf_name, sizeof(u16)*len);
}
reg_common:
st->glyph.ID = (u32)(PTR_TO_U_CAST st);
st->font = font;
st->glyph.horiz_advance = font->max_advance_h;
if (atts.horiz_adv_x) st->glyph.horiz_advance = FIX2INT( gf_ceil(atts.horiz_adv_x->value) );
if (atts.d) {
st->glyph.path = atts.d;
gf_path_get_bounds(atts.d, &rc);
st->glyph.width = FIX2INT( gf_ceil(rc.width) );
st->glyph.height = FIX2INT( gf_ceil(rc.height) );
}
st->glyph.vert_advance = st->glyph.height;
if (!st->glyph.vert_advance)
st->glyph.vert_advance = font->max_advance_v;
if (!font->glyph) {
font->glyph = &st->glyph;
} else {
glyph = font->glyph;
while (glyph->next) glyph = glyph->next;
glyph->next = &st->glyph;
}
gf_node_set_private(node, st);
gf_node_set_callback_function(node, svg_traverse_glyph);
}
typedef struct
{
GF_Font *font;
GF_Font *alias;
GF_Compositor *compositor;
GF_MediaObject *mo;
} FontURIStack;
static Bool svg_font_uri_check(GF_Node *node, FontURIStack *st)
{
GF_Font *font;
GF_Node *font_elt;
SVGAllAttributes atts;
gf_svg_flatten_attributes((SVG_Element*)node, &atts);
if (!atts.xlink_href) return 0;
if (atts.xlink_href->type == XMLRI_ELEMENTID) {
if (!atts.xlink_href->target) atts.xlink_href->target = gf_sg_find_node_by_name(gf_node_get_graph(node), atts.xlink_href->string+1);
} else {
GF_SceneGraph *ext_sg;
char *font_name = strchr(atts.xlink_href->string, '#');
if (!font_name) return 0;
if (!st->mo) {
st->mo = gf_mo_load_xlink_resource(node, 0, 0, -1);
if (!st->mo) return 0;
}
ext_sg = gf_mo_get_scenegraph(st->mo);
if (!ext_sg) return 0;
atts.xlink_href->target = gf_sg_find_node_by_name(ext_sg, font_name+1);
if (!atts.xlink_href->target) return 0;
}
font_elt = atts.xlink_href->target;
if (gf_node_get_tag(font_elt) != TAG_SVG_font) return 0;
font = gf_node_get_private(font_elt);
if (!font) return 0;
st->alias = font;
gf_mo_is_done(st->mo);
font->not_loaded = 0;
return 1;
}
GF_Font *svg_font_uri_get_alias(void *udta)
{
GF_Node *node = (GF_Node *)udta;
FontURIStack *st = gf_node_get_private(node);
if (!st->alias && !svg_font_uri_check(node, st)) {
return NULL;
}
return st->alias;
}
static GF_Err svg_font_uri_get_glyphs(void *udta, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *lang, Bool *is_rtl)
{
return GF_URL_ERROR;
}
static GF_Glyph *svg_font_uri_load_glyph(void *udta, u32 glyph_name)
{
return NULL;
}
static void svg_traverse_font_face_uri(GF_Node *node, void *rs, Bool is_destroy)
{
if (is_destroy) {
FontURIStack *st = gf_node_get_private(node);
if (st) {
gf_font_manager_unregister_font(st->font->ft_mgr, st->font);
if (st->font->name) gf_free(st->font->name);
gf_free(st->font);
if (st->mo) gf_mo_unload_xlink_resource(node, st->mo);
gf_free(st);
}
}
}
void compositor_init_svg_font_face_uri(GF_Compositor *compositor, GF_Node *node)
{
GF_Node *par;
GF_Font *font;
FontURIStack *stack;
GF_Err e;
SVGAllAttributes atts;
par = gf_node_get_parent(node, 0);
if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face_src)) return;
par = gf_node_get_parent(par, 0);
if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face)) return;
gf_svg_flatten_attributes((SVG_Element*)node, &atts);
if (!atts.xlink_href) return;
gf_svg_flatten_attributes((SVG_Element*)par, &atts);
if (!atts.font_family) return;
if (gf_compositor_svg_set_font(compositor->font_manager, atts.font_family->value, 0, 1) != NULL)
return;
GF_SAFEALLOC(font, GF_Font);
if (!font) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate font for svg font face URI\n"));
return;
}
e = gf_font_manager_register_font(compositor->font_manager, font);
if (e) {
gf_free(font);
return;
}
GF_SAFEALLOC(stack, FontURIStack);
if (!stack) {
gf_free(font);
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg font face URI stack\n"));
return;
}
stack->font = font;
stack->compositor = compositor;
font->ft_mgr = compositor->font_manager;
font->get_glyphs = svg_font_uri_get_glyphs;
font->load_glyph = svg_font_uri_load_glyph;
font->get_alias = svg_font_uri_get_alias;
font->udta = node;
font->name = gf_strdup(atts.font_family->value);
gf_node_set_private(node, stack);
gf_node_set_callback_function(node, svg_traverse_font_face_uri);
font->not_loaded = 1;
compositor->fonts_pending++;
svg_font_uri_check(node, stack);
}
#endif