Commit a8029873 authored by Federico Mena Quintero's avatar Federico Mena Quintero

gitlab#201 - Overhaul attributes parsing with perfect hashing (merge phf-attributes)

Libxml2 hands us a NULL-terminated char** of key/value pairs for each
element's attributes.

We would store those pairs in a GHashTable to be able to do random
access on the list of attributes for each element - this was
RsvgPropertyBag.

However, big chains of lookups for "all the attributes" are slower
than necessary.  Instead, we would rather process the attributes that
are actually present in each element, instead of trying to match all
elements for every known attribute to the styling system.

We now use a Perfect Hash Function to map attribute names to enum
values.  We can then switch() in C, or match in Rust, over these enum
values - the compiler can optimize that very well.

We now have a Rust implementation called PropertyBag, which wraps
libxml2's array of key/value pairs.  It uses iteration rather than
lookups, and avoids string copies.

This produces a good minor speedup.  Across 5588 SVG icons on my
machine:

2.42.2 - 10.25 sec
phf-attributes - 9.64 sec
parents 4bb8914a 24450902
Pipeline #3885 passed with stage
in 20 minutes and 4 seconds
......@@ -24,6 +24,7 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES = \
librsvg-enum-types.h \
librsvg-features.c \
librsvg-features.h \
rsvg-attributes.h \
rsvg-base-file-util.c \
rsvg-base.c \
rsvg-cairo-clip.c \
......@@ -64,7 +65,9 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES = \
RUST_SOURCES = \
rust/Cargo.toml \
rust/build.rs \
rust/src/aspect_ratio.rs \
rust/src/attributes.rs \
rust/src/bbox.rs \
rust/src/chars.rs \
rust/src/clip_path.rs \
......
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 sts=4 expandtab: */
#ifndef RSVG_ATTRIBUTES_H
#define RSVG_ATTRIBUTES_H
#include <glib.h>
/* Keep this in sync with rust/src/build.rs */
typedef enum {
RSVG_ATTRIBUTE_ALTERNATE,
RSVG_ATTRIBUTE_AMPLITUDE,
RSVG_ATTRIBUTE_AZIMUTH,
RSVG_ATTRIBUTE_BASE_FREQUENCY,
RSVG_ATTRIBUTE_BASELINE_SHIFT,
RSVG_ATTRIBUTE_BIAS,
RSVG_ATTRIBUTE_CLASS,
RSVG_ATTRIBUTE_CLIP_PATH,
RSVG_ATTRIBUTE_CLIP_RULE,
RSVG_ATTRIBUTE_CLIP_PATH_UNITS,
RSVG_ATTRIBUTE_COLOR,
RSVG_ATTRIBUTE_COMP_OP,
RSVG_ATTRIBUTE_CX,
RSVG_ATTRIBUTE_CY,
RSVG_ATTRIBUTE_D,
RSVG_ATTRIBUTE_DIFFUSE_CONSTANT,
RSVG_ATTRIBUTE_DIRECTION,
RSVG_ATTRIBUTE_DISPLAY,
RSVG_ATTRIBUTE_DIVISOR,
RSVG_ATTRIBUTE_DX,
RSVG_ATTRIBUTE_DY,
RSVG_ATTRIBUTE_EDGE_MODE,
RSVG_ATTRIBUTE_ELEVATION,
RSVG_ATTRIBUTE_ENABLE_BACKGROUND,
RSVG_ATTRIBUTE_ENCODING,
RSVG_ATTRIBUTE_EXPONENT,
RSVG_ATTRIBUTE_FILL,
RSVG_ATTRIBUTE_FILL_OPACITY,
RSVG_ATTRIBUTE_FILL_RULE,
RSVG_ATTRIBUTE_FILTER,
RSVG_ATTRIBUTE_FILTER_UNITS,
RSVG_ATTRIBUTE_FLOOD_COLOR,
RSVG_ATTRIBUTE_FLOOD_OPACITY,
RSVG_ATTRIBUTE_FONT_FAMILY,
RSVG_ATTRIBUTE_FONT_SIZE,
RSVG_ATTRIBUTE_FONT_STRETCH,
RSVG_ATTRIBUTE_FONT_STYLE,
RSVG_ATTRIBUTE_FONT_VARIANT,
RSVG_ATTRIBUTE_FONT_WEIGHT,
RSVG_ATTRIBUTE_FX,
RSVG_ATTRIBUTE_FY,
RSVG_ATTRIBUTE_GRADIENT_TRANSFORM,
RSVG_ATTRIBUTE_GRADIENT_UNITS,
RSVG_ATTRIBUTE_HEIGHT,
RSVG_ATTRIBUTE_HREF,
RSVG_ATTRIBUTE_ID,
RSVG_ATTRIBUTE_IN,
RSVG_ATTRIBUTE_IN2,
RSVG_ATTRIBUTE_INTERCEPT,
RSVG_ATTRIBUTE_K1,
RSVG_ATTRIBUTE_K2,
RSVG_ATTRIBUTE_K3,
RSVG_ATTRIBUTE_K4,
RSVG_ATTRIBUTE_KERNEL_MATRIX,
RSVG_ATTRIBUTE_KERNEL_UNIT_LENGTH,
RSVG_ATTRIBUTE_LETTER_SPACING,
RSVG_ATTRIBUTE_LIGHTING_COLOR,
RSVG_ATTRIBUTE_LIMITING_CONE_ANGLE,
RSVG_ATTRIBUTE_MARKER,
RSVG_ATTRIBUTE_MARKER_END,
RSVG_ATTRIBUTE_MARKER_MID,
RSVG_ATTRIBUTE_MARKER_START,
RSVG_ATTRIBUTE_MARKER_HEIGHT,
RSVG_ATTRIBUTE_MARKER_UNITS,
RSVG_ATTRIBUTE_MARKER_WIDTH,
RSVG_ATTRIBUTE_MASK,
RSVG_ATTRIBUTE_MASK_CONTENT_UNITS,
RSVG_ATTRIBUTE_MASK_UNITS,
RSVG_ATTRIBUTE_MODE,
RSVG_ATTRIBUTE_NUM_OCTAVES,
RSVG_ATTRIBUTE_OFFSET,
RSVG_ATTRIBUTE_OPACITY,
RSVG_ATTRIBUTE_OPERATOR,
RSVG_ATTRIBUTE_ORDER,
RSVG_ATTRIBUTE_ORIENT,
RSVG_ATTRIBUTE_OVERFLOW,
RSVG_ATTRIBUTE_PARSE,
RSVG_ATTRIBUTE_PATH,
RSVG_ATTRIBUTE_PATTERN_CONTENT_UNITS,
RSVG_ATTRIBUTE_PATTERN_TRANSFORM,
RSVG_ATTRIBUTE_PATTERN_UNITS,
RSVG_ATTRIBUTE_POINTS,
RSVG_ATTRIBUTE_POINTS_AT_X,
RSVG_ATTRIBUTE_POINTS_AT_Y,
RSVG_ATTRIBUTE_POINTS_AT_Z,
RSVG_ATTRIBUTE_PRESERVE_ALPHA,
RSVG_ATTRIBUTE_PRESERVE_ASPECT_RATIO,
RSVG_ATTRIBUTE_PRIMITIVE_UNITS,
RSVG_ATTRIBUTE_R,
RSVG_ATTRIBUTE_RADIUS,
RSVG_ATTRIBUTE_REF_X,
RSVG_ATTRIBUTE_REF_Y,
RSVG_ATTRIBUTE_REQUIRED_EXTENSIONS,
RSVG_ATTRIBUTE_REQUIRED_FEATURES,
RSVG_ATTRIBUTE_RESULT,
RSVG_ATTRIBUTE_RX,
RSVG_ATTRIBUTE_RY,
RSVG_ATTRIBUTE_SCALE,
RSVG_ATTRIBUTE_SEED,
RSVG_ATTRIBUTE_SHAPE_RENDERING,
RSVG_ATTRIBUTE_SLOPE,
RSVG_ATTRIBUTE_SPECULAR_CONSTANT,
RSVG_ATTRIBUTE_SPECULAR_EXPONENT,
RSVG_ATTRIBUTE_SPREAD_METHOD,
RSVG_ATTRIBUTE_STD_DEVIATION,
RSVG_ATTRIBUTE_STITCH_TILES,
RSVG_ATTRIBUTE_STOP_COLOR,
RSVG_ATTRIBUTE_STOP_OPACITY,
RSVG_ATTRIBUTE_STROKE,
RSVG_ATTRIBUTE_STROKE_DASHARRAY,
RSVG_ATTRIBUTE_STROKE_DASHOFFSET,
RSVG_ATTRIBUTE_STROKE_LINECAP,
RSVG_ATTRIBUTE_STROKE_LINEJOIN,
RSVG_ATTRIBUTE_STROKE_MITERLIMIT,
RSVG_ATTRIBUTE_STROKE_OPACITY,
RSVG_ATTRIBUTE_STROKE_WIDTH,
RSVG_ATTRIBUTE_STYLE,
RSVG_ATTRIBUTE_SURFACE_SCALE,
RSVG_ATTRIBUTE_SYSTEM_LANGUAGE,
RSVG_ATTRIBUTE_TABLE_VALUES,
RSVG_ATTRIBUTE_TARGET_X,
RSVG_ATTRIBUTE_TARGET_Y,
RSVG_ATTRIBUTE_TEXT_ANCHOR,
RSVG_ATTRIBUTE_TEXT_DECORATION,
RSVG_ATTRIBUTE_TEXT_RENDERING,
RSVG_ATTRIBUTE_TRANSFORM,
RSVG_ATTRIBUTE_TYPE,
RSVG_ATTRIBUTE_UNICODE_BIDI,
RSVG_ATTRIBUTE_VALUES,
RSVG_ATTRIBUTE_VERTS,
RSVG_ATTRIBUTE_VIEW_BOX,
RSVG_ATTRIBUTE_VISIBILITY,
RSVG_ATTRIBUTE_WIDTH,
RSVG_ATTRIBUTE_WRITING_MODE,
RSVG_ATTRIBUTE_X,
RSVG_ATTRIBUTE_X1,
RSVG_ATTRIBUTE_Y1,
RSVG_ATTRIBUTE_X2,
RSVG_ATTRIBUTE_Y2,
RSVG_ATTRIBUTE_X_CHANNEL_SELECTOR,
RSVG_ATTRIBUTE_XLINK_HREF,
RSVG_ATTRIBUTE_XML_LANG,
RSVG_ATTRIBUTE_XML_SPACE,
RSVG_ATTRIBUTE_Y,
RSVG_ATTRIBUTE_Y_CHANNEL_SELECTOR,
RSVG_ATTRIBUTE_Z,
} RsvgAttribute;
/* Implemented in rust/src/attributes.rs */
G_GNUC_INTERNAL
gboolean rsvg_attribute_from_name (const char *name, RsvgAttribute *out_attr);
#endif /* RSVG_ATTRIBUTES_H */
......@@ -28,6 +28,7 @@
#define _GNU_SOURCE 1
#include "rsvg-private.h"
#include "rsvg-attributes.h"
#include "rsvg-css.h"
#include "rsvg-styles.h"
#include "rsvg-shapes.h"
......@@ -122,7 +123,6 @@ typedef struct {
/* hide this fact from the general public */
typedef RsvgSaxHandlerExtra RsvgSaxHandlerTitle;
typedef RsvgSaxHandlerExtra RsvgSaxHandlerDesc;
typedef RsvgSaxHandlerExtra RsvgSaxHandlerMetadata;
static void
rsvg_style_handler_free (RsvgSaxHandler * self)
......@@ -145,7 +145,7 @@ rsvg_style_handler_characters (RsvgSaxHandler * self, const char *ch, gssize len
}
static void
rsvg_style_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
rsvg_style_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag atts)
{
}
......@@ -168,9 +168,10 @@ static void
rsvg_start_style (RsvgHandle *handle, RsvgPropertyBag *atts)
{
RsvgSaxHandlerStyle *handler = g_new0 (RsvgSaxHandlerStyle, 1);
const char *type;
type = rsvg_property_bag_lookup (atts, "type");
RsvgPropertyBagIter *iter;
const char *key;
RsvgAttribute attr;
const char *value;
handler->super.free = rsvg_style_handler_free;
handler->super.characters = rsvg_style_handler_characters;
......@@ -180,6 +181,9 @@ rsvg_start_style (RsvgHandle *handle, RsvgPropertyBag *atts)
handler->style = g_string_new (NULL);
handler->parent = (RsvgSaxHandlerDefs *) handle->priv->handler;
handle->priv->handler = &handler->super;
/* FIXME: See these:
*
* https://www.w3.org/TR/SVG/styling.html#StyleElementTypeAttribute
......@@ -192,10 +196,17 @@ rsvg_start_style (RsvgHandle *handle, RsvgPropertyBag *atts)
* See where is_text_css is used to see where we parse the contents
* of the style element.
*/
handler->is_text_css = (type == NULL) || (g_ascii_strcasecmp (type, "text/css") == 0);
handler->is_text_css = TRUE;
handler->parent = (RsvgSaxHandlerDefs *) handle->priv->handler;
handle->priv->handler = &handler->super;
iter = rsvg_property_bag_iter_begin (atts);
while (rsvg_property_bag_iter_next (iter, &key, &attr, &value)) {
if (attr == RSVG_ATTRIBUTE_TYPE) {
handler->is_text_css = (g_ascii_strcasecmp (value, "text/css") == 0);
}
}
rsvg_property_bag_iter_end (iter);
}
static void
......@@ -207,17 +218,6 @@ add_node_to_handle (RsvgHandle *handle, RsvgNode *node)
g_ptr_array_add (handle->priv->all_nodes, rsvg_node_ref (node));
}
static void
register_node_in_defs (RsvgHandle *handle, RsvgNode *node, RsvgPropertyBag *atts)
{
const char *id;
id = rsvg_property_bag_lookup (atts, "id");
if (id) {
rsvg_defs_register_node_by_id (handle->priv->defs, id, node);
}
}
static void
push_element_name (RsvgHandle *handle, const char *name)
{
......@@ -388,29 +388,47 @@ get_node_creator_for_element_name (const char *name)
}
static void
node_set_atts (RsvgNode * node, RsvgHandle *handle, const NodeCreator *creator, RsvgPropertyBag * atts)
node_set_atts (RsvgNode * node, RsvgHandle *handle, const NodeCreator *creator, RsvgPropertyBag atts)
{
if (rsvg_property_bag_size (atts) > 0) {
const char *id;
const char *klazz;
RsvgPropertyBagIter *iter;
const char *key;
RsvgAttribute attr;
const char *value;
rsvg_node_set_atts (node, handle, atts);
const char *id = NULL;
const char *klazz = NULL;
/* The "svg" node is special; it will load its id/class
* attributes until the end, when rsvg_end_element() calls
* _rsvg_node_svg_apply_atts()
*/
if (rsvg_node_get_type (node) != RSVG_NODE_TYPE_SVG) {
id = rsvg_property_bag_lookup (atts, "id");
iter = rsvg_property_bag_iter_begin (atts);
if (creator->supports_class_attribute)
klazz = rsvg_property_bag_lookup (atts, "class");
else
klazz = NULL;
while (rsvg_property_bag_iter_next (iter, &key, &attr, &value)) {
switch (attr) {
case RSVG_ATTRIBUTE_ID:
id = value;
rsvg_defs_register_node_by_id (handle->priv->defs, id, node);
break;
case RSVG_ATTRIBUTE_CLASS:
if (creator->supports_class_attribute) {
klazz = value;
}
break;
rsvg_parse_style_attrs (handle, node, creator->element_name, klazz, id, atts);
default:
break;
}
}
rsvg_property_bag_iter_end (iter);
rsvg_node_set_atts (node, handle, atts);
/* The "svg" node is special; it will load its id/class
* attributes until the end, when rsvg_end_element() calls
* rsvg_node_svg_apply_atts()
*/
if (rsvg_node_get_type (node) != RSVG_NODE_TYPE_SVG) {
rsvg_parse_style_attrs (handle, node, creator->element_name, klazz, id, atts);
}
}
static void
......@@ -430,7 +448,6 @@ rsvg_standard_element_start (RsvgHandle *handle, const char *name, RsvgPropertyB
push_element_name (handle, name);
add_node_to_handle (handle, newnode);
register_node_in_defs (handle, newnode, atts);
if (handle->priv->currentnode) {
rsvg_node_add_child (handle->priv->currentnode, newnode);
......@@ -446,7 +463,7 @@ rsvg_standard_element_start (RsvgHandle *handle, const char *name, RsvgPropertyB
newnode = rsvg_node_unref (newnode);
}
/* extra (title, desc, metadata) */
/* extra (title, desc) */
static void
rsvg_extra_handler_free (RsvgSaxHandler * self)
......@@ -482,7 +499,7 @@ rsvg_extra_handler_characters (RsvgSaxHandler * self, const char *ch, gssize len
}
static void
rsvg_extra_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
rsvg_extra_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag atts)
{
}
......@@ -550,54 +567,6 @@ rsvg_start_title (RsvgHandle *handle)
/* end title */
/* start metadata */
static void
rsvg_metadata_props_enumerate (const char *key, const char *value, gpointer user_data)
{
GString *metadata = (GString *) user_data;
g_string_append_printf (metadata, "%s=\"%s\" ", key, value);
}
static void
rsvg_metadata_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
{
RsvgSaxHandlerMetadata *z = (RsvgSaxHandlerMetadata *) self;
rsvg_extra_handler_start (self, name, atts);
if (!z->string)
return;
g_string_append_printf (z->string, "<%s ", name);
rsvg_property_bag_enumerate (atts, rsvg_metadata_props_enumerate, z->string);
g_string_append (z->string, ">\n");
}
static void
rsvg_metadata_handler_end (RsvgSaxHandler * self, const char *name)
{
RsvgSaxHandlerMetadata *z = (RsvgSaxHandlerMetadata *) self;
if (strcmp (name, z->name) != 0) {
if (z->string)
g_string_append_printf (z->string, "</%s>\n", name);
} else {
rsvg_extra_handler_end (self, name);
}
}
static void
rsvg_start_metadata (RsvgHandle *handle)
{
RsvgSaxHandlerMetadata *handler = rsvg_start_extra (handle, "metadata", &handle->priv->metadata);
handler->super.start_element = rsvg_metadata_handler_start;
handler->super.end_element = rsvg_metadata_handler_end;
}
/* end metadata */
/* start xinclude */
typedef struct _RsvgSaxHandlerXinclude {
......@@ -629,7 +598,7 @@ rsvg_xinclude_handler_characters (RsvgSaxHandler * self, const char *ch, gssize
}
static void
rsvg_xinclude_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
rsvg_xinclude_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag atts)
{
RsvgSaxHandlerXinclude *z = (RsvgSaxHandlerXinclude *) self;
......@@ -723,68 +692,91 @@ static void
rsvg_start_xinclude (RsvgHandle *handle, RsvgPropertyBag * atts)
{
RsvgSaxHandlerXinclude *handler;
const char *href, *parse;
const char *href = NULL;
const char *parse = NULL;
const char *encoding = NULL;
gboolean success = FALSE;
href = rsvg_property_bag_lookup (atts, "href");
if (href == NULL)
goto fallback;
RsvgPropertyBagIter *iter;
const char *key;
RsvgAttribute attr;
const char *value;
parse = rsvg_property_bag_lookup (atts, "parse");
if (parse && !strcmp (parse, "text")) {
char *data;
gsize data_len;
const char *encoding;
iter = rsvg_property_bag_iter_begin (atts);
data = _rsvg_handle_acquire_data (handle, href, NULL, &data_len, NULL);
if (data == NULL)
goto fallback;
while (rsvg_property_bag_iter_next (iter, &key, &attr, &value)) {
switch (attr) {
case RSVG_ATTRIBUTE_HREF:
href = value;
break;
encoding = rsvg_property_bag_lookup (atts, "encoding");
if (encoding && g_ascii_strcasecmp (encoding, "UTF-8") != 0) {
char *text_data;
gsize text_data_len;
case RSVG_ATTRIBUTE_PARSE:
parse = value;
break;
text_data = g_convert (data, data_len, "utf-8", encoding, NULL,
&text_data_len, NULL);
g_free (data);
case RSVG_ATTRIBUTE_ENCODING:
encoding = value;
break;
data = text_data;
data_len = text_data_len;
default:
break;
}
}
rsvg_characters_impl (handle, data, data_len);
rsvg_property_bag_iter_end (iter);
g_free (data);
} else {
/* xml */
GInputStream *stream;
GError *err = NULL;
xmlParserCtxtPtr xml_parser;
if (href) {
if (parse && !strcmp (parse, "text")) {
char *data;
gsize data_len;
stream = _rsvg_handle_acquire_stream (handle, href, NULL, NULL);
if (stream == NULL)
goto fallback;
data = _rsvg_handle_acquire_data (handle, href, NULL, &data_len, NULL);
if (data) {
if (encoding && g_ascii_strcasecmp (encoding, "UTF-8") != 0) {
char *text_data;
gsize text_data_len;
xml_parser = create_xml_stream_parser (handle,
stream,
NULL, /* cancellable */
&err);
text_data = g_convert (data, data_len, "utf-8", encoding, NULL,
&text_data_len, NULL);
g_free (data);
g_object_unref (stream);
data = text_data;
data_len = text_data_len;
}
if (xml_parser) {
(void) xmlParseDocument (xml_parser);
rsvg_characters_impl (handle, data, data_len);
xml_parser = rsvg_free_xml_parser_and_doc (xml_parser);
}
g_free (data);
g_clear_error (&err);
}
success = TRUE;
}
} else {
/* xml */
GInputStream *stream;
GError *err = NULL;
xmlParserCtxtPtr xml_parser;
stream = _rsvg_handle_acquire_stream (handle, href, NULL, NULL);
if (stream) {
xml_parser = create_xml_stream_parser (handle,
stream,
NULL, /* cancellable */
&err);
success = TRUE;
g_object_unref (stream);
fallback:
if (xml_parser) {
(void) xmlParseDocument (xml_parser);
xml_parser = rsvg_free_xml_parser_and_doc (xml_parser);
}
g_clear_error (&err);
success = TRUE;
}
}
}
/* needed to handle xi:fallback */
handler = g_new0 (RsvgSaxHandlerXinclude, 1);
......@@ -805,7 +797,7 @@ rsvg_start_xinclude (RsvgHandle *handle, RsvgPropertyBag * atts)
static void
rsvg_start_element (void *data, const xmlChar * name, const xmlChar ** atts)
{
RsvgPropertyBag *bag;
RsvgPropertyBag bag;
RsvgHandle *handle = (RsvgHandle *) data;
bag = rsvg_property_bag_new ((const char **) atts);
......@@ -826,8 +818,6 @@ rsvg_start_element (void *data, const xmlChar * name, const xmlChar ** atts)
rsvg_start_title (handle);
else if (!strcmp ((const char *) name, "desc"))
rsvg_start_desc (handle);
else if (!strcmp ((const char *) name, "metadata"))
rsvg_start_metadata (handle);
else if (!strcmp ((const char *) name, "include")) /* xi:include */
rsvg_start_xinclude (handle, bag);
else
......@@ -1099,36 +1089,59 @@ rsvg_processing_instruction (void *user_data, const xmlChar * target, const xmlC
xml_atts = rsvg_css_parse_xml_attribute_string ((const char *) data);
if (xml_atts) {
const char *alternate = NULL;
const char *type = NULL;
const char *href = NULL;
RsvgPropertyBagIter *iter;
const char *key;
RsvgAttribute attr;
const char *value;
atts = rsvg_property_bag_new ((const char **) xml_atts);
value = rsvg_property_bag_lookup (atts, "alternate");
if (!value || !value[0] || (strcmp (value, "no") != 0)) {
value = rsvg_property_bag_lookup (atts, "type");
if (value && strcmp (value, "text/css") == 0) {
value = rsvg_property_bag_lookup (atts, "href");
if (value && value[0]) {
char *style_data;
gsize style_data_len;
char *mime_type = NULL;
style_data = _rsvg_handle_acquire_data (handle,
value,
&mime_type,
&style_data_len,
NULL);
if (style_data &&
mime_type &&
strcmp (mime_type, "text/css") == 0) {
rsvg_parse_cssbuffer (handle, style_data, style_data_len);
}
g_free (mime_type);
g_free (style_data);
}
iter = rsvg_property_bag_iter_begin (atts);
while (rsvg_property_bag_iter_next (iter, &key, &attr, &value)) {
switch (attr) {
case RSVG_ATTRIBUTE_ALTERNATE:
alternate = value;
break;
case RSVG_ATTRIBUTE_TYPE:
type = value;
break;
case RSVG_ATTRIBUTE_HREF:
href = value;
break;
default:
break;
}
}
if ((!alternate || strcmp (alternate, "no") != 0)
&& type && strcmp (type, "text/css") == 0
&& href) {
char *style_data;
gsize style_data_len;
char *mime_type = NULL;
style_data = _rsvg_handle_acquire_data (handle,
href,
&mime_type,
&style_data_len,
NULL);
if (style_data &&
mime_type &&
strcmp (mime_type, "text/css") == 0) {
rsvg_parse_cssbuffer (handle, style_data, style_data_len);
}
g_free (mime_type);
g_free (style_data);
}
rsvg_property_bag_free (atts);
g_strfreev (xml_atts);
}
......
......@@ -170,34 +170,46 @@ rsvg_cond_parse_system_language (const char *value)
gboolean
rsvg_eval_switch_attributes (RsvgPropertyBag * atts, gboolean * p_has_cond)
{
gboolean permitted = TRUE;
gboolean required_features_ok = TRUE;
gboolean required_extensions_ok = TRUE;
gboolean system_language_ok = TRUE;
gboolean has_cond = FALSE;
if (atts && rsvg_property_bag_size (atts)) {
const char *value;
RsvgPropertyBagIter *iter;
const char *key;
RsvgAttribute attr;
const char *value;
iter = rsvg_property_bag_iter_begin (atts);
if ((value = rsvg_property_bag_lookup (atts, "requiredFeatures"))) {
permitted =
rsvg_cond_fulfills_requirement (value, implemented_features,
nb_implemented_features);
while (rsvg_property_bag_iter_next (iter, &key, &attr, &value)) {
switch (attr) {
case RSVG_ATTRIBUTE_REQUIRED_FEATURES:
required_features_ok = rsvg_cond_fulfills_requirement (value, implemented_features,
nb_implemented_features);
has_cond = TRUE;
}
break;
if (permitted && (value = rsvg_property_bag_lookup (atts, "requiredExtensions"))) {
permitted =
rsvg_cond_fulfills_requirement (value, implemented_extensions,
nb_implemented_extensions);
case RSVG_ATTRIBUTE_REQUIRED_EXTENSIONS:
required_extensions_ok = rsvg_cond_fulfills_requirement (value, implemented_extensions,
nb_implemented_extensions);
has_cond = TRUE;
}
break;
if (permitted && (value = rsvg_property_bag_lookup (atts, "systemLanguage"))) {
permitted = rsvg_cond_parse_system_language (value);
case RSVG_ATTRIBUTE_SYSTEM_LANGUAGE:
system_language_ok = rsvg_cond_parse_system_language (value);
has_cond = TRUE;
break;
default:
break;
}
}
rsvg_property_bag_iter_end (iter);
if (p_has_cond)
*p_has_cond = has_cond;
return permitted;
return required_features_ok && required_extensions_ok && system_language_ok;
}
This diff is collapsed.
......@@ -219,8 +219,6 @@ rsvg_handle_dispose (GObject *instance)
g_string_free (self->priv->title, TRUE);
if (self->priv->desc)
g_string_free (self->priv->desc, TRUE);
if (self->priv->metadata)
g_string_free (self->priv->metadata, TRUE);
if (self->priv->base_uri)
g_free (self->priv->base_uri);
......@@ -703,10 +701,7 @@ rsvg_handle_get_base_uri (RsvgHandle * handle)
* rsvg_handle_get_metadata:
* @handle: An #RsvgHandle
*
* Returns the SVG's metadata in UTF-8 or %NULL. You must make a copy
* of this metadata if you wish to use it after @handle has been freed.
*
* Returns: (nullable): The SVG's title
* Returns: (nullable): This function always returns #NULL.
*
* Since: 2.9
*
......@@ -717,10 +712,7 @@ rsvg_handle_get_metadata (RsvgHandle * handle)
{