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);
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, 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 val = val / pow(10.0, eval);
137 int p = sp_svg_number_write_d(buf, val, tprec, 0, padf);
138 buf[p++] = 'e';
139 p += sp_svg_number_write_i(buf + p, (int) eval);
140 return p;
141 }
142 }
144 /* Length */
146 bool SVGLength::read(gchar const *str)
147 {
148 if (!str) {
149 return false;
150 }
152 SVGLength::Unit u;
153 float v;
154 float c;
155 if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
156 return false;
157 }
159 _set = true;
160 unit = u;
161 value = v;
162 computed = c;
164 return true;
165 }
167 static bool svg_length_absolute_unit(SVGLength::Unit u)
168 {
169 return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
170 }
172 bool SVGLength::readAbsolute(gchar const *str)
173 {
174 if (!str) {
175 return false;
176 }
178 SVGLength::Unit u;
179 float v;
180 float c;
181 if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
182 return false;
183 }
185 if (svg_length_absolute_unit(u) == false) {
186 return false;
187 }
189 _set = true;
190 unit = u;
191 value = v;
192 computed = c;
194 return true;
195 }
198 unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
199 {
200 if (!str) {
201 return 0;
202 }
204 SVGLength::Unit unit;
205 float computed;
206 if (!sp_svg_length_read_lff(str, &unit, NULL, &computed, NULL)) {
207 // failed to read
208 return 0;
209 }
211 if (svg_length_absolute_unit(unit) == false) {
212 return 0;
213 }
215 *length = computed;
217 return 1;
218 }
220 std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
221 {
222 if (!str) {
223 return std::vector<SVGLength>();
224 }
226 SVGLength::Unit unit;
227 float value;
228 float computed;
229 char *next = (char *) str;
230 std::vector<SVGLength> list;
232 while (sp_svg_length_read_lff(next, &unit, &value, &computed, &next)) {
234 SVGLength length;
235 length.set(unit, value, computed);
236 list.push_back(length);
238 while (next && *next &&
239 (*next == ',' || *next == ' ' || *next == '\n' || *next == '\r' || *next == '\t')) {
240 // the list can be comma- or space-separated, but we will be generous and accept
241 // a mix, including newlines and tabs
242 next++;
243 }
245 if (!next || !*next) {
246 break;
247 }
248 }
250 return list;
251 }
254 #define UVAL(a,b) (((unsigned int) (a) << 8) | (unsigned int) (b))
256 static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next)
257 {
258 if (!str) {
259 return 0;
260 }
262 gchar const *e;
263 float const v = g_ascii_strtod(str, (char **) &e);
264 if (e == str) {
265 return 0;
266 }
268 if (!e[0]) {
269 /* Unitless */
270 if (unit) {
271 *unit = SVGLength::NONE;
272 }
273 if (val) {
274 *val = v;
275 }
276 if (computed) {
277 *computed = v;
278 }
279 if (next) {
280 *next = NULL; // no more values
281 }
282 return 1;
283 } else if (!g_ascii_isalnum(e[0])) {
284 /* Unitless or percent */
285 if (e[0] == '%') {
286 /* Percent */
287 if (e[1] && g_ascii_isalnum(e[1])) {
288 return 0;
289 }
290 if (unit) {
291 *unit = SVGLength::PERCENT;
292 }
293 if (val) {
294 *val = v * 0.01;
295 }
296 if (computed) {
297 *computed = v * 0.01;
298 }
299 if (next) {
300 *next = (char *) e + 1;
301 }
302 return 1;
303 } else {
304 /* Unitless */
305 if (unit) {
306 *unit = SVGLength::NONE;
307 }
308 if (val) {
309 *val = v;
310 }
311 if (computed) {
312 *computed = v;
313 }
314 if (next) {
315 *next = (char *) e;
316 }
317 return 1;
318 }
319 } else if (e[1] && !g_ascii_isalnum(e[2])) {
320 /* TODO: Allow the number of px per inch to vary (document preferences, X server
321 * or whatever). E.g. don't fill in computed here, do it at the same time as
322 * percentage units are done. */
323 unsigned int const uval = UVAL(e[0], e[1]);
324 switch (uval) {
325 case UVAL('p','x'):
326 if (unit) {
327 *unit = SVGLength::PX;
328 }
329 if (computed) {
330 *computed = v;
331 }
332 break;
333 case UVAL('p','t'):
334 if (unit) {
335 *unit = SVGLength::PT;
336 }
337 if (computed) {
338 *computed = v * PX_PER_PT;
339 }
340 break;
341 case UVAL('p','c'):
342 if (unit) {
343 *unit = SVGLength::PC;
344 }
345 if (computed) {
346 *computed = v * 12 * PX_PER_PT;
347 }
348 break;
349 case UVAL('m','m'):
350 if (unit) {
351 *unit = SVGLength::MM;
352 }
353 if (computed) {
354 *computed = v * PX_PER_MM;
355 }
356 break;
357 case UVAL('c','m'):
358 if (unit) {
359 *unit = SVGLength::CM;
360 }
361 if (computed) {
362 *computed = v * PX_PER_CM;
363 }
364 break;
365 case UVAL('i','n'):
366 if (unit) {
367 *unit = SVGLength::INCH;
368 }
369 if (computed) {
370 *computed = v * PX_PER_IN;
371 }
372 break;
373 case UVAL('e','m'):
374 if (unit) {
375 *unit = SVGLength::EM;
376 }
377 break;
378 case UVAL('e','x'):
379 if (unit) {
380 *unit = SVGLength::EX;
381 }
382 break;
383 default:
384 /* Invalid */
385 return 0;
386 break;
387 }
388 if (val) {
389 *val = v;
390 }
391 if (next) {
392 *next = (char *) e + 2;
393 }
394 return 1;
395 }
397 /* Invalid */
398 return 0;
399 }
401 unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
402 {
403 float a;
404 float b;
405 unsigned int r = sp_svg_length_read_lff(str, unit, &a, &b, NULL);
406 if (r) {
407 if (value) {
408 *value = a;
409 }
410 if (computed) {
411 *computed = b;
412 }
413 }
414 return r;
415 }
417 void SVGLength::set(SVGLength::Unit u, float v, float c)
418 {
419 _set = true;
420 unit = u;
421 value = v;
422 computed = c;
423 }
425 void SVGLength::unset(SVGLength::Unit u, float v, float c)
426 {
427 _set = false;
428 unit = u;
429 value = v;
430 computed = c;
431 }
433 void SVGLength::update(double em, double ex, double scale)
434 {
435 if (unit == EM) {
436 computed = value * em;
437 } else if (unit == EX) {
438 computed = value * ex;
439 } else if (unit == PERCENT) {
440 computed = value * scale;
441 }
442 }
444 double sp_svg_read_percentage(char const *str, double def)
445 {
446 if (str == NULL) {
447 return def;
448 }
450 char *u;
451 double v = g_ascii_strtod(str, &u);
452 while (isspace(*u)) {
453 if (*u == '\0') {
454 return v;
455 }
456 u++;
457 }
458 if (*u == '%') {
459 v /= 100.0;
460 }
462 return v;
463 }
465 gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
466 {
467 switch (unit) {
468 case SVGLength::NONE: return "";
469 case SVGLength::PX: return "";
470 case SVGLength::PT: return "pt";
471 case SVGLength::PC: return "pc";
472 case SVGLength::MM: return "mm";
473 case SVGLength::CM: return "cm";
474 case SVGLength::INCH: return "in";
475 case SVGLength::EM: return "em";
476 case SVGLength::EX: return "ex";
477 case SVGLength::PERCENT: return "%";
478 }
479 return "";
480 }
482 /**
483 * N.B.\ This routine will sometimes return strings with `e' notation, so is unsuitable for CSS
484 * lengths (which don't allow scientific `e' notation).
485 */
486 std::string sp_svg_length_write_with_units(SVGLength const &length)
487 {
488 Inkscape::SVGOStringStream os;
489 if (length.unit == SVGLength::PERCENT) {
490 os << 100*length.value << sp_svg_length_get_css_units(length.unit);
491 } else {
492 os << length.value << sp_svg_length_get_css_units(length.unit);
493 }
494 return os.str();
495 }
498 void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
499 {
500 if (!read(str)) {
501 unset(u, v, c);
502 }
503 }
506 /*
507 Local Variables:
508 mode:c++
509 c-file-style:"stroustrup"
510 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
511 indent-tabs-mode:nil
512 fill-column:99
513 End:
514 */
515 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :