Code

peeled back the gboolean code as it hit on some complexity theory principles...
[inkscape.git] / src / svg / svg-length.cpp
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     }
43     
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     }
59     
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     }
71     
72     int i = 0;
73     char c[32];
74     do {
75         c[32 - (++i)] = '0' + (val % 10);
76         val /= 10;
77     } while (val > 0);
78     
79     memcpy(buf + p, &c[32 - i], i);
80     p += i;
81     buf[p] = 0;
82     
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     }
94     
95     /* Determine number of integral digits */
96     int idigits = 0;
97     if (val >= 1.0) {
98         idigits = (int) floor(log10(val));
99     }
100     
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;
130 unsigned int sp_svg_number_write_de(gchar *buf, double val, unsigned int tprec, int min_exp, unsigned int padf)
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     }
148 /* Length */
150 bool SVGLength::read(gchar const *str)
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;
171 static bool svg_length_absolute_unit(SVGLength::Unit u)
173     return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
176 bool SVGLength::readAbsolute(gchar const *str)
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;
202 unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
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;
224 std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
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;
235     
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         }
248         
249         if (!next || !*next) {
250             break;
251         }
252     }
254     return list;
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)
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     }
271     
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;
405 unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
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;
421 void SVGLength::set(SVGLength::Unit u, float v, float c)
423     _set = true;
424     unit = u;
425     value = v;
426     computed = c;
429 void SVGLength::unset(SVGLength::Unit u, float v, float c)
431     _set = false;
432     unit = u;
433     value = v;
434     computed = c;
437 void SVGLength::update(double em, double ex, double scale)
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     }
448 double sp_svg_read_percentage(char const *str, double def)
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;
469 gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
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 "";
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)
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();
502 void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
504     if (!read(str)) {
505         unset(u, v, c);
506     }
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 :