Code

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