/*
* Copyright © 2025 Red Hat , Inc
*
* SPDX - License - Identifier : LGPL - 2 . 1 - or - later
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 . 1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see < http : //www.gnu.org/licenses/>.
*
* Authors : Matthias Clasen < mclasen @ redhat . com >
*/
#include "config.h"
#include "gtksvgnumbersprivate.h"
#include "gtksvgvalueprivate.h"
#include "gtksvgnumberprivate.h"
#include "gtksvgstringutilsprivate.h"
#include "gtksvgutilsprivate.h"
#include "gtksvgelementinternal.h"
typedef struct
{
double value;
SvgUnit unit;
} Number;
static inline gboolean
number_equal (const Number *n0,
const Number *n1)
{
return n0->unit == n1->unit && n0->value == n1->value;
}
typedef struct
{
SvgValue base;
unsigned int n_values;
Number values[1 ];
} SvgNumbers;
static unsigned int
svg_numbers_size (unsigned int n)
{
return sizeof (SvgNumbers) + (n > 0 ? n - 1 : 0 ) * sizeof (Number);
}
static gboolean
svg_numbers_equal (const SvgValue *value0,
const SvgValue *value1)
{
const SvgNumbers *p0 = (const SvgNumbers *) value0;
const SvgNumbers *p1 = (const SvgNumbers *) value1;
if (p0->n_values != p1->n_values)
return FALSE ;
for (unsigned int i = 0 ; i < p0->n_values; i++)
{
if (!number_equal (&p0->values[i], &p1->values[i]))
return FALSE ;
}
return TRUE ;
}
static SvgValue *
svg_numbers_accumulate (const SvgValue *value0,
const SvgValue *value1,
SvgComputeContext *context,
int n)
{
return NULL;
}
static void
svg_numbers_print (const SvgValue *value,
GString *string)
{
const SvgNumbers *p = (const SvgNumbers *) value;
for (unsigned int i = 0 ; i < p->n_values; i++)
{
if (i > 0 )
g_string_append_c (string, ' ' );
string_append_double (string, "" , p->values[i].value);
g_string_append (string, svg_unit_name (p->values[i].unit));
}
}
static SvgValue * svg_numbers_interpolate (const SvgValue *value0,
const SvgValue *value1,
SvgComputeContext *context,
double t);
static SvgValue * svg_numbers_resolve (const SvgValue *value,
SvgProperty attr,
unsigned int idx,
SvgElement *shape,
SvgComputeContext *context);
static const SvgValueClass SVG_NUMBERS_CLASS = {
"SvgNumbers" ,
svg_value_default_free,
svg_numbers_equal,
svg_numbers_interpolate,
svg_numbers_accumulate,
svg_numbers_print,
svg_value_default_distance,
svg_numbers_resolve,
};
SvgValue *
svg_numbers_new (double *values,
unsigned int n_values)
{
SvgNumbers *result;
result = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (n_values));
result->n_values = n_values;
for (unsigned int i = 0 ; i < n_values; i++)
{
result->values[i].unit = SVG_UNIT_NUMBER;
result->values[i].value = values[i];
}
return (SvgValue *) result;
}
SvgValue *
svg_numbers_new_full (double *values,
SvgUnit *units,
unsigned int n_values)
{
SvgNumbers *result;
result = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (n_values));
result->n_values = n_values;
for (unsigned int i = 0 ; i < n_values; i++)
{
result->values[i].unit = units[i];
result->values[i].value = values[i];
}
return (SvgValue *) result;
}
SvgValue *
svg_numbers_new_identity_matrix (void )
{
static SvgValue *id;
if (id == NULL)
{
id = svg_numbers_new ((double []) { 1 , 0 , 0 , 0 , 0 ,
0 , 1 , 0 , 0 , 0 ,
0 , 0 , 1 , 0 , 0 ,
0 , 0 , 0 , 1 , 0 }, 20 );
id->ref_count = 0 ;
}
return id;
}
SvgValue *
svg_numbers_new_none (void )
{
static SvgNumbers none = { { &SVG_NUMBERS_CLASS, 0 }, .n_values = 0 , .values[0 ] = { .unit = SVG_UNIT_NUMBER, .value = 0 } };
return (SvgValue *) &none;
}
SvgValue *
svg_numbers_new1 (double value)
{
static SvgNumbers singletons[] = {
{ { &SVG_NUMBERS_CLASS, 0 }, .n_values = 1 , .values[0 ] = { .unit = SVG_UNIT_NUMBER, .value = 0 } },
{ { &SVG_NUMBERS_CLASS, 0 }, .n_values = 1 , .values[0 ] = { .unit = SVG_UNIT_NUMBER, .value = 1 } },
{ { &SVG_NUMBERS_CLASS, 0 }, .n_values = 1 , .values[0 ] = { .unit = SVG_UNIT_NUMBER, .value = 2 } },
};
SvgNumbers *p;
for (unsigned int i = 0 ; i < G_N_ELEMENTS (singletons); i++)
{
if (singletons[i].values[0 ].value == value)
return (SvgValue *) &singletons[i];
}
p = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (1 ));
p->n_values = 1 ;
p->values[0 ].unit = SVG_UNIT_NUMBER;
p->values[0 ].value = value;
return (SvgValue *) p;
}
SvgValue *
svg_numbers_new_00 (void )
{
static SvgNumbers *value = NULL;
if (value == NULL)
{
value = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (2 ));
((SvgValue *) value)->ref_count = 0 ;
value->values[0 ].value = value->values[1 ].value = 0 ;
value->values[0 ].unit = value->values[1 ].unit = SVG_UNIT_NUMBER;
}
return (SvgValue *) value;
}
SvgValue *
svg_numbers_new2 (double v1, SvgUnit u1,
double v2, SvgUnit u2)
{
SvgNumbers *n;
if (v1 == 0 && u1 == SVG_UNIT_NUMBER &&
v2 == 0 && u2 == SVG_UNIT_NUMBER)
return svg_numbers_new_00 ();
n = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (2 ));
n->n_values = 2 ;
n->values[0 ].value = v1;
n->values[0 ].unit = u1;
n->values[1 ].value = v2;
n->values[1 ].unit = u2;
return (SvgValue *) n;
}
SvgValue *
svg_numbers_parse2 (GtkCssParser *parser,
unsigned int flags)
{
SvgNumbers *p;
GArray *numbers;
Number n;
numbers = g_array_new (FALSE , FALSE , sizeof (Number));
while (1 )
{
gtk_css_parser_skip_whitespace (parser);
if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
break ;
if (!svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, flags, &n.value, &n.unit))
{
g_array_unref (numbers);
return NULL;
}
g_array_append_val (numbers, n);
skip_whitespace_and_optional_comma (parser);
}
p = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS,
svg_numbers_size (numbers->len));
p->n_values = numbers->len;
for (unsigned int i = 0 ; i < numbers->len; i++)
p->values[i] = g_array_index (numbers, Number, i);
g_array_unref (numbers);
return (SvgValue *) p;
}
SvgValue *
svg_numbers_parse (GtkCssParser *parser)
{
return svg_numbers_parse2 (parser, SVG_PARSE_NUMBER);
}
static SvgValue *
svg_numbers_interpolate (const SvgValue *value0,
const SvgValue *value1,
SvgComputeContext *context,
double t)
{
const SvgNumbers *p0 = (const SvgNumbers *) value0;
const SvgNumbers *p1 = (const SvgNumbers *) value1;
SvgNumbers *p;
if (p0->n_values != p1->n_values)
{
if (t < 0 .5 )
return svg_value_ref ((SvgValue *) value0);
else
return svg_value_ref ((SvgValue *) value1);
}
p = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (p0->n_values));
p->n_values = p0->n_values;
for (unsigned int i = 0 ; i < p0->n_values; i++)
{
g_assert (p0->values[i].unit != SVG_UNIT_PERCENTAGE);
p->values[i].unit = SVG_UNIT_NUMBER;
p->values[i].value = lerp (t, p0->values[i].value, p1->values[i].value);
}
return (SvgValue *) p;
}
static SvgValue *
svg_numbers_resolve (const SvgValue *value,
SvgProperty attr,
unsigned int idx,
SvgElement *shape,
SvgComputeContext *context)
{
SvgNumbers *orig = (SvgNumbers *) value;
SvgNumbers *p;
double size;
if (orig->n_values == 0 )
return svg_value_ref ((SvgValue *) orig);
if (attr == SVG_PROPERTY_TRANSFORM_ORIGIN ||
attr == SVG_PROPERTY_TEXT_X ||
attr == SVG_PROPERTY_TEXT_Y ||
attr == SVG_PROPERTY_TEXT_DX ||
attr == SVG_PROPERTY_TEXT_DY)
{
double font_size;
if (value == svg_numbers_new_00 ())
return svg_value_ref ((SvgValue *) value);
p = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (svg_numbers_get_length (value)));
p->n_values = svg_numbers_get_length (value);
memcpy (p->values, orig->values, sizeof (Number) * p->n_values);
font_size = shape_get_current_font_size (shape, attr, context);
for (unsigned int i = 0 ; i < p->n_values; i++)
{
switch ((unsigned int ) p->values[i].unit)
{
case SVG_UNIT_NUMBER:
case SVG_UNIT_PERCENTAGE:
break ;
case SVG_UNIT_PX:
case SVG_UNIT_PT:
case SVG_UNIT_IN:
case SVG_UNIT_CM:
case SVG_UNIT_MM:
p->values[i].value = absolute_length_to_px (p->values[i].value, p->values[i].unit);
p->values[i].unit = SVG_UNIT_PX;
break ;
case SVG_UNIT_VW:
case SVG_UNIT_VH:
case SVG_UNIT_VMIN:
case SVG_UNIT_VMAX:
p->values[i].value = viewport_relative_to_px (p->values[i].value, p->values[i].unit, context->viewport);
p->values[i].unit = SVG_UNIT_PX;
break ;
case SVG_UNIT_EM:
p->values[i].value = p->values[i].value * font_size;
p->values[i].unit = SVG_UNIT_PX;
break ;
case SVG_UNIT_EX:
p->values[i].value = p->values[i].value * 0 .5 * font_size;
p->values[i].unit = SVG_UNIT_PX;
break ;
default :
g_assert_not_reached ();
}
}
return (SvgValue *) p;
}
p = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (orig->n_values));
p->n_values = orig->n_values;
size = normalized_diagonal (context->viewport);
for (unsigned int i = 0 ; i < orig->n_values; i++)
{
if (orig->values[i].unit == SVG_UNIT_PERCENTAGE)
{
p->values[i].unit = SVG_UNIT_NUMBER;
p->values[i].value = orig->values[i].value * size / 100 ;
}
else
{
p->values[i].value = orig->values[i].value;
}
}
return (SvgValue *) p;
}
unsigned int
svg_numbers_get_length (const SvgValue *value)
{
const SvgNumbers *n = (const SvgNumbers *) value;
g_assert (value->class == &SVG_NUMBERS_CLASS);
return n->n_values;
}
SvgUnit
svg_numbers_get_unit (const SvgValue *value,
unsigned int pos)
{
const SvgNumbers *n = (const SvgNumbers *) value;
g_assert (value->class == &SVG_NUMBERS_CLASS);
g_assert (pos < n->n_values);
return n->values[pos].unit;
}
double
svg_numbers_get (const SvgValue *value,
unsigned int pos,
double one_hundred_percent)
{
const SvgNumbers *n = (const SvgNumbers *) value;
g_assert (value->class == &SVG_NUMBERS_CLASS);
g_assert (pos < n->n_values);
if (n->values[pos].unit == SVG_UNIT_PERCENTAGE)
return n->values[pos].value / 100 * one_hundred_percent;
else
return n->values[pos].value;
}
SvgValue *
svg_numbers_drop_last (const SvgValue *v)
{
const SvgNumbers *o = (const SvgNumbers *) v;
SvgNumbers *n;
if (v->ref_count == 1 )
{
((SvgNumbers *) v)->n_values--;
return (SvgValue *) v;
}
n = (SvgNumbers *) svg_value_alloc (&SVG_NUMBERS_CLASS, svg_numbers_size (o->n_values - 1 ));
n->n_values = o->n_values - 1 ;
for (unsigned int i = 0 ; i < n->n_values; i++)
n->values[i] = o->values[i];
return (SvgValue *) n;
}
Messung V0.5 in Prozent C=100 H=96 G=97
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland