1 /* AbiSource Program Utilities
2 * Copyright (C) 1998 AbiSource, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <math.h>
24 #include <ctype.h>
25 #include "ut_locale.h"
26
27 #include "ut_types.h"
28 #include "ut_misc.h"
29 #include "ut_assert.h"
30 #include "ut_string.h"
31 #include "ut_units.h"
32 #include "ut_debugmsg.h"
33
UT_dimensionName(UT_Dimension dim)34 const char * UT_dimensionName(UT_Dimension dim)
35 {
36 switch (dim)
37 {
38 case DIM_IN:
39 return "in";
40
41 case DIM_CM:
42 return "cm";
43
44 case DIM_MM:
45 return "mm";
46
47 case DIM_PI:
48 return "pi";
49
50 case DIM_PT:
51 return "pt";
52
53 case DIM_PX:
54 return "px";
55
56 case DIM_PERCENT:
57 return "%";
58
59 case DIM_STAR:
60 return "%";
61
62 case DIM_none:
63 return "";
64
65 default:
66 UT_ASSERT(UT_NOT_IMPLEMENTED);
67 return "in";
68 }
69 }
70
UT_determineDimension(const char * sz,UT_Dimension fallback)71 UT_Dimension UT_determineDimension(const char * sz, UT_Dimension fallback)
72 {
73 char * p = NULL ;
74
75 {
76 UT_LocaleTransactor t(LC_NUMERIC, "C");
77 strtod(sz, &p);
78 }
79
80 // p should now point to the unit
81 if (p && *p)
82 {
83 // trim off leading spaces
84 while (*p && isspace(*p))
85 p++;
86
87 if (g_ascii_strcasecmp(p,"in") == 0 || g_ascii_strcasecmp(p, "inch") == 0)
88 return DIM_IN;
89
90 else if (g_ascii_strcasecmp(p,"cm") == 0)
91 return DIM_CM;
92
93 else if (g_ascii_strcasecmp(p,"mm") == 0)
94 return DIM_MM;
95
96 else if (g_ascii_strcasecmp(p,"pi") == 0)
97 return DIM_PI;
98
99 else if (g_ascii_strcasecmp(p,"pt") == 0)
100 return DIM_PT;
101
102 else if (g_ascii_strcasecmp(p,"px") == 0)
103 return DIM_PX;
104
105 else if (g_ascii_strcasecmp(p,"%") == 0)
106 return DIM_PERCENT;
107
108 else if (g_ascii_strcasecmp(p,"*") == 0)
109 return DIM_STAR;
110
111 UT_DEBUGMSG(("ut_units - unknown unit presented '%s' \n",p));
112 UT_ASSERT_NOT_REACHED();
113 }
114
115 return fallback;
116 }
117
UT_convertInchesToDimension(double inches,UT_Dimension dim)118 double UT_convertInchesToDimension(double inches, UT_Dimension dim)
119 {
120 double valueScaled = inches;
121
122 switch (dim)
123 {
124 case DIM_IN: valueScaled = inches; break;
125 case DIM_CM: valueScaled = (inches * 2.54); break;
126 case DIM_MM: valueScaled = (inches * 25.4); break;
127 case DIM_PI: valueScaled = (inches * 6.0); break;
128 case DIM_PT: valueScaled = (inches * 72.0); break;
129 case DIM_PX: valueScaled = (inches * 72.0); break;
130 default:
131 UT_ASSERT(UT_NOT_IMPLEMENTED);
132 break;
133 }
134
135 return valueScaled;
136 }
137
UT_convertDimensions(double f,UT_Dimension from,UT_Dimension to)138 double UT_convertDimensions(double f, UT_Dimension from, UT_Dimension to)
139 {
140 double valueScaled = UT_convertDimToInches(f, from);
141 return UT_convertInchesToDimension(valueScaled, to);
142 }
143
UT_convertInchesToDimensionString(UT_Dimension dim,double valueInInches,const char * szPrecision)144 const char * UT_convertInchesToDimensionString(UT_Dimension dim, double valueInInches, const char * szPrecision)
145 {
146 // return pointer to static buffer -- use it quickly.
147 //
148 // We temporarily force the locale back to english so that
149 // we get a period as the decimal point.
150
151 // TODO what should the decimal precision of each different
152 // TODO unit of measurement be ??
153 static char buf[100];
154 char bufFormat[100];
155 double valueScaled;
156
157 switch (dim)
158 {
159 case DIM_IN:
160 // (1/16th (0.0625) is smallest unit the ui will
161 // let them enter (via the TopRuler), so let's
162 // set the precision so that we get nice roundoff.
163 // TODO we may need to improve this later.
164 valueScaled = valueInInches;
165 sprintf(bufFormat,"%%%sfin",((szPrecision && *szPrecision) ? szPrecision : ".4"));
166 break;
167
168 case DIM_CM:
169 valueScaled = (valueInInches * 2.54);
170 sprintf(bufFormat,"%%%sfcm",((szPrecision && *szPrecision) ? szPrecision : ".2"));
171 break;
172
173 case DIM_MM:
174 valueScaled = (valueInInches * 25.4);
175 sprintf(bufFormat,"%%%sfmm",((szPrecision && *szPrecision) ? szPrecision : ".1"));
176 break;
177
178 case DIM_PI:
179 valueScaled = (valueInInches * 6);
180 sprintf(bufFormat,"%%%sfpi",((szPrecision && *szPrecision) ? szPrecision : ".0"));
181 break;
182
183 case DIM_PT:
184 valueScaled = (valueInInches * 72);
185 sprintf(bufFormat,"%%%sfpt",((szPrecision && *szPrecision) ? szPrecision : ".0"));
186 break;
187
188 case DIM_PX:
189 valueScaled = (valueInInches * 72);
190 sprintf(bufFormat,"%%%sfpx",((szPrecision && *szPrecision) ? szPrecision : ".0"));
191 break;
192
193 case DIM_none:
194 valueScaled = valueInInches;
195 sprintf(bufFormat,"%%%sf",((szPrecision && *szPrecision) ? szPrecision : ""));
196 break;
197
198 case DIM_PERCENT:
199 valueScaled = valueInInches;
200 sprintf(bufFormat,"%%%sf%%",((szPrecision && *szPrecision) ? szPrecision : ""));
201 break;
202
203 default:
204 UT_ASSERT(UT_NOT_IMPLEMENTED);
205 valueScaled = valueInInches;
206 sprintf(bufFormat,"%%%sf",((szPrecision && *szPrecision) ? szPrecision : ""));
207 break;
208 }
209
210 {
211 UT_LocaleTransactor t(LC_NUMERIC, "C");
212 sprintf(buf,bufFormat,valueScaled);
213 }
214
215 return buf;
216 }
217
218 /*!
219 * FIXME ROB this should be in sync with UT_getDimensionPrecisicion
220 */
UT_formatDimensionString(UT_Dimension dim,double value,const char * szPrecision)221 const char * UT_formatDimensionString(UT_Dimension dim, double value, const char * szPrecision)
222 {
223 // return pointer to static buffer -- use it quickly.
224 //
225 // We temporarily force the locale back to english so that
226 // we get a period as the decimal point.
227
228 // TODO what should the decimal precision of each different
229 // TODO unit of measurement be ??
230
231 static char buf[100];
232 char bufFormat[100];
233
234 switch (dim)
235 {
236 case DIM_IN:
237 // (1/16th (0.0625) is smallest unit the ui will
238 // let them enter (via the TopRuler), so let's
239 // set the precision so that we get nice roundoff.
240 // TODO we may need to improve this later.
241 sprintf(bufFormat,"%%%sfin",((szPrecision && *szPrecision) ? szPrecision : ".4"));
242 break;
243
244 case DIM_CM:
245 sprintf(bufFormat,"%%%sfcm",((szPrecision && *szPrecision) ? szPrecision : ".2"));
246 break;
247
248 case DIM_MM:
249 sprintf(bufFormat,"%%%sfmm",((szPrecision && *szPrecision) ? szPrecision : ".1"));
250 break;
251
252 case DIM_PI:
253 sprintf(bufFormat,"%%%sfpi",((szPrecision && *szPrecision) ? szPrecision : ".0"));
254 break;
255
256 case DIM_PT:
257 sprintf(bufFormat,"%%%sfpt",((szPrecision && *szPrecision) ? szPrecision : ".0"));
258 break;
259
260 case DIM_PX:
261 sprintf(bufFormat,"%%%sfpx",((szPrecision && *szPrecision) ? szPrecision : ".0"));
262 break;
263
264 case DIM_none:
265 sprintf(bufFormat,"%%%sf",((szPrecision && *szPrecision) ? szPrecision : ""));
266 break;
267
268 case DIM_PERCENT:
269 sprintf(bufFormat,"%%%sf%%",((szPrecision && *szPrecision) ? szPrecision : ""));
270 break;
271
272 default:
273 UT_ASSERT(UT_NOT_IMPLEMENTED);
274 sprintf(bufFormat,"%%%sf",((szPrecision && *szPrecision) ? szPrecision : ""));
275 break;
276 }
277
278 {
279 UT_LocaleTransactor t(LC_NUMERIC, "C");
280 sprintf(buf,bufFormat,value);
281 }
282
283 return buf;
284 }
285
UT_reformatDimensionString(UT_Dimension dim,const char * sz,const char * szPrecision)286 const char * UT_reformatDimensionString(UT_Dimension dim, const char *sz, const char * szPrecision)
287 {
288 UT_ASSERT(sz); // this function segfaults if it gets a null
289 if (!sz) // if we really need to give it a null, we make it = 0in
290 {
291 sz = "0.0in";
292 UT_DEBUGMSG(("UT_reformatDimensionString just made the assumption null = 0.0in\n"));
293 }
294 double d = UT_convertDimensionless(sz);
295
296 // if needed, switch unit systems and round off
297 UT_Dimension dimOld = UT_determineDimension(sz, dim);
298
299 if (dimOld != dim)
300 {
301 double dInches = UT_convertToInches(sz);
302 d = UT_convertInchesToDimension(dInches, dim);
303 }
304
305 return UT_formatDimensionString(dim, d, szPrecision);
306 }
307
308 /*!
309 * This method increments a dimenstioned string by the amount given.
310 \param const char * dimString - string to be incremented.
311 \param amount of increment.
312 */
UT_incrementDimString(const char * dimString,double inc)313 const char * UT_incrementDimString(const char * dimString, double inc)
314 {
315 UT_Dimension dim = UT_determineDimension(dimString);
316 double val = UT_convertDimensionless(dimString);
317 val += inc;
318 const char * retv = UT_formatDimensionString(dim, val);
319 return retv;
320 }
321
322
323 /*!
324 * This method multiplys a dimenstioned string by the amount given.
325 \param const char * dimString - string to be incremented.
326 \param amount to be multiplied.
327 */
UT_multiplyDimString(const char * dimString,double mult)328 const char * UT_multiplyDimString(const char * dimString, double mult)
329 {
330 UT_Dimension dim = UT_determineDimension(dimString);
331 double val = UT_convertDimensionless(dimString);
332 val *= mult;
333 const char * retv = UT_formatDimensionString(dim, val);
334 return retv;
335 }
336
337
UT_convertToInches(const char * s)338 double UT_convertToInches(const char* s)
339 {
340 // NOTE: we explicitly use a period '.' as a decimal place
341 // NOTE: and assume that the locale is set to english.
342 // NOTE: all other places where we deal with these values
343 // NOTE: are wrapped with locale code.
344
345 double result = 0;
346
347 if (!s || !*s)
348 return 0;
349
350 double f = UT_convertDimensionless(s);
351
352 if (f == 0.)
353 return 0.;
354
355 UT_Dimension dim = UT_determineDimension(s, (UT_Dimension)-1);
356 result = UT_convertDimToInches (f, dim);
357
358 return result;
359 }
360
UT_convertDimToInches(double f,UT_Dimension dim)361 double UT_convertDimToInches (double f, UT_Dimension dim)
362 {
363 double result = 0.0;
364 switch(dim)
365 {
366 case DIM_IN: result = f; break;
367 case DIM_PI: result = f / 6; break;
368 case DIM_PT: result = f / 72; break;
369 case DIM_CM: result = f / 2.54; break;
370 case DIM_MM: result = f / 25.4; break;
371 case DIM_PX: result = f / 72; break;
372 default:
373 UT_DEBUGMSG(("Unknown dimension type: %d", dim));
374 UT_ASSERT_NOT_REACHED();
375 result = f;
376 }
377 return result;
378 }
379
UT_convertToPoints(const char * s)380 double UT_convertToPoints(const char* s)
381 {
382 if (!s || !*s)
383 return 0.;
384
385 double result = 0.;
386 double f = UT_convertDimensionless(s);
387 UT_Dimension dim = UT_determineDimension(s, (UT_Dimension)-1);
388
389 switch(dim)
390 {
391 case DIM_PT: result = f; break;
392 case DIM_PI: result = f * 12; break; // ie, 72 / 6
393 case DIM_IN: result = f * 72; break;
394 case DIM_CM: result = f * 72 / 2.54; break;
395 case DIM_MM: result = f * 72 / 25.4; break;
396 case DIM_PX: result = f ; break;
397 default:
398 UT_DEBUGMSG(("Unknown dimension type for: %s", s));
399 UT_ASSERT_NOT_REACHED();
400 if(f > 0.9)
401 {
402 result = f;
403 }
404 else
405 {
406 result = 12.; // default for a font-size
407 }
408 }
409
410 return result;
411 }
412
UT_convertToLogicalUnits(const char * s)413 UT_sint32 UT_convertToLogicalUnits(const char* s)
414 {
415 return static_cast<UT_sint32>(UT_convertToInches(s) * UT_LAYOUT_RESOLUTION);
416 }
417
UT_convertSizeToLayoutUnits(double Value,UT_Dimension dim)418 UT_sint32 UT_convertSizeToLayoutUnits(double Value, UT_Dimension dim)
419 {
420 return static_cast<UT_sint32>(UT_convertDimToInches(Value, dim) * UT_LAYOUT_RESOLUTION);
421 }
422
UT_convertDimensionless(const char * sz)423 double UT_convertDimensionless(const char * sz)
424 {
425 UT_return_val_if_fail(sz, 0);
426
427 // convert given string into a number -- without using any dimension
428 // info that may be in the string.
429 //
430 // normally we would just use atof(), but that is locale-aware and
431 // we want our file format to be locale-independent and thus portable.
432 // this means that anything we do internally (eg top ruler gadgets),
433 // needs to be in this convention.
434 //
435 // we can let the GUI do locale-specific conversions for presentation
436 // in dialogs and etc.
437
438 double f;
439 {
440 UT_LocaleTransactor t(LC_NUMERIC, "C");
441 f = atof(sz);
442 }
443
444 return f;
445 }
446
UT_convertToDimensionlessString(double value,const char * szPrecision)447 const char * UT_convertToDimensionlessString(double value, const char * szPrecision)
448 {
449 // return pointer to static buffer -- use it quickly.
450 //
451 // We temporarily force the locale back to english so that
452 // we get a period as the decimal point.
453
454 static char buf[100];
455
456 char bufFormat[100];
457 sprintf(bufFormat,"%%%sf",((szPrecision && *szPrecision) ? szPrecision : ""));
458
459 {
460 UT_LocaleTransactor t(LC_NUMERIC, "C");
461 sprintf(buf,bufFormat,value);
462 }
463
464 return buf;
465 }
466
UT_hasDimensionComponent(const char * sz)467 bool UT_hasDimensionComponent(const char * sz)
468 {
469 // TODO : check against known units instead of taking any
470 // TODO : ASCII chars after a number as a sign of units.
471
472 if (!sz)
473 return false;
474
475 char *p = NULL;
476
477 {
478 UT_LocaleTransactor t(LC_NUMERIC, "C");
479 strtod(sz, &p);
480 }
481
482 // if we landed on non-NULL, unit component
483 if(p && *p)
484 return true;
485 else
486 return false;
487 }
488
UT_paperUnits(const char * sz)489 UT_sint32 UT_paperUnits(const char * sz)
490 {
491 // convert string in form "8.5in" into "paper" units.
492 // paper units are a relatively low-resolution (say
493 // 1/100 inch) but are suitable for specifying margins,
494 // etc. -- stuff relative to the actual paper.
495
496 if (!sz || !*sz)
497 return 0;
498
499 double dInches = UT_convertToInches(sz);
500 double dResolution = UT_PAPER_UNITS_PER_INCH;
501
502 return static_cast<UT_sint32>(dInches * dResolution);
503 }
504
505 /*!
506 Converts paper units into inches.
507 \param1 quantity of paper units
508 \return corresponding quantity of inches
509
510 This function uses the UT_PAPER_UNITS_PER_INCH constant
511 to convert paper units into inches.
512
513 Paper units are a relatively low-resolution measurement (say
514 1/100 inch) but are suitable for specifying margins,
515 etc. -- stuff relative to the actual paper.
516
517 \see UT_paperUnits
518 \see UT_paperUnitsFromInches
519 */
520
UT_inchesFromPaperUnits(UT_sint32 iPaperUnits)521 double UT_inchesFromPaperUnits(UT_sint32 iPaperUnits)
522 {
523 double dResolution = UT_PAPER_UNITS_PER_INCH;
524
525 return (static_cast<double>(iPaperUnits) / dResolution);
526 }
527
528 /*!
529 Converts inches into paper units.
530 \param1 quantity of inches
531 \return corresponding quantity of paper units
532
533 This function uses the UT_PAPER_UNITS_PER_INCH constant
534 to convert paper units into inches. Unlike UT_paperUnits,
535 this function does not require a string as input.
536
537 Paper units are a relatively low-resolution measurement (say
538 1/100 inch) but are suitable for specifying margins,
539 etc. -- stuff relative to the actual paper.
540
541 \see UT_paperUnits
542 \see UT_paperUnitsFromInches
543 */
UT_paperUnitsFromInches(double dInches)544 UT_sint32 UT_paperUnitsFromInches(double dInches)
545 {
546 double dResolution = UT_PAPER_UNITS_PER_INCH;
547
548 return static_cast<UT_sint32>(dInches * dResolution);
549 }
550
UT_layoutUnitsFromPaperUnits(UT_sint32 iPaperUnits)551 UT_sint32 UT_layoutUnitsFromPaperUnits(UT_sint32 iPaperUnits)
552 {
553 // convert number in paper units (see above) into
554 // "layout" units.
555
556 return (UT_LAYOUT_RESOLUTION * iPaperUnits / UT_PAPER_UNITS_PER_INCH);
557 }
558
UT_formatDimensionedValue(double value,const char * szUnits,const char * szPrecision)559 const char * UT_formatDimensionedValue(double value,
560 const char * szUnits,
561 const char * szPrecision)
562 {
563 // format the given value into a static buffer with
564 // the optional format precision and using the given
565 // physical units.
566
567 static char buf[100];
568
569 const char * szValue = UT_convertToDimensionlessString(value,szPrecision);
570
571 sprintf(buf,"%s%s",szValue,szUnits);
572
573 return buf;
574 }
575
UT_convertToDimension(const char * s,UT_Dimension dim)576 double UT_convertToDimension(const char* s, UT_Dimension dim)
577 {
578 double d;
579
580 // if needed, switch unit systems and round off
581
582 if (UT_determineDimension(s, dim) != dim)
583 {
584 double dInches = UT_convertToInches(s);
585 d = UT_convertInchesToDimension(dInches, dim);
586 }
587 else
588 {
589 d = UT_convertDimensionless(s);
590 }
591
592 return d;
593 }
594
UT_isValidDimensionString(const char * sz,size_t max_length)595 bool UT_isValidDimensionString(const char * sz, size_t max_length)
596 {
597 // Check for validity of the input: the first part (before the dimension specifier)
598 // should only contain the characters '0'..'9' and/or at most one '.'. The last part possibly
599 // containing the dim specifier is not checked.
600 // This is not perfect, but good enough for most purposes. Feel g_free to improve the test.
601
602 // FIXME: other locales might want to use another character as a decimal seperator.
603 // we should be able to handle that too.
604 UT_LocaleTransactor t(LC_NUMERIC, "C");
605
606 if (max_length > 0 && (strlen(sz) > max_length))
607 return false;
608
609 const gchar* p = sz;
610 bool valid = true;
611 bool seenDecSep = false;
612 int valChars = 0;
613 while (*p && valid)
614 {
615 valid = isdigit(*p) || ((*p == '.') && seenDecSep == false);
616 if (*p == '.') seenDecSep = true;
617 if (valid) valChars++;
618 p++;
619 }
620 return valChars > 0;
621 }
622
623 /*!
624 * Returns the user visible precisition (number of decimal digits)
625 * for a certain dimension.
626 */
UT_getDimensionPrecisicion(UT_Dimension dim)627 UT_uint32 UT_getDimensionPrecisicion (UT_Dimension dim)
628 {
629 switch (dim)
630 {
631 case DIM_IN:
632 return 2;
633 case DIM_CM:
634 return 1;
635 case DIM_MM:
636 case DIM_PI:
637 case DIM_PT:
638 case DIM_PX:
639 return 0;
640 default:
641 UT_ASSERT(UT_NOT_IMPLEMENTED);
642 return 1;
643 }
644 }
645
646 /*!
647 * Returns the user visible resolution
648 * for a certain dimension.
649 */
UT_getDimensionResolution(UT_Dimension dim)650 double UT_getDimensionResolution (UT_Dimension dim)
651 {
652 switch (dim)
653 {
654 case DIM_IN:
655 return 0.1;
656 case DIM_CM:
657 return 0.5;
658 case DIM_PI:
659 return 1;
660 case DIM_MM:
661 return 5;
662 case DIM_PT:
663 return 10;
664 case DIM_PX:
665 return 10;
666 case DIM_PERCENT:
667 return 1;
668 default:
669 UT_ASSERT(UT_NOT_IMPLEMENTED);
670 return 1;
671 }
672 }
673
674 /*!
675 * Convert a percentage to a double precision fractional value
676 */
UT_convertFraction(const char * pszFrac)677 double UT_convertFraction(const char * pszFrac)
678 {
679 double res = 0.0;
680 UT_Dimension dim = UT_determineDimension(pszFrac);
681 if(dim == DIM_PERCENT)
682 {
683 res = UT_convertDimensionless(pszFrac)/100.;
684 }
685 else
686 {
687 res = UT_convertDimensionless(pszFrac);
688 }
689 return res;
690 }
691