1 #define __SP_SVG_LENGTH_C__
3 /*
4 * SVG data parser
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * Copyright (C) 1999-2002 Lauris Kaplinski
11 *
12 * This code is in public domain
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #include <math.h>
20 #include <glib/gstrfuncs.h>
21 #include "svg.h"
22 #include "stringstream.h"
23 #include "../unit-constants.h"
26 static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next);
28 #ifndef MAX
29 # define MAX(a,b) ((a < b) ? (b) : (a))
30 #endif
32 unsigned int sp_svg_number_read_f(gchar const *str, float *val)
33 {
34 if (!str) {
35 return 0;
36 }
38 char *e;
39 float const v = g_ascii_strtod(str, &e);
40 if ((gchar const *) e == str) {
41 return 0;
42 }
44 *val = v;
45 return 1;
46 }
48 unsigned int sp_svg_number_read_d(gchar const *str, double *val)
49 {
50 if (!str) {
51 return 0;
52 }
54 char *e;
55 double const v = g_ascii_strtod(str, &e);
56 if ((gchar const *) e == str) {
57 return 0;
58 }
60 *val = v;
61 return 1;
62 }
64 static unsigned int sp_svg_number_write_i(gchar *buf, int val)
65 {
66 int p = 0;
67 if (val < 0) {
68 buf[p++] = '-';
69 val = -val;
70 }
72 int i = 0;
73 char c[32];
74 do {
75 c[32 - (++i)] = '0' + (val % 10);
76 val /= 10;
77 } while (val > 0);
79 memcpy(buf + p, &c[32 - i], i);
80 p += i;
81 buf[p] = 0;
83 return p;
84 }
86 static unsigned sp_svg_number_write_d(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
87 {
88 /* Process sign */
89 int i = 0;
90 if (val < 0.0) {
91 buf[i++] = '-';
92 val = fabs(val);
93 }
95 /* Determine number of integral digits */
96 int idigits = 0;
97 if (val >= 1.0) {
98 idigits = (int) floor(log10(val));
99 }
101 /* Determine the actual number of fractional digits */
102 fprec = MAX(fprec, tprec - idigits - 1);
103 /* Round value */
104 val += 0.5 * pow(10.0, - ((double) fprec));
105 /* Extract integral and fractional parts */
106 double dival = floor(val);
107 int ival = (int) dival;
108 double fval = val - dival;
109 /* Write integra */
110 i += sp_svg_number_write_i(buf + i, ival);
111 int end_i = i;
112 if (fprec > 0 && (padf || fval > 0.0)) {
113 buf[i++] = '.';
114 while ((fprec > 0) && (padf || (fval > 0.0))) {
115 fval *= 10.0;
116 dival = floor(fval);
117 fval -= dival;
118 int const int_dival = (int) dival;
119 buf[i++] = '0' + int_dival;
120 if (int_dival != 0) {
121 end_i = i;
122 }
123 fprec -= 1;
124 }
125 }
126 buf[end_i] = 0;
127 return end_i;
128 }
130 unsigned int sp_svg_number_write_de(gchar *buf, double val, unsigned int tprec, int min_exp, unsigned int padf)
131 {
132 if (val == 0.0 || (fabs(val) >= 0.1 && fabs(val) < 10000000)) {
133 return sp_svg_number_write_d(buf, val, tprec, 0, padf);
134 } else {
135 double eval = floor(log10(fabs(val)));
136 if ((int) eval < min_exp) {
137 return sp_svg_number_write_d(buf, 0, tprec, 0, padf);
138 } else {
139 val = val / pow(10.0, eval);
140 int p = sp_svg_number_write_d(buf, val, tprec, 0, padf);
141 buf[p++] = 'e';
142 p += sp_svg_number_write_i(buf + p, (int) eval);
143 return p;
144 }
145 }
146 }
148 /* Length */
150 bool SVGLength::read(gchar const *str)
151 {
152 if (!str) {
153 return false;
154 }
156 SVGLength::Unit u;
157 float v;
158 float c;
159 if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
160 return false;
161 }
163 _set = true;
164 unit = u;
165 value = v;
166 computed = c;
168 return true;
169 }
171 static bool svg_length_absolute_unit(SVGLength::Unit u)
172 {
173 return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
174 }
176 bool SVGLength::readAbsolute(gchar const *str)
177 {
178 if (!str) {
179 return false;
180 }
182 SVGLength::Unit u;
183 float v;
184 float c;
185 if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
186 return false;
187 }
189 if (svg_length_absolute_unit(u) == false) {
190 return false;
191 }
193 _set = true;
194 unit = u;
195 value = v;
196 computed = c;
198 return true;
199 }
202 unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
203 {
204 if (!str) {
205 return 0;
206 }
208 SVGLength::Unit unit;
209 float computed;
210 if (!sp_svg_length_read_lff(str, &unit, NULL, &computed, NULL)) {
211 // failed to read
212 return 0;
213 }
215 if (svg_length_absolute_unit(unit) == false) {
216 return 0;
217 }
219 *length = computed;
221 return 1;
222 }
224 std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
225 {
226 if (!str) {
227 return std::vector<SVGLength>();
228 }
230 SVGLength::Unit unit;
231 float value;
232 float computed;
233 char *next = (char *) str;
234 std::vector<SVGLength> list;
236 while (sp_svg_length_read_lff(next, &unit, &value, &computed, &next)) {
238 SVGLength length;
239 length.set(unit, value, computed);
240 list.push_back(length);
242 while (next && *next &&
243 (*next == ',' || *next == ' ' || *next == '\n' || *next == '\r' || *next == '\t')) {
244 // the list can be comma- or space-separated, but we will be generous and accept
245 // a mix, including newlines and tabs
246 next++;
247 }
249 if (!next || !*next) {
250 break;
251 }
252 }
254 return list;
255 }
258 #define UVAL(a,b) (((unsigned int) (a) << 8) | (unsigned int) (b))
260 static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next)
261 {
262 if (!str) {
263 return 0;
264 }
266 gchar const *e;
267 float const v = g_ascii_strtod(str, (char **) &e);
268 if (e == str) {
269 return 0;
270 }
272 if (!e[0]) {
273 /* Unitless */
274 if (unit) {
275 *unit = SVGLength::NONE;
276 }
277 if (val) {
278 *val = v;
279 }
280 if (computed) {
281 *computed = v;
282 }
283 if (next) {
284 *next = NULL; // no more values
285 }
286 return 1;
287 } else if (!g_ascii_isalnum(e[0])) {
288 /* Unitless or percent */
289 if (e[0] == '%') {
290 /* Percent */
291 if (e[1] && g_ascii_isalnum(e[1])) {
292 return 0;
293 }
294 if (unit) {
295 *unit = SVGLength::PERCENT;
296 }
297 if (val) {
298 *val = v * 0.01;
299 }
300 if (computed) {
301 *computed = v * 0.01;
302 }
303 if (next) {
304 *next = (char *) e + 1;
305 }
306 return 1;
307 } else {
308 /* Unitless */
309 if (unit) {
310 *unit = SVGLength::NONE;
311 }
312 if (val) {
313 *val = v;
314 }
315 if (computed) {
316 *computed = v;
317 }
318 if (next) {
319 *next = (char *) e;
320 }
321 return 1;
322 }
323 } else if (e[1] && !g_ascii_isalnum(e[2])) {
324 /* TODO: Allow the number of px per inch to vary (document preferences, X server
325 * or whatever). E.g. don't fill in computed here, do it at the same time as
326 * percentage units are done. */
327 unsigned int const uval = UVAL(e[0], e[1]);
328 switch (uval) {
329 case UVAL('p','x'):
330 if (unit) {
331 *unit = SVGLength::PX;
332 }
333 if (computed) {
334 *computed = v;
335 }
336 break;
337 case UVAL('p','t'):
338 if (unit) {
339 *unit = SVGLength::PT;
340 }
341 if (computed) {
342 *computed = v * PX_PER_PT;
343 }
344 break;
345 case UVAL('p','c'):
346 if (unit) {
347 *unit = SVGLength::PC;
348 }
349 if (computed) {
350 *computed = v * 12 * PX_PER_PT;
351 }
352 break;
353 case UVAL('m','m'):
354 if (unit) {
355 *unit = SVGLength::MM;
356 }
357 if (computed) {
358 *computed = v * PX_PER_MM;
359 }
360 break;
361 case UVAL('c','m'):
362 if (unit) {
363 *unit = SVGLength::CM;
364 }
365 if (computed) {
366 *computed = v * PX_PER_CM;
367 }
368 break;
369 case UVAL('i','n'):
370 if (unit) {
371 *unit = SVGLength::INCH;
372 }
373 if (computed) {
374 *computed = v * PX_PER_IN;
375 }
376 break;
377 case UVAL('e','m'):
378 if (unit) {
379 *unit = SVGLength::EM;
380 }
381 break;
382 case UVAL('e','x'):
383 if (unit) {
384 *unit = SVGLength::EX;
385 }
386 break;
387 default:
388 /* Invalid */
389 return 0;
390 break;
391 }
392 if (val) {
393 *val = v;
394 }
395 if (next) {
396 *next = (char *) e + 2;
397 }
398 return 1;
399 }
401 /* Invalid */
402 return 0;
403 }
405 unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
406 {
407 float a;
408 float b;
409 unsigned int r = sp_svg_length_read_lff(str, unit, &a, &b, NULL);
410 if (r) {
411 if (value) {
412 *value = a;
413 }
414 if (computed) {
415 *computed = b;
416 }
417 }
418 return r;
419 }
421 void SVGLength::set(SVGLength::Unit u, float v, float c)
422 {
423 _set = true;
424 unit = u;
425 value = v;
426 computed = c;
427 }
429 void SVGLength::unset(SVGLength::Unit u, float v, float c)
430 {
431 _set = false;
432 unit = u;
433 value = v;
434 computed = c;
435 }
437 void SVGLength::update(double em, double ex, double scale)
438 {
439 if (unit == EM) {
440 computed = value * em;
441 } else if (unit == EX) {
442 computed = value * ex;
443 } else if (unit == PERCENT) {
444 computed = value * scale;
445 }
446 }
448 double sp_svg_read_percentage(char const *str, double def)
449 {
450 if (str == NULL) {
451 return def;
452 }
454 char *u;
455 double v = g_ascii_strtod(str, &u);
456 while (isspace(*u)) {
457 if (*u == '\0') {
458 return v;
459 }
460 u++;
461 }
462 if (*u == '%') {
463 v /= 100.0;
464 }
466 return v;
467 }
469 gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
470 {
471 switch (unit) {
472 case SVGLength::NONE: return "";
473 case SVGLength::PX: return "";
474 case SVGLength::PT: return "pt";
475 case SVGLength::PC: return "pc";
476 case SVGLength::MM: return "mm";
477 case SVGLength::CM: return "cm";
478 case SVGLength::INCH: return "in";
479 case SVGLength::EM: return "em";
480 case SVGLength::EX: return "ex";
481 case SVGLength::PERCENT: return "%";
482 }
483 return "";
484 }
486 /**
487 * N.B.\ This routine will sometimes return strings with `e' notation, so is unsuitable for CSS
488 * lengths (which don't allow scientific `e' notation).
489 */
490 std::string sp_svg_length_write_with_units(SVGLength const &length)
491 {
492 Inkscape::SVGOStringStream os;
493 if (length.unit == SVGLength::PERCENT) {
494 os << 100*length.value << sp_svg_length_get_css_units(length.unit);
495 } else {
496 os << length.value << sp_svg_length_get_css_units(length.unit);
497 }
498 return os.str();
499 }
502 void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
503 {
504 if (!read(str)) {
505 unset(u, v, c);
506 }
507 }
510 /*
511 Local Variables:
512 mode:c++
513 c-file-style:"stroustrup"
514 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
515 indent-tabs-mode:nil
516 fill-column:99
517 End:
518 */
519 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :