143f32c10Smrg/* 243f32c10SmrgCopyright (c) 2002-2003 by Juliusz Chroboczek 343f32c10Smrg 443f32c10SmrgPermission is hereby granted, free of charge, to any person obtaining a copy 543f32c10Smrgof this software and associated documentation files (the "Software"), to deal 643f32c10Smrgin the Software without restriction, including without limitation the rights 743f32c10Smrgto use, copy, modify, merge, publish, distribute, sublicense, and/or sell 843f32c10Smrgcopies of the Software, and to permit persons to whom the Software is 943f32c10Smrgfurnished to do so, subject to the following conditions: 1043f32c10Smrg 1143f32c10SmrgThe above copyright notice and this permission notice shall be included in 1243f32c10Smrgall copies or substantial portions of the Software. 1343f32c10Smrg 1443f32c10SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1543f32c10SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1643f32c10SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1743f32c10SmrgAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1843f32c10SmrgLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1943f32c10SmrgOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2043f32c10SmrgTHE SOFTWARE. 2143f32c10Smrg*/ 2243f32c10Smrg/* $XdotOrg: xc/programs/fonttosfnt/util.c,v 1.11 2003/12/19 02:16:36 dawes Exp $ */ 2343f32c10Smrg/* $XFree86: xc/programs/fonttosfnt/util.c,v 1.10 2003/12/19 02:05:39 dawes Exp $ */ 2443f32c10Smrg 2543f32c10Smrg#include <time.h> 2643f32c10Smrg#include <string.h> 2743f32c10Smrg#include <errno.h> 2843f32c10Smrg#include <stdlib.h> 29d3173433Smrg#include <math.h> 3043f32c10Smrg#include <stdarg.h> 3143f32c10Smrg 3243f32c10Smrg#include <ft2build.h> 3343f32c10Smrg#include FT_FREETYPE_H 3443f32c10Smrg#include FT_BDF_H 3543f32c10Smrg#include "X11/Xos.h" 36ea148d1dSmrg#include "X11/Xfuncproto.h" /* for _X_ATTRIBUTE_PRINTF */ 3743f32c10Smrg#include "fonttosfnt.h" 3843f32c10Smrg 3943f32c10Smrg#ifdef __GLIBC__ 4043f32c10Smrg#define HAVE_TIMEGM 4143f32c10Smrg#define HAVE_TM_GMTOFF 4243f32c10Smrg#endif 4343f32c10Smrg 4443f32c10Smrg#ifdef BSD 4543f32c10Smrg#define HAVE_TM_GMTOFF 4643f32c10Smrg#define GMTOFFMEMBER tm_gmtoff 4743f32c10Smrg#endif 4843f32c10Smrg 4943f32c10Smrg/* That's in POSIX */ 5043f32c10Smrg#define HAVE_TZSET 5143f32c10Smrg 5243f32c10Smrg#ifdef NEED_SETENV 5343f32c10Smrgextern int setenv(const char *name, const char *value, int overwrite); 5443f32c10Smrgextern void unsetenv(const char *name); 5543f32c10Smrg#endif 5643f32c10Smrg 5743f32c10Smrgchar* 586ef05171Smrgsprintf_alloc(const char *f, ...) 5943f32c10Smrg{ 6043f32c10Smrg char *s; 6143f32c10Smrg va_list args; 6243f32c10Smrg va_start(args, f); 6343f32c10Smrg s = vsprintf_alloc(f, args); 6443f32c10Smrg va_end(args); 6543f32c10Smrg return s; 6643f32c10Smrg} 6743f32c10Smrg 6843f32c10Smrg#if HAVE_VASPRINTF 69ea148d1dSmrg_X_ATTRIBUTE_PRINTF(1, 0) 7043f32c10Smrgchar* 716ef05171Smrgvsprintf_alloc(const char *f, va_list args) 7243f32c10Smrg{ 7343f32c10Smrg char *r; 7443f32c10Smrg int rc; 7543f32c10Smrg 7643f32c10Smrg rc = vasprintf(&r, f, args); 7743f32c10Smrg if(rc < 0) 7843f32c10Smrg return NULL; 7943f32c10Smrg return r; 8043f32c10Smrg} 8143f32c10Smrg#else 82ea148d1dSmrg_X_ATTRIBUTE_PRINTF(1, 0) 8343f32c10Smrgchar* 846ef05171Smrgvsprintf_alloc(const char *f, va_list args) 8543f32c10Smrg{ 8643f32c10Smrg int n, size = 12; 8743f32c10Smrg char *string; 88fbfaf8f3Smrg#if HAVE_DECL_VA_COPY 8943f32c10Smrg va_list args_copy; 90fbfaf8f3Smrg#endif 9143f32c10Smrg 9243f32c10Smrg while(1) { 9343f32c10Smrg if(size > 4096) 9443f32c10Smrg return NULL; 9543f32c10Smrg string = malloc(size); 9643f32c10Smrg if(!string) 9743f32c10Smrg return NULL; 9843f32c10Smrg 9943f32c10Smrg#if HAVE_DECL_VA_COPY 10043f32c10Smrg va_copy(args_copy, args); 10143f32c10Smrg n = vsnprintf(string, size, f, args_copy); 10243f32c10Smrg#else 10343f32c10Smrg n = vsnprintf(string, size, f, args); 10443f32c10Smrg#endif 10543f32c10Smrg if(n >= 0 && n < size) 10643f32c10Smrg return string; 10743f32c10Smrg else if(n >= size) 10843f32c10Smrg size = n + 1; 10943f32c10Smrg else 11043f32c10Smrg size = size * 3 / 2 + 1; 11143f32c10Smrg free(string); 11243f32c10Smrg } 11343f32c10Smrg /* NOTREACHED */ 11443f32c10Smrg} 11543f32c10Smrg#endif 11643f32c10Smrg 117ea148d1dSmrg/* Build a UTF-16 string from a Latin-1 string. 11843f32c10Smrg Result is not NUL-terminated. */ 11943f32c10Smrgchar * 1206ef05171SmrgmakeUTF16(const char *string) 12143f32c10Smrg{ 12243f32c10Smrg int n = strlen(string); 12343f32c10Smrg char *value = malloc(2 * n); 12443f32c10Smrg if(!value) 12543f32c10Smrg return NULL; 126fbfaf8f3Smrg for(int i = 0; i < n; i++) { 12743f32c10Smrg value[2 * i] = '\0'; 12843f32c10Smrg value[2 * i + 1] = string[i]; 12943f32c10Smrg } 13043f32c10Smrg return value; 13143f32c10Smrg} 13243f32c10Smrg 13343f32c10Smrg/* Like mktime(3), but UTC rather than local time */ 13443f32c10Smrg#if defined(HAVE_TIMEGM) 13543f32c10Smrgstatic time_t 13643f32c10Smrgmktime_gmt(struct tm *tm) 13743f32c10Smrg{ 13843f32c10Smrg return timegm(tm); 13943f32c10Smrg} 14043f32c10Smrg#elif defined(HAVE_TM_GMTOFF) 14143f32c10Smrgstatic time_t 14243f32c10Smrgmktime_gmt(struct tm *tm) 14343f32c10Smrg{ 14443f32c10Smrg time_t t; 14543f32c10Smrg struct tm *ltm; 14643f32c10Smrg 14743f32c10Smrg t = mktime(tm); 14843f32c10Smrg if(t < 0) 14943f32c10Smrg return -1; 15043f32c10Smrg ltm = localtime(&t); 15143f32c10Smrg if(ltm == NULL) 15243f32c10Smrg return -1; 15343f32c10Smrg return t + ltm->GMTOFFMEMBER; 15443f32c10Smrg} 15543f32c10Smrg#elif defined(HAVE_TZSET) 15643f32c10Smrg/* Taken from the Linux timegm(3) man page */ 15743f32c10Smrgstatic time_t 15843f32c10Smrgmktime_gmt(struct tm *tm) 15943f32c10Smrg{ 16043f32c10Smrg time_t t; 16143f32c10Smrg char *tz; 16243f32c10Smrg 16343f32c10Smrg tz = getenv("TZ"); 16443f32c10Smrg setenv("TZ", "", 1); 16543f32c10Smrg tzset(); 16643f32c10Smrg t = mktime(tm); 16743f32c10Smrg if(tz) 16843f32c10Smrg setenv("TZ", tz, 1); 16943f32c10Smrg else 17043f32c10Smrg unsetenv("TZ"); 17143f32c10Smrg tzset(); 17243f32c10Smrg return t; 17343f32c10Smrg} 17443f32c10Smrg#else 17543f32c10Smrg#error no mktime_gmt implementation on this platform 17643f32c10Smrg#endif 17743f32c10Smrg 17843f32c10Smrg/* Return the current time as a signed 64-bit number of seconds since 17943f32c10Smrg midnight, 1 January 1904. This is apparently when the Macintosh 18043f32c10Smrg was designed. */ 18143f32c10Smrgint 18243f32c10SmrgmacTime(int *hi, unsigned *lo) 18343f32c10Smrg{ 184d3173433Smrg unsigned long long diff; /* Not time_t */ 185d3173433Smrg char *source_date_epoch; 18643f32c10Smrg time_t macEpoch, current; 18743f32c10Smrg struct tm tm; 18843f32c10Smrg tm.tm_sec = 0; 18943f32c10Smrg tm.tm_min = 0; 19043f32c10Smrg tm.tm_hour = 0; 19143f32c10Smrg tm.tm_mday = 1; 19243f32c10Smrg tm.tm_mon = 1; 19343f32c10Smrg tm.tm_year = 4; 19443f32c10Smrg tm.tm_isdst = -1; 19543f32c10Smrg 19643f32c10Smrg macEpoch = mktime_gmt(&tm); 197c813b494Smrg if(macEpoch == -1) return -1; 19843f32c10Smrg 199d3173433Smrg /* This assumes that the SOURCE_DATE_EPOCH environment variable will contain 200d3173433Smrg a correct, positive integer in the time_t range */ 201d3173433Smrg if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")) == NULL || 202d3173433Smrg (current = (time_t)strtoll(source_date_epoch, NULL, 10)) <= 0) 203d3173433Smrg current = time(NULL); 204c813b494Smrg if(current == -1) 20543f32c10Smrg return -1; 20643f32c10Smrg 20743f32c10Smrg if(current < macEpoch) { 20843f32c10Smrg errno = EINVAL; 20943f32c10Smrg return -1; 21043f32c10Smrg } 21143f32c10Smrg 21243f32c10Smrg diff = current - macEpoch; 21343f32c10Smrg#if INT_MAX == LONG_MAX 21443f32c10Smrg *hi = 0; 21543f32c10Smrg#else 21643f32c10Smrg *hi = diff >> 32; 21743f32c10Smrg#endif 21843f32c10Smrg *lo = diff & 0xFFFFFFFF; 21943f32c10Smrg return 0; 22043f32c10Smrg} 22143f32c10Smrg 22243f32c10Smrgunsigned 22343f32c10SmrgfaceFoundry(FT_Face face) 22443f32c10Smrg{ 22543f32c10Smrg int rc; 22643f32c10Smrg BDF_PropertyRec prop; 22743f32c10Smrg 22843f32c10Smrg rc = FT_Get_BDF_Property(face, "FOUNDRY", &prop); 229ea148d1dSmrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) { 23043f32c10Smrg if(strcasecmp(prop.u.atom, "adobe") == 0) 23143f32c10Smrg return makeName("ADBE"); 23243f32c10Smrg else if(strcasecmp(prop.u.atom, "agfa") == 0) 23343f32c10Smrg return makeName("AGFA"); 23443f32c10Smrg else if(strcasecmp(prop.u.atom, "altsys") == 0) 23543f32c10Smrg return makeName("ALTS"); 23643f32c10Smrg else if(strcasecmp(prop.u.atom, "apple") == 0) 23743f32c10Smrg return makeName("APPL"); 23843f32c10Smrg else if(strcasecmp(prop.u.atom, "arphic") == 0) 23943f32c10Smrg return makeName("ARPH"); 24043f32c10Smrg else if(strcasecmp(prop.u.atom, "alltype") == 0) 24143f32c10Smrg return makeName("ATEC"); 24243f32c10Smrg else if(strcasecmp(prop.u.atom, "b&h") == 0) 24343f32c10Smrg return makeName("B&H "); 24443f32c10Smrg else if(strcasecmp(prop.u.atom, "bitstream") == 0) 24543f32c10Smrg return makeName("BITS"); 24643f32c10Smrg else if(strcasecmp(prop.u.atom, "dynalab") == 0) 24743f32c10Smrg return makeName("DYNA"); 24843f32c10Smrg else if(strcasecmp(prop.u.atom, "ibm") == 0) 24943f32c10Smrg return makeName("IBM "); 25043f32c10Smrg else if(strcasecmp(prop.u.atom, "itc") == 0) 25143f32c10Smrg return makeName("ITC "); 25243f32c10Smrg else if(strcasecmp(prop.u.atom, "interleaf") == 0) 25343f32c10Smrg return makeName("LEAF"); 25443f32c10Smrg else if(strcasecmp(prop.u.atom, "impress") == 0) 25543f32c10Smrg return makeName("IMPR"); 25643f32c10Smrg else if(strcasecmp(prop.u.atom, "larabiefonts") == 0) 25743f32c10Smrg return makeName("LARA"); 25843f32c10Smrg else if(strcasecmp(prop.u.atom, "linotype") == 0) 25943f32c10Smrg return makeName("LINO"); 26043f32c10Smrg else if(strcasecmp(prop.u.atom, "monotype") == 0) 26143f32c10Smrg return makeName("MT "); 26243f32c10Smrg else if(strcasecmp(prop.u.atom, "microsoft") == 0) 26343f32c10Smrg return makeName("MS "); 26443f32c10Smrg else if(strcasecmp(prop.u.atom, "urw") == 0) 26543f32c10Smrg return makeName("URW "); 26643f32c10Smrg else if(strcasecmp(prop.u.atom, "y&y") == 0) 26743f32c10Smrg return makeName("Y&Y "); 268c813b494Smrg else { 269c813b494Smrg char buf[5]; 270c813b494Smrg snprintf(buf, sizeof(buf), "%-4s", prop.u.atom); 271c813b494Smrg return makeName(buf); 272c813b494Smrg } 27343f32c10Smrg } 27443f32c10Smrg /* For now */ 27543f32c10Smrg return makeName("UNKN"); 27643f32c10Smrg} 277ea148d1dSmrg 27843f32c10Smrg 27943f32c10Smrgint 28043f32c10SmrgfaceWeight(FT_Face face) 28143f32c10Smrg{ 28243f32c10Smrg int rc; 28343f32c10Smrg BDF_PropertyRec prop; 28443f32c10Smrg rc = FT_Get_BDF_Property(face, "WEIGHT_NAME", &prop); 285ea148d1dSmrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) { 28643f32c10Smrg if(strcasecmp(prop.u.atom, "thin") == 0) 28743f32c10Smrg return 100; 28843f32c10Smrg else if(strcasecmp(prop.u.atom, "extralight") == 0) 28943f32c10Smrg return 200; 29043f32c10Smrg else if(strcasecmp(prop.u.atom, "light") == 0) 29143f32c10Smrg return 300; 29243f32c10Smrg else if(strcasecmp(prop.u.atom, "medium") == 0) 293c813b494Smrg return 400; 29443f32c10Smrg else if(strcasecmp(prop.u.atom, "semibold") == 0) 29543f32c10Smrg return 600; 29643f32c10Smrg else if(strcasecmp(prop.u.atom, "bold") == 0) 29743f32c10Smrg return 700; 29843f32c10Smrg else if(strcasecmp(prop.u.atom, "extrabold") == 0) 29943f32c10Smrg return 800; 30043f32c10Smrg else if(strcasecmp(prop.u.atom, "black") == 0) 30143f32c10Smrg return 900; 30243f32c10Smrg else 303c813b494Smrg return 400; 30443f32c10Smrg } else 305c813b494Smrg return 400; /* for now */ 30643f32c10Smrg} 30743f32c10Smrg 30843f32c10Smrgint 30943f32c10SmrgfaceWidth(FT_Face face) 31043f32c10Smrg{ 31143f32c10Smrg int rc; 31243f32c10Smrg BDF_PropertyRec prop; 31343f32c10Smrg rc = FT_Get_BDF_Property(face, "SETWIDTH_NAME", &prop); 314ea148d1dSmrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) { 31543f32c10Smrg if(strcasecmp(prop.u.atom, "ultracondensed") == 0) 31643f32c10Smrg return 1; 31743f32c10Smrg else if(strcasecmp(prop.u.atom, "extracondensed") == 0) 31843f32c10Smrg return 2; 31943f32c10Smrg else if(strcasecmp(prop.u.atom, "condensed") == 0) 32043f32c10Smrg return 3; 32143f32c10Smrg else if(strcasecmp(prop.u.atom, "semicondensed") == 0) 32243f32c10Smrg return 4; 32343f32c10Smrg else if(strcasecmp(prop.u.atom, "normal") == 0) 32443f32c10Smrg return 5; 32543f32c10Smrg else if(strcasecmp(prop.u.atom, "semiexpanded") == 0) 32643f32c10Smrg return 6; 32743f32c10Smrg else if(strcasecmp(prop.u.atom, "expanded") == 0) 32843f32c10Smrg return 7; 32943f32c10Smrg else if(strcasecmp(prop.u.atom, "extraexpanded") == 0) 33043f32c10Smrg return 8; 33143f32c10Smrg else if(strcasecmp(prop.u.atom, "ultraexpanded") == 0) 33243f32c10Smrg return 9; 33343f32c10Smrg else 33443f32c10Smrg return 5; 33543f32c10Smrg } else 33643f32c10Smrg return 5; /* for now */ 33743f32c10Smrg} 33843f32c10Smrg 33943f32c10Smrgint 34043f32c10SmrgfaceItalicAngle(FT_Face face) 34143f32c10Smrg{ 34243f32c10Smrg int rc; 34343f32c10Smrg BDF_PropertyRec prop; 34443f32c10Smrg 34543f32c10Smrg rc = FT_Get_BDF_Property(face, "ITALIC_ANGLE", &prop); 34643f32c10Smrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_INTEGER) { 34743f32c10Smrg return (prop.u.integer - 64 * 90) * (TWO_SIXTEENTH / 64); 34843f32c10Smrg } 34943f32c10Smrg 35043f32c10Smrg rc = FT_Get_BDF_Property(face, "SLANT", &prop); 351ea148d1dSmrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) { 35243f32c10Smrg if(strcasecmp(prop.u.atom, "i") == 0 || 35343f32c10Smrg strcasecmp(prop.u.atom, "s") == 0) 35443f32c10Smrg return -30 * TWO_SIXTEENTH; 35543f32c10Smrg else 35643f32c10Smrg return 0; 35743f32c10Smrg } else 35843f32c10Smrg return 0; /* for now */ 35943f32c10Smrg} 36043f32c10Smrg 36143f32c10Smrgint 36243f32c10SmrgfaceFlags(FT_Face face) 36343f32c10Smrg{ 36443f32c10Smrg int flags = 0; 36543f32c10Smrg BDF_PropertyRec prop; 36643f32c10Smrg int rc; 36743f32c10Smrg 36843f32c10Smrg if(faceWeight(face) >= 650) 36943f32c10Smrg flags |= FACE_BOLD; 37043f32c10Smrg rc = FT_Get_BDF_Property(face, "SLANT", &prop); 371ea148d1dSmrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) { 37243f32c10Smrg if(strcasecmp(prop.u.atom, "i") == 0 || 37343f32c10Smrg strcasecmp(prop.u.atom, "s") == 0) 37443f32c10Smrg flags |= FACE_ITALIC; 37543f32c10Smrg } 37643f32c10Smrg return flags; 37743f32c10Smrg} 37843f32c10Smrg 379c813b494Smrgint 380c813b494SmrgfaceIntProp(FT_Face face, const char *name) 381c813b494Smrg{ 382c813b494Smrg int rc; 383c813b494Smrg BDF_PropertyRec prop; 384c813b494Smrg 385c813b494Smrg rc = FT_Get_BDF_Property(face, name, &prop); 386c813b494Smrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_INTEGER) 387c813b494Smrg return prop.u.integer; 388c813b494Smrg else 389c813b494Smrg return UNDEF; 390c813b494Smrg} 391c813b494Smrg 392d2f28e1bSmrgchar * 393d2f28e1bSmrgfaceStringProp(FT_Face face, const char *name) 394d2f28e1bSmrg{ 395d2f28e1bSmrg int rc; 396d2f28e1bSmrg BDF_PropertyRec prop; 397d2f28e1bSmrg char *buf = NULL; 398d2f28e1bSmrg 399d2f28e1bSmrg rc = FT_Get_BDF_Property(face, name, &prop); 400d2f28e1bSmrg if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM) { 401d2f28e1bSmrg buf = sprintf_alloc("%s", prop.u.atom ? prop.u.atom : ""); 402d2f28e1bSmrg if(buf == NULL) { 403d2f28e1bSmrg perror("sprintf_alloc failed"); 404d2f28e1bSmrg exit(1); 405d2f28e1bSmrg } 406d2f28e1bSmrg } 407d2f28e1bSmrg return buf; 408d2f28e1bSmrg} 409d2f28e1bSmrg 41043f32c10Smrgchar * 41143f32c10SmrgfaceEncoding(FT_Face face) 41243f32c10Smrg{ 41343f32c10Smrg BDF_PropertyRec p1, p2; 41443f32c10Smrg int rc; 41543f32c10Smrg 41643f32c10Smrg rc = FT_Get_BDF_Property(face, "CHARSET_REGISTRY", &p1); 41743f32c10Smrg if(rc != 0 || p1.type != BDF_PROPERTY_TYPE_ATOM) 41843f32c10Smrg return NULL; 41943f32c10Smrg rc = FT_Get_BDF_Property(face, "CHARSET_ENCODING", &p2); 42043f32c10Smrg if(rc != 0 || p2.type != BDF_PROPERTY_TYPE_ATOM) 42143f32c10Smrg return NULL; 422ea148d1dSmrg if(!(p1.u.atom && p2.u.atom)) 423ea148d1dSmrg return NULL; 42443f32c10Smrg 42543f32c10Smrg return sprintf_alloc("%s-%s", p1.u.atom, p2.u.atom); 42643f32c10Smrg} 427ea148d1dSmrg 42843f32c10Smrgint 42943f32c10SmrgdegreesToFraction(int deg, int *num, int *den) 43043f32c10Smrg{ 43143f32c10Smrg double n, d; 43243f32c10Smrg double rad, val; 43343f32c10Smrg 43443f32c10Smrg if(deg <= -(60 * TWO_SIXTEENTH) || deg >= (60 * TWO_SIXTEENTH)) 43543f32c10Smrg goto fail; 43643f32c10Smrg 43743f32c10Smrg rad = (((double)deg) / TWO_SIXTEENTH) / 180.0 * M_PI; 43843f32c10Smrg 43943f32c10Smrg n = sin(-rad); 44043f32c10Smrg d = cos(rad); 44143f32c10Smrg 44243f32c10Smrg if(d < 0.001) 44343f32c10Smrg goto fail; 44443f32c10Smrg 44543f32c10Smrg val = atan2(n, d); 44643f32c10Smrg /* There must be a cleaner way */ 447fbfaf8f3Smrg for(int i = 1; i < 10000; i++) { 44843f32c10Smrg if((int)(d * i) != 0.0 && 44943f32c10Smrg fabs(atan2(ROUND(n * i), ROUND(d * i)) - val) < 0.05) { 45043f32c10Smrg *num = (int)ROUND(n * i); 45143f32c10Smrg *den = (int)ROUND(d * i); 45243f32c10Smrg return 0; 45343f32c10Smrg } 45443f32c10Smrg } 45543f32c10Smrg 45643f32c10Smrg fail: 45743f32c10Smrg *den = 1; 45843f32c10Smrg *num = 0; 45943f32c10Smrg return -1; 46043f32c10Smrg} 46143f32c10Smrg 462