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