Code

CodingStyle: const placement
[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, 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         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     }
144 /* Length */
146 bool SVGLength::read(gchar const *str)
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;
167 static bool svg_length_absolute_unit(SVGLength::Unit u)
169     return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
172 bool SVGLength::readAbsolute(gchar const *str)
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;
198 unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
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;
220 std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
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;
231     
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         }
244         
245         if (!next || !*next) {
246             break;
247         }
248     }
250     return list;
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)
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     }
267     
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;
401 unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
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;
417 void SVGLength::set(SVGLength::Unit u, float v, float c)
419     _set = true;
420     unit = u;
421     value = v;
422     computed = c;
425 void SVGLength::unset(SVGLength::Unit u, float v, float c)
427     _set = false;
428     unit = u;
429     value = v;
430     computed = c;
433 void SVGLength::update(double em, double ex, double scale)
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     }
444 double sp_svg_read_percentage(char const *str, double def)
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;
465 gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
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 "";
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)
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();
498 void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
500     if (!read(str)) {
501         unset(u, v, c);
502     }
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 :