Code

942f74d46af3b324c7fddb5a49a6b78a9a7388da
[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 <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 bufLen, 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, int bufLen, 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(static_cast<int>(fprec), static_cast<int>(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;
149 unsigned int sp_svg_number_write_de(gchar *buf, int bufLen, double val, unsigned int tprec, int min_exp)
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, bufLen, 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, bufLen, val, tprec, 0);
165         buf[p++] = 'e';
166         p += sp_svg_number_write_i(buf + p, bufLen - p, eval);
167         return p;
168     }
171 /* Length */
173 bool SVGLength::read(gchar const *str)
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;
194 static bool svg_length_absolute_unit(SVGLength::Unit u)
196     return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
199 bool SVGLength::readAbsolute(gchar const *str)
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;
225 unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
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;
247 std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
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;
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)
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;
436 unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
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;
452 void SVGLength::set(SVGLength::Unit u, float v, float c)
454     _set = true;
455     unit = u;
456     value = v;
457     computed = c;
460 void SVGLength::unset(SVGLength::Unit u, float v, float c)
462     _set = false;
463     unit = u;
464     value = v;
465     computed = c;
468 void SVGLength::update(double em, double ex, double scale)
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     }
479 double sp_svg_read_percentage(char const *str, double def)
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;
500 gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
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 "";
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)
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();
534 void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
536     if (!read(str)) {
537         unset(u, v, c);
538     }
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 :