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