Code

Make units pc (pica) and ft (feet) work correctly. Closes: LP #167943.
[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     }
46     
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     }
62     
63     *val = v;
64     return 1;
65 }
67 static unsigned int sp_svg_number_write_i(gchar *buf, int val)
68 {
69     int p = 0;
70     if (val < 0) {
71         buf[p++] = '-';
72         val = -val;
73     }
74     
75     int i = 0;
76     char c[32];
77     do {
78         c[32 - (++i)] = '0' + (val % 10);
79         val /= 10;
80     } while (val > 0);
81     
82     memcpy(buf + p, &c[32 - i], i);
83     p += i;
84     buf[p] = 0;
85     
86     return p;
87 }
89 static unsigned sp_svg_number_write_d(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
90 {
91     /* Process sign */
92     int i = 0;
93     if (val < 0.0) {
94         buf[i++] = '-';
95         val = fabs(val);
96     }
97     
98     /* Determine number of integral digits */
99     int idigits = 0;
100     if (val >= 1.0) {
101         idigits = (int) floor(log10(val));
102     }
103     
104     /* Determine the actual number of fractional digits */
105     fprec = MAX(fprec, tprec - idigits - 1);
106     /* Round value */
107     val += 0.5 * pow(10.0, - ((double) fprec));
108     /* Extract integral and fractional parts */
109     double dival = floor(val);
110     int ival = (int) dival;
111     double fval = val - dival;
112     /* Write integra */
113     i += sp_svg_number_write_i(buf + i, ival);
114     int end_i = i;
115     if (fprec > 0 && (padf || fval > 0.0)) {
116         buf[i++] = '.';
117         while ((fprec > 0) && (padf || (fval > 0.0))) {
118             fval *= 10.0;
119             dival = floor(fval);
120             fval -= dival;
121             int const int_dival = (int) dival;
122             buf[i++] = '0' + int_dival;
123             if (int_dival != 0) {
124                 end_i = i;
125             }
126             fprec -= 1;
127         }
128     }
129     buf[end_i] = 0;
130     return end_i;
133 unsigned int sp_svg_number_write_de(gchar *buf, double val, unsigned int tprec, int min_exp, unsigned int padf)
135     if (val == 0.0 || (fabs(val) >= 0.1 && fabs(val) < 10000000)) {
136         return sp_svg_number_write_d(buf, val, tprec, 0, padf);
137     } else {
138         double eval = floor(log10(fabs(val)));
139         if ((int) eval < min_exp) {
140             return sp_svg_number_write_d(buf, 0, tprec, 0, padf);
141         } else {
142             val = val / pow(10.0, eval);
143             int p = sp_svg_number_write_d(buf, val, tprec, 0, padf);
144             buf[p++] = 'e';
145             p += sp_svg_number_write_i(buf + p, (int) eval);
146             return p;
147         }
148     }
151 /* Length */
153 bool SVGLength::read(gchar const *str)
155     if (!str) {
156         return false;
157     }
159     SVGLength::Unit u;
160     float v;
161     float c;
162     if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
163         return false;
164     }
166     _set = true;
167     unit = u;
168     value = v;
169     computed = c;
171     return true;
174 static bool svg_length_absolute_unit(SVGLength::Unit u)
176     return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
179 bool SVGLength::readAbsolute(gchar const *str)
181     if (!str) {
182         return false;
183     }
185     SVGLength::Unit u;
186     float v;
187     float c;
188     if (!sp_svg_length_read_lff(str, &u, &v, &c, NULL)) {
189         return false;
190     }
192     if (svg_length_absolute_unit(u) == false) {
193         return false;
194     }
196     _set = true;
197     unit = u;
198     value = v;
199     computed = c;
201     return true;
205 unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
207     if (!str) {
208         return 0;
209     }
211     SVGLength::Unit unit;
212     float computed;
213     if (!sp_svg_length_read_lff(str, &unit, NULL, &computed, NULL)) {
214         // failed to read
215         return 0;
216     }
218     if (svg_length_absolute_unit(unit) == false) {
219         return 0;
220     }
222     *length = computed;
224     return 1;
227 std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
229     if (!str) {
230         return std::vector<SVGLength>();
231     }
233     SVGLength::Unit unit;
234     float value;
235     float computed;
236     char *next = (char *) str;
237     std::vector<SVGLength> list;
238     
239     while (sp_svg_length_read_lff(next, &unit, &value, &computed, &next)) {
241         SVGLength length;
242         length.set(unit, value, computed);
243         list.push_back(length);
245         while (next && *next &&
246                (*next == ',' || *next == ' ' || *next == '\n' || *next == '\r' || *next == '\t')) {
247             // the list can be comma- or space-separated, but we will be generous and accept
248             // a mix, including newlines and tabs
249             next++; 
250         }
251         
252         if (!next || !*next) {
253             break;
254         }
255     }
257     return list;
261 #define UVAL(a,b) (((unsigned int) (a) << 8) | (unsigned int) (b))
263 static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next)
265     if (!str) {
266         return 0;
267     }
269     gchar const *e;
270     float const v = g_ascii_strtod(str, (char **) &e);
271     if (e == str) {
272         return 0;
273     }
274     
275     if (!e[0]) {
276         /* Unitless */
277         if (unit) {
278             *unit = SVGLength::NONE;
279         }
280         if (val) {
281             *val = v;
282         }
283         if (computed) {
284             *computed = v;
285         }
286         if (next) {
287             *next = NULL; // no more values
288         }
289         return 1;
290     } else if (!g_ascii_isalnum(e[0])) {
291         /* Unitless or percent */
292         if (e[0] == '%') {
293             /* Percent */
294             if (e[1] && g_ascii_isalnum(e[1])) {
295                 return 0;
296             }
297             if (unit) {
298                 *unit = SVGLength::PERCENT;
299             }
300             if (val) {
301                 *val = v * 0.01;
302             }
303             if (computed) {
304                 *computed = v * 0.01;
305             }
306             if (next) {
307                 *next = (char *) e + 1;
308             }
309             return 1;
310         } else {
311             /* Unitless */
312             if (unit) {
313                 *unit = SVGLength::NONE;
314             }
315             if (val) {
316                 *val = v;
317             }
318             if (computed) {
319                 *computed = v;
320             }
321             if (next) {
322                 *next = (char *) e;
323             }
324             return 1;
325         }
326     } else if (e[1] && !g_ascii_isalnum(e[2])) {
327         /* TODO: Allow the number of px per inch to vary (document preferences, X server
328          * or whatever).  E.g. don't fill in computed here, do it at the same time as
329          * percentage units are done. */
330         unsigned int const uval = UVAL(e[0], e[1]);
331         switch (uval) {
332             case UVAL('p','x'):
333                 if (unit) {
334                     *unit = SVGLength::PX;
335                 }
336                 if (computed) {
337                     *computed = v;
338                 }
339                 break;
340             case UVAL('p','t'):
341                 if (unit) {
342                     *unit = SVGLength::PT;
343                 }
344                 if (computed) {
345                     *computed = v * PX_PER_PT;
346                 }
347                 break;
348             case UVAL('p','c'):
349                 if (unit) {
350                     *unit = SVGLength::PC;
351                 }
352                 if (computed) {
353                     *computed = v * PX_PER_PC;
354                 }
355                 break;
356             case UVAL('m','m'):
357                 if (unit) {
358                     *unit = SVGLength::MM;
359                 }
360                 if (computed) {
361                     *computed = v * PX_PER_MM;
362                 }
363                 break;
364             case UVAL('c','m'):
365                 if (unit) {
366                     *unit = SVGLength::CM;
367                 }
368                 if (computed) {
369                     *computed = v * PX_PER_CM;
370                 }
371                 break;
372             case UVAL('i','n'):
373                 if (unit) {
374                     *unit = SVGLength::INCH;
375                 }
376                 if (computed) {
377                     *computed = v * PX_PER_IN;
378                 }
379                 break;
380             case UVAL('f','t'):
381                 if (unit) {
382                     *unit = SVGLength::FOOT;
383                 }
384                 if (computed) {
385                     *computed = v * PX_PER_FT;
386                 }
387                 break;
388             case UVAL('e','m'):
389                 if (unit) {
390                     *unit = SVGLength::EM;
391                 }
392                 break;
393             case UVAL('e','x'):
394                 if (unit) {
395                     *unit = SVGLength::EX;
396                 }
397                 break;
398             default:
399                 /* Invalid */
400                 return 0;
401                 break;
402         }
403         if (val) {
404             *val = v;
405         }
406         if (next) {
407             *next = (char *) e + 2;
408         }
409         return 1;
410     }
412     /* Invalid */
413     return 0;
416 unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
418     float a;
419     float b;
420     unsigned int r = sp_svg_length_read_lff(str, unit, &a, &b, NULL);
421     if (r) {
422         if (value) {
423             *value = a;
424         }
425         if (computed) {
426             *computed = b;
427         }
428     }
429     return r;
432 void SVGLength::set(SVGLength::Unit u, float v, float c)
434     _set = true;
435     unit = u;
436     value = v;
437     computed = c;
440 void SVGLength::unset(SVGLength::Unit u, float v, float c)
442     _set = false;
443     unit = u;
444     value = v;
445     computed = c;
448 void SVGLength::update(double em, double ex, double scale)
450     if (unit == EM) {
451         computed = value * em;
452     } else if (unit == EX) {
453         computed = value * ex;
454     } else if (unit == PERCENT) {
455         computed = value * scale;
456     }
459 double sp_svg_read_percentage(char const *str, double def)
461     if (str == NULL) {
462         return def;
463     }
465     char *u;
466     double v = g_ascii_strtod(str, &u);
467     while (isspace(*u)) {
468         if (*u == '\0') {
469             return v;
470         }
471         u++;
472     }
473     if (*u == '%') {
474         v /= 100.0;
475     }
477     return v;
480 gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
482     switch (unit) {
483         case SVGLength::NONE: return "";
484         case SVGLength::PX: return "";
485         case SVGLength::PT: return "pt";
486         case SVGLength::PC: return "pc";
487         case SVGLength::MM: return "mm";
488         case SVGLength::CM: return "cm";
489         case SVGLength::INCH: return "in";
490         case SVGLength::EM: return "em";
491         case SVGLength::EX: return "ex";
492         case SVGLength::PERCENT: return "%";
493     }
494     return "";
497 /**
498  * N.B.\ This routine will sometimes return strings with `e' notation, so is unsuitable for CSS
499  * lengths (which don't allow scientific `e' notation).
500  */
501 std::string sp_svg_length_write_with_units(SVGLength const &length)
503     Inkscape::SVGOStringStream os;
504     if (length.unit == SVGLength::PERCENT) {
505         os << 100*length.value << sp_svg_length_get_css_units(length.unit);
506     } else {
507         os << length.value << sp_svg_length_get_css_units(length.unit);
508     }
509     return os.str();
513 void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
515     if (!read(str)) {
516         unset(u, v, c);
517     }
521 /*
522   Local Variables:
523   mode:c++
524   c-file-style:"stroustrup"
525   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
526   indent-tabs-mode:nil
527   fill-column:99
528   End:
529 */
530 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :