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