/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #include #include #include #if defined(DXD_NON_UNIX_DIR_SEPARATOR) #define DX_DIR_SEPARATOR ';' #else #define DX_DIR_SEPARATOR ':' #endif static Error verifyfont(Group font, char *); static Error getname(char *dirlist, char *file, char *result); /* changed. used to read old proprietary format; now uses import * to read a normal .dx format file. * * a font is a group of 256 fields, one for each character value, * with 2 attributes attached to the group: * "font ascent" which is a single float of max height above baseline * "font descent" which is a single positive float of max depth below baseline * (these are for all chars combined in this font) the combined height * should be 1.0 * * each field must have 2d or 3d positions and either line or triangle * connections, and have one attribute: * "char width" which is a float for the spacing of this char * eventually the points or connections can have colors and/or normals. */ Object DXGetFont(char *name, float *ascent, float *descent) { Group font = NULL; char *tbuf = NULL; char *dir = NULL; float fm; if (!name) return NULL; /* look in cache first */ font = (Group)DXGetCacheEntry(name, 0, 0); if (font) goto done; /* where to look */ dir = (char *) getenv("DXFONTS"); if (!dir) dir = (char *) getenv("DXEXECROOT"); if (!dir) dir = (char *) getenv("DXROOT"); if (!dir) dir = "/usr/lpp/dx"; #define XTRA 32 /* enough extra room for "../fonts/.. .dx" */ tbuf = (char *)DXAllocateLocalZero(strlen(dir) + strlen(name) + XTRA); if (!tbuf) goto error; /* try the places listed in the environment variable. * the filename to open is returned in tbuf. */ if (!getname(dir, name, tbuf)) goto error; /* try to open the file */ font = (Group)DXImportDX(tbuf, NULL, NULL, NULL, NULL); if (!font) { DXAddMessage("as font file"); goto error; } /* make sure the object structure is ok */ if (!verifyfont(font, name)) goto error; /* put in cache */ DXReference((Object)font); if (!DXSetCacheEntry((Object)font, 0.0, name, 0, 0)) goto error; done: /* return the info if the caller asks for it */ if (!DXGetFloatAttribute((Object)font, "font ascent", &fm)) { DXSetError(ERROR_BAD_PARAMETER, "#10810", name); goto error; } if (ascent) *ascent = fm; if (!DXGetFloatAttribute((Object)font, "font descent", &fm)) { DXSetError(ERROR_BAD_PARAMETER, "#10812", name); goto error; } if (descent) *descent = fm; DXFree((Pointer)tbuf); return (Object)font; error: DXDelete((Object)font); DXFree((Pointer)tbuf); return NULL; } /* find the font file */ static Error getname(char *dirlist, char *file, char *result) { int fd = -1; char *dirbuf = NULL; char *dp, *cp; /* if absolute pathname, don't fool with directories. */ #if !defined(DXD_NON_UNIX_DIR_SEPARATOR) if (file[0] == '/') { #else if (file[0] == '/' || file[0] == '\\' || file[1] == ':') { #endif strcpy(result, file); return OK; } /* allocate space to construct variations of the given filename. * get enough space the first time so we can construct any variation * without having to reallocate (xtra defined up above). */ dirbuf = (char *)DXAllocateLocalZero(strlen(dirlist) + strlen(file) + XTRA); if (!dirbuf) return ERROR; /* allow dirlist to be a colon separated list of directory names. * try dir/name, dir/name.dx, dir/fonts/name, dir/fonts/name.dx */ dp = dirlist; while (dp) { strcpy(dirbuf, dp); if ((cp = strchr(dirbuf, DX_DIR_SEPARATOR)) != NULL) *cp = '\0'; strcat(dirbuf, "/"); strcat(dirbuf, file); if ((fd = open(dirbuf, O_RDONLY)) >= 0) goto done; strcat(dirbuf, ".dx"); if ((fd = open(dirbuf, O_RDONLY)) >= 0) goto done; strcpy(dirbuf, dp); if ((cp = strchr(dirbuf, DX_DIR_SEPARATOR)) != NULL) *cp = '\0'; strcat(dirbuf, "/fonts/"); strcat(dirbuf, file); if ((fd = open(dirbuf, O_RDONLY)) >= 0) goto done; strcat(dirbuf, ".dx"); if ((fd = open(dirbuf, O_RDONLY)) >= 0) goto done; dp = strchr(dp, DX_DIR_SEPARATOR); if (dp) dp++; } error: /* only gets here on error */ DXSetError(ERROR_INVALID_DATA, "#10800", file); result[0] = '\0'; DXFree((Pointer)dirbuf); return ERROR; done: if (fd >= 0) close(fd); strcpy(result, dirbuf); DXFree((Pointer)dirbuf); return OK; } static Error verifyfont(Group font, char *name) { Object subo; Array a; float fm; char *dbuf = NULL; char *tbuf = NULL; char *dir; int items; /* do sanity checks now on things we will assume are ok later */ if (DXGetObjectClass((Object)font) != CLASS_GROUP) { DXSetError(ERROR_BAD_PARAMETER, "#10802", name); goto error; } if (!DXGetMemberCount(font, &items) || items != 256) { DXSetError(ERROR_BAD_PARAMETER, "#10802", name); goto error; } for (items = 0; subo = DXGetEnumeratedMember(font, items, NULL); items++) { if (DXGetObjectClass(subo) != CLASS_FIELD) { DXSetError(ERROR_BAD_PARAMETER, "#10804", name); goto error; } if (DXEmptyField((Field)subo)) continue; if (!DXGetFloatAttribute((Object)subo, "char width", &fm)) { DXSetError(ERROR_BAD_PARAMETER, "#10806", name); goto error; } } if (!DXGetFloatAttribute((Object)font, "font ascent", &fm)) { DXSetError(ERROR_BAD_PARAMETER, "#10810", name); goto error; } if (!DXGetFloatAttribute((Object)font, "font descent", &fm)) { DXSetError(ERROR_BAD_PARAMETER, "#10812", name); goto error; } return OK; error: return ERROR; } Object DXGeometricText(char *s, Object font, float *stringwidth) { unsigned char *p; double offset; Array a, points_array = NULL, conn_array = NULL; Field f, newf = NULL; float charwidth; Pointer conn, newconn, pos; float *newpos; Point *points, *newpoints; Line *lines, *newlines; Triangle *tris, *newtris; int pointdim, connsize; int npoints, nconnect, np, nc, i; int p1, q1, r1; char *conntype; static RGBColor white = {1,1,1}; /* output field: newpoints. do connections after you know whether * they are lines or triangles. */ newf = DXNewField(); if (!newf) goto error; points_array = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3); if (!points_array) goto error; if (!DXSetComponentValue(newf, "positions", (Object)points_array)) goto error; npoints = 0; offset = 0.0; /* for each character in the string... */ for (p=(unsigned char *)s; *p; p++) { f = (Field) DXGetEnumeratedMember((Group)font, *p, NULL); if (!f) continue; if (!DXGetFloatAttribute((Object)f, "char width", &charwidth)) charwidth = 0.0; /* get the points and lines/triangles associated with this character. * allow empty fields to have a width. */ a = (Array) DXGetComponentValue(f, "connections"); DXGetArrayInfo(a, &nc, NULL, NULL, NULL, NULL); if (!a || nc <= 0) { offset += charwidth; continue; } newconn = DXGetArrayData(a); /* make the new connections component only after you've seen the * first connections component so you can make the right type. */ if (!conn_array) { if (!DXGetStringAttribute((Object)a, "element type", &conntype) || (strcmp(conntype, "lines") && strcmp(conntype, "triangles"))) { DXSetError(ERROR_INVALID_DATA, "#10814", "object"); goto error; } connsize = strcmp(conntype, "lines") ? 3 : 2; conn_array = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, connsize); if (!conn_array) goto error; DXSetConnections(newf, connsize == 2 ? "lines" : "triangles", conn_array); nconnect = 0; } a = (Array) DXGetComponentValue(f, "positions"); if (!a) { offset += charwidth; continue; } newpos = (float *)DXGetArrayData(a); DXGetArrayInfo(a, NULL, NULL, NULL, NULL, &pointdim); if (pointdim != 2 && pointdim != 3) { DXSetError(ERROR_INVALID_DATA, "#10816", "object"); goto error; } np = nc * connsize; /* add new points, connections */ if (!DXAddArrayData(points_array, npoints, np, NULL)) goto error; newpoints = ((Point *) DXGetArrayData(points_array)) + npoints; if (!DXAddArrayData(conn_array, nconnect, nc, NULL)) goto error; conn = (Pointer)((int *)DXGetArrayData(conn_array) + nconnect * connsize); if (connsize == 2) { lines = (Line *)conn; newlines = (Line *)newconn; for (i=0; i