3f04588eae6af5b8c118603e630d9c3b3cc70a0e
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 // TODO must add a buffer length parameter for safety:
68 static unsigned int sp_svg_number_write_ui(gchar *buf, unsigned int val)
69 {
70 unsigned int i = 0;
71 char c[16u];
72 do {
73 c[16u - (++i)] = '0' + (val % 10u);
74 val /= 10u;
75 } while (val > 0u);
77 memcpy(buf, &c[16u - i], i);
78 buf[i] = 0;
80 return i;
81 }
83 static unsigned int sp_svg_number_write_i(gchar *buf, int bufLen, int val)
84 {
85 int p = 0;
86 unsigned int uval;
87 if (val < 0) {
88 buf[p++] = '-';
89 uval = (unsigned int)-val;
90 } else {
91 uval = (unsigned int)val;
92 }
94 p += sp_svg_number_write_ui(buf+p, uval);
96 return p;
97 }
99 static unsigned sp_svg_number_write_d(gchar *buf, int bufLen, double val, unsigned int tprec, unsigned int fprec)
100 {
101 /* Process sign */
102 int i = 0;
103 if (val < 0.0) {
104 buf[i++] = '-';
105 val = fabs(val);
106 }
108 /* Determine number of integral digits */
109 int idigits = 0;
110 if (val >= 1.0) {
111 idigits = (int) floor(log10(val)) + 1;
112 }
114 /* Determine the actual number of fractional digits */
115 fprec = MAX(static_cast<int>(fprec), static_cast<int>(tprec) - idigits);
116 /* Round value */
117 val += 0.5 / pow(10.0, fprec);
118 /* Extract integral and fractional parts */
119 double dival = floor(val);
120 double fval = val - dival;
121 /* Write integra */
122 if (idigits > (int)tprec) {
123 i += sp_svg_number_write_ui(buf + i, (unsigned int)floor(dival/pow(10.0, idigits-tprec) + .5));
124 for(unsigned int j=0; j<(unsigned int)idigits-tprec; j++) {
125 buf[i+j] = '0';
126 }
127 i += idigits-tprec;
128 } else {
129 i += sp_svg_number_write_ui(buf + i, (unsigned int)dival);
130 }
131 int end_i = i;
132 if (fprec > 0 && fval > 0.0) {
133 buf[i++] = '.';
134 do {
135 fval *= 10.0;
136 dival = floor(fval);
137 fval -= dival;
138 int const int_dival = (int) dival;
139 buf[i++] = '0' + int_dival;
140 if (int_dival != 0) {
141 end_i = i;
142 }
143 fprec -= 1;
144 } while(fprec > 0 && fval > 0.0);
145 }
146 buf[end_i] = 0;
147 return end_i;
148 }
150 unsigned int sp_svg_number_write_de(gchar *buf, int bufLen, double val, unsigned int tprec, int min_exp)
151 {
152 int eval = (int)floor(log10(fabs(val)));
153 if (val == 0.0 || eval < min_exp) {
154 return sp_svg_number_write_ui(buf, 0);
155 }
156 unsigned int maxnumdigitsWithoutExp = // This doesn't include the sign because it is included in either representation
157 eval<0?tprec+(unsigned int)-eval+1:
158 eval+1<(int)tprec?tprec+1:
159 (unsigned int)eval+1;
160 unsigned int maxnumdigitsWithExp = tprec + ( eval<0 ? 4 : 3 ); // It's not necessary to take larger exponents into account, because then maxnumdigitsWithoutExp is DEFINITELY larger
161 if (maxnumdigitsWithoutExp <= maxnumdigitsWithExp) {
162 return sp_svg_number_write_d(buf, bufLen, val, tprec, 0);
163 } else {
164 val = eval < 0 ? val * pow(10.0, -eval) : val / pow(10.0, eval);
165 int p = sp_svg_number_write_d(buf, bufLen, val, tprec, 0);
166 buf[p++] = 'e';
167 p += sp_svg_number_write_i(buf + p, bufLen - p, eval);
168 return p;
169 }
170 }
172 /* Length */
174 bool SVGLength::read(gchar const *str)
175 {
176 if (!str) {
177 return false;
178 }
180 SVGLength::Unit u;
181 float v;
182 float c;
183 if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
184 return false;
185 }
187 _set = true;
188 unit = u;
189 value = v;
190 computed = c;
192 return true;
193 }
195 static bool svg_length_absolute_unit(SVGLength::Unit u)
196 {
197 return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
198 }
200 bool SVGLength::readAbsolute(gchar const *str)
201 {
202 if (!str) {
203 return false;
204 }
206 SVGLength::Unit u;
207 float v;
208 float c;
209 if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
210 return false;
211 }
213 if (svg_length_absolute_unit(u) == false) {
214 return false;
215 }
217 _set = true;
218 unit = u;
219 value = v;
220 computed = c;
222 return true;
223 }
226 unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
227 {
228 if (!str) {
229 return 0;
230 }
232 SVGLength::Unit unit;
233 float computed;
234 if (!sp_svg_length_read_lff(str, &unit, NULL, &computed, NULL)) {
235 // failed to read
236 return 0;
237 }
239 if (svg_length_absolute_unit(unit) == false) {
240 return 0;
241 }
243 *length = computed;
245 return 1;
246 }
248 std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
249 {
250 if (!str) {
251 return std::vector<SVGLength>();
252 }
254 SVGLength::Unit unit;
255 float value;
256 float computed;
257 char *next = (char *) str;
258 std::vector<SVGLength> list;
260 while (sp_svg_length_read_lff(next, &unit, &value, &computed, &next)) {
262 SVGLength length;
263 length.set(unit, value, computed);
264 list.push_back(length);
266 while (next && *next &&
267 (*next == ',' || *next == ' ' || *next == '\n' || *next == '\r' || *next == '\t')) {
268 // the list can be comma- or space-separated, but we will be generous and accept
269 // a mix, including newlines and tabs
270 next++;
271 }
273 if (!next || !*next) {
274 break;
275 }
276 }
278 return list;
279 }
282 #define UVAL(a,b) (((unsigned int) (a) << 8) | (unsigned int) (b))
284 static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next)
285 {
286 if (!str) {
287 return 0;
288 }
290 gchar const *e;
291 float const v = g_ascii_strtod(str, (char **) &e);
292 if (e == str) {
293 return 0;
294 }
296 if (!e[0]) {
297 /* Unitless */
298 if (unit) {
299 *unit = SVGLength::NONE;
300 }
301 if (val) {
302 *val = v;
303 }
304 if (computed) {
305 *computed = v;
306 }
307 if (next) {
308 *next = NULL; // no more values
309 }
310 return 1;
311 } else if (!g_ascii_isalnum(e[0])) {
312 /* Unitless or percent */
313 if (e[0] == '%') {
314 /* Percent */
315 if (e[1] && g_ascii_isalnum(e[1])) {
316 return 0;
317 }
318 if (unit) {
319 *unit = SVGLength::PERCENT;
320 }
321 if (val) {
322 *val = v * 0.01;
323 }
324 if (computed) {
325 *computed = v * 0.01;
326 }
327 if (next) {
328 *next = (char *) e + 1;
329 }
330 return 1;
331 } else {
332 /* Unitless */
333 if (unit) {
334 *unit = SVGLength::NONE;
335 }
336 if (val) {
337 *val = v;
338 }
339 if (computed) {
340 *computed = v;
341 }
342 if (next) {
343 *next = (char *) e;
344 }
345 return 1;
346 }
347 } else if (e[1] && !g_ascii_isalnum(e[2])) {
348 /* TODO: Allow the number of px per inch to vary (document preferences, X server
349 * or whatever). E.g. don't fill in computed here, do it at the same time as
350 * percentage units are done. */
351 unsigned int const uval = UVAL(e[0], e[1]);
352 switch (uval) {
353 case UVAL('p','x'):
354 if (unit) {
355 *unit = SVGLength::PX;
356 }
357 if (computed) {
358 *computed = v;
359 }
360 break;
361 case UVAL('p','t'):
362 if (unit) {
363 *unit = SVGLength::PT;
364 }
365 if (computed) {
366 *computed = v * PX_PER_PT;
367 }
368 break;
369 case UVAL('p','c'):
370 if (unit) {
371 *unit = SVGLength::PC;
372 }
373 if (computed) {
374 *computed = v * PX_PER_PC;
375 }
376 break;
377 case UVAL('m','m'):
378 if (unit) {
379 *unit = SVGLength::MM;
380 }
381 if (computed) {
382 *computed = v * PX_PER_MM;
383 }
384 break;
385 case UVAL('c','m'):
386 if (unit) {
387 *unit = SVGLength::CM;
388 }
389 if (computed) {
390 *computed = v * PX_PER_CM;
391 }
392 break;
393 case UVAL('i','n'):
394 if (unit) {
395 *unit = SVGLength::INCH;
396 }
397 if (computed) {
398 *computed = v * PX_PER_IN;
399 }
400 break;
401 case UVAL('f','t'):
402 if (unit) {
403 *unit = SVGLength::FOOT;
404 }
405 if (computed) {
406 *computed = v * PX_PER_FT;
407 }
408 break;
409 case UVAL('e','m'):
410 if (unit) {
411 *unit = SVGLength::EM;
412 }
413 break;
414 case UVAL('e','x'):
415 if (unit) {
416 *unit = SVGLength::EX;
417 }
418 break;
419 default:
420 /* Invalid */
421 return 0;
422 break;
423 }
424 if (val) {
425 *val = v;
426 }
427 if (next) {
428 *next = (char *) e + 2;
429 }
430 return 1;
431 }
433 /* Invalid */
434 return 0;
435 }
437 unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
438 {
439 float a;
440 float b;
441 unsigned int r = sp_svg_length_read_lff(str, unit, &a, &b, NULL);
442 if (r) {
443 if (value) {
444 *value = a;
445 }
446 if (computed) {
447 *computed = b;
448 }
449 }
450 return r;
451 }
453 void SVGLength::set(SVGLength::Unit u, float v, float c)
454 {
455 _set = true;
456 unit = u;
457 value = v;
458 computed = c;
459 }
461 void SVGLength::unset(SVGLength::Unit u, float v, float c)
462 {
463 _set = false;
464 unit = u;
465 value = v;
466 computed = c;
467 }
469 void SVGLength::update(double em, double ex, double scale)
470 {
471 if (unit == EM) {
472 computed = value * em;
473 } else if (unit == EX) {
474 computed = value * ex;
475 } else if (unit == PERCENT) {
476 computed = value * scale;
477 }
478 }
480 double sp_svg_read_percentage(char const *str, double def)
481 {
482 if (str == NULL) {
483 return def;
484 }
486 char *u;
487 double v = g_ascii_strtod(str, &u);
488 while (isspace(*u)) {
489 if (*u == '\0') {
490 return v;
491 }
492 u++;
493 }
494 if (*u == '%') {
495 v /= 100.0;
496 }
498 return v;
499 }
501 gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
502 {
503 switch (unit) {
504 case SVGLength::NONE: return "";
505 case SVGLength::PX: return "";
506 case SVGLength::PT: return "pt";
507 case SVGLength::PC: return "pc";
508 case SVGLength::MM: return "mm";
509 case SVGLength::CM: return "cm";
510 case SVGLength::INCH: return "in";
511 case SVGLength::FOOT: return ""; // Does not have a "foot" unit string in the SVG spec
512 case SVGLength::EM: return "em";
513 case SVGLength::EX: return "ex";
514 case SVGLength::PERCENT: return "%";
515 }
516 return "";
517 }
519 /**
520 * N.B.\ This routine will sometimes return strings with `e' notation, so is unsuitable for CSS
521 * lengths (which don't allow scientific `e' notation).
522 */
523 std::string sp_svg_length_write_with_units(SVGLength const &length)
524 {
525 Inkscape::SVGOStringStream os;
526 if (length.unit == SVGLength::PERCENT) {
527 os << 100*length.value << sp_svg_length_get_css_units(length.unit);
528 } else {
529 os << length.value << sp_svg_length_get_css_units(length.unit);
530 }
531 return os.str();
532 }
535 void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
536 {
537 if (!read(str)) {
538 unset(u, v, c);
539 }
540 }
543 /*
544 Local Variables:
545 mode:c++
546 c-file-style:"stroustrup"
547 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
548 indent-tabs-mode:nil
549 fill-column:99
550 End:
551 */
552 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :