/* libwmf (api.c): library for wmf conversion Copyright (C) 2001 - Francis James Franklin The libwmf Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The libwmf Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the libwmf Library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "wmfconfig.h" #endif /* HAVE_CONFIG_H */ #include #include #include #include #include "wmfdefs.h" #include "api.h" /** * Creates and initializes an instance of the \b libwmf library (lite interface) for a specified device layer. * * @param API_return pointer to a wmfAPI* (the API handle use henceforth) * @param flags bitwise OR of WMF_OPT_ options * @param options pointer to wmfAPI_Options structure * * This is the first and necessary step when using \b libwmf. Options are passed via the wmfAPI_Options * structure and \p flags. wmf_api_create allocates the wmfAPI structure and initializes the color tables, * the metafile player, and the device layer. If successful then the pointer to the wmfAPI structure is * returned via \p API_return, otherwise all allocated memory is released and the library exits with an * appropriate error. * * wmf_lite_create () ignores command line arguments, if any are given, and does \b not attempt to set up * font mapping. * * The library should be closed using the corresponding wmf_lite_destroy () function. * * @return The error state of the library: \b wmf_E_None indicates successful creation and initialization * of the library, and \p *API_return will be non-zero. For any other error value \p *API_return * will be zero. */ wmf_error_t wmf_lite_create (wmfAPI** API_return,unsigned long flags,wmfAPI_Options* options) { wmfAPI* API = 0; wmfMemoryManager* MM = 0; wmfFunctionReference* FR = 0; wmf_error_t err = wmf_E_None; (*API_return) = 0; /* Initialize memory management array & allocate */ if (flags & WMF_OPT_ALLOC) { MM = (wmfMemoryManager*) options->malloc (options->context,sizeof (wmfMemoryManager)); } else { MM = (wmfMemoryManager*) malloc (sizeof (wmfMemoryManager)); } if (MM == 0) { if ((flags & WMF_OPT_NO_ERROR) == 0) { fputs ("wmf_api_create: insufficient memory!\n",stderr); } return (wmf_E_InsMem); } MM->count = 0; MM->max = 32; if (flags & WMF_OPT_ALLOC) { MM->list = (void**) options->malloc (options->context,MM->max * sizeof (void*)); } else { MM->list = (void**) malloc (MM->max * sizeof (void*)); } if (MM->list == 0) { if ((flags & WMF_OPT_NO_ERROR) == 0) { fputs ("wmf_api_create: insufficient memory!\n",stderr); } if (flags & WMF_OPT_ALLOC) { options->free (options->context,MM); } else { free (MM); } return (wmf_E_InsMem); } if (flags & WMF_OPT_ALLOC) { MM->context = options->context; MM->malloc = options->malloc; MM->realloc = options->realloc; MM->free = options->free; } else { MM->context = 0; MM->malloc = 0; MM->realloc = 0; MM->free = 0; } /* Allocate wmfAPI structure */ if (flags & WMF_OPT_ALLOC) { API = (wmfAPI*) options->malloc (options->context,sizeof (wmfAPI)); } else { API = (wmfAPI*) malloc (sizeof (wmfAPI)); } if (API == 0) { if ((flags & WMF_OPT_NO_ERROR) == 0) { fputs ("wmf_api_create: insufficient memory!\n",stderr); } if (flags & WMF_OPT_ALLOC) { options->free (options->context,MM->list); options->free (options->context,MM); } else { free (MM->list); free (MM); } return (wmf_E_InsMem); } API->memory_data = (void*) MM; /* Initialize debug, error & other streams */ if (flags & WMF_OPT_NO_DEBUG) API->debug_out = 0; else { if (flags & WMF_OPT_LOG_DEBUG) API->debug_out = options->debug_out; else { API->debug_out = stdout; } } if (flags & WMF_OPT_NO_ERROR) API->error_out = 0; else { if (flags & WMF_OPT_LOG_ERROR) API->error_out = options->error_out; else { API->error_out = stderr; } } API->write_data = 0; API->MetaHeader.pmh = &(API->PlaceableMetaHeader); API->MetaHeader.wmfheader = &(API->Head); API->File = &(API->MetaHeader); API->File->filein = 0; /* Input stream: file (unused) */ API->buffer_data = 0; /* Input stream */ API->bbuf.read = 0; API->bbuf.seek = 0; API->bbuf.tell = 0; /* Status function & context */ API->status.context = 0; API->status.function = 0; /* General purpose string buffer */ API->string_buffer.length = 0; API->string_buffer.buffer = 0; /* Zero some unset pointers */ API->function_reference = 0; API->font_data = 0; /* non-lite option TODO */ API->fonts = 0; API->color_data = 0; API->store.attrlist = 0; API->store.count = 0; API->store.max = 0; /* Library error state: */ API->err = wmf_E_None; /* Finally: flags, etc. */ API->flags = flags; /* ---- Henceforth all allocation to be done via api ---- */ /* General purpose string buffer */ API->string_buffer.length = 64; API->string_buffer.buffer = wmf_malloc (API,API->string_buffer.length * sizeof (char)); if (ERR (API)) { WMF_DEBUG (API,"bailing..."); err = wmf_lite_destroy (API); return (err); } /* Create color data - must be done prior to IPA initialization */ wmf_ipa_color_init (API); if (ERR (API)) { WMF_DEBUG (API,"bailing..."); err = wmf_lite_destroy (API); return (err); } /* Create ipa function interface */ API->function_reference = wmf_malloc (API,sizeof (wmfFunctionReference)); if (ERR (API)) { WMF_DEBUG (API,"bailing..."); err = wmf_lite_destroy (API); return (err); } FR = (wmfFunctionReference*) API->function_reference; FR->device_open = 0; FR->device_close = 0; FR->device_begin = 0; FR->device_end = 0; FR->flood_interior = 0; FR->flood_exterior = 0; FR->draw_pixel = 0; FR->draw_pie = 0; FR->draw_chord = 0; FR->draw_arc = 0; FR->draw_ellipse = 0; FR->draw_line = 0; FR->poly_line = 0; FR->draw_polygon = 0; FR->draw_polypolygon = 0; FR->draw_rectangle = 0; FR->rop_draw = 0; FR->bmp_draw = 0; FR->bmp_read = 0; FR->bmp_free = 0; FR->draw_text = 0; FR->udata_init = 0; FR->udata_copy = 0; FR->udata_set = 0; FR->udata_free = 0; FR->region_frame = 0; FR->region_paint = 0; FR->region_clip = 0; /* Create ipa-device data */ if (flags & WMF_OPT_FUNCTION) { options->function (API); } else if (flags & WMF_OPT_MODULE) /* TODO... TODO... TODO... */ { WMF_ERROR (API,"libwmf: module interface not implemented yet..."); WMF_ERROR (API," unable to initialize device layer!"); API->err = wmf_E_Glitch; } else { WMF_ERROR (API,"libwmf: unable to initialize device layer!"); API->err = wmf_E_Glitch; } if (ERR (API)) { WMF_DEBUG (API,"bailing..."); err = wmf_lite_destroy (API); return (err); } /* Create player data */ wmf_player_init (API); if (ERR (API)) { WMF_DEBUG (API,"bailing..."); err = wmf_lite_destroy (API); return (err); } /* Have successfully created the API... */ (*API_return) = API; return (wmf_E_None); } /** * Close the device layer, if open, and release all allocated memory attached to the memory manager. * * @param API the API handle * * @return The final error state of the library. */ wmf_error_t wmf_lite_destroy (wmfAPI* API) /* Basically free all alloced memory */ { wmf_error_t err; /* associated with the API */ wmfMemoryManager* MM = (wmfMemoryManager*) API->memory_data; wmfFunctionReference* FR = (wmfFunctionReference*) API->function_reference; if (FR) { /* FR->device_close must be the first action of wmf_lite_destroy in case * FR->device_close decides, for whatever reason, to re-play the meta file * or to acquire API resources... */ if ((API->flags & API_DEVICE_OPEN) && FR->device_close) FR->device_close (API); } if (API->flags & API_FILE_OPEN) wmf_file_close (API); err = API->err; while (MM->count) { MM->count--; if (MM->free) { MM->free (MM->context,MM->list[MM->count]); } else { free (MM->list[MM->count]); } } if (MM->free) { MM->free (MM->context,API); MM->free (MM->context,MM->list); MM->free (MM->context,MM); } else { free (API); free (MM->list); free (MM); } return (err); } /* ERROR & DEBUG Reporting * ======================= */ /** * Set the error state of the library to wmf_E_Assert. * * @param API the API handle * @param file file name * @param line line number * * This should only be called via the macro WMF_ASSERT(API,) which is defined (for debug builds only) * as: * @verbatim #define WMF_ASSERT(Z,M) if (!(M)) wmf_assert (Z,__FILE__,__LINE__) @endverbatim * i.e., if evaluates to 0 then call wmf_assert() with current file name and line number. */ void wmf_assert (wmfAPI* API,char* file,int line) { wmf_error (API,file,line,"Assertion failed!"); API->err = wmf_E_Assert; } /** * Print message to error stream. * * @param API the API handle * @param file file name * @param line line number * @param msg message to print * * This should only be called via the macro WMF_ERROR(API,msg) which calls wmf_error() with the current file * name and line number. */ void wmf_error (wmfAPI* API,char* file,int line,char* msg) { if (API->error_out == 0) return; fprintf (API->error_out,"ERROR: %s (%d): %s\n",file,line,msg); fflush (API->error_out); } /** * Print message to debug stream. * * @param API the API handle * @param file file name * @param line line number * @param msg message to print * * This should only be called via the macro WMF_DEBUG(API,msg) which (in debug builds only) calls * wmf_debug() with the current file name and line number. */ void wmf_debug (wmfAPI* API,char* file,int line,char* msg) { if (API->debug_out == 0) return; fprintf (API->debug_out,"%s (%d): %s\n",file,line,msg); fflush (API->debug_out); } /** * Print formatted message to debug stream. * * @param API the API handle * @param msg message to print * * With syntax similar to printf(), wmf_printf() prints formatted output to the debug stream. */ void wmf_printf (wmfAPI* API,char* msg,...) { va_list argp; va_start (argp,msg); if (API->debug_out) { vfprintf (API->debug_out,msg,argp); fflush (API->debug_out); } va_end (argp); } /* Memory management interface * =========================== */ /** * Allocate memory of specified size and attach to the API's memory manager's internal list. * * @param API the API handle * @param size size in bytes of memory required * * With syntax similar to malloc(), wmf_malloc() allocates \p size bytes of memory and adds a reference to * it in the memory manager's list. To free the memory, use wmf_free(). * * @return Pointer to new memory, or zero on failure. * Sets error state \b wmf_E_InsMem on failure. */ void* wmf_malloc (wmfAPI* API,size_t size) { wmfMemoryManager* MM = (wmfMemoryManager*) API->memory_data; void* mem = 0; void** more = 0; if (size == 0) { WMF_DEBUG (API,"wmf_[*]alloc: attempt to allocate zero-sized memory!"); return (0); } if (MM->count == MM->max) { if (MM->realloc) { more = (void**) MM->realloc (MM->context,MM->list,(MM->max+32) * sizeof (void*)); } else { more = (void**) realloc (MM->list,(MM->max+32) * sizeof (void*)); } if (more == 0) { WMF_ERROR (API,"wmf_[*]alloc: insufficient memory!"); API->err = wmf_E_InsMem; return (0); } MM->max += 32; MM->list = more; } if (MM->malloc) { mem = MM->malloc (MM->context,size); } else { mem = malloc (size); } if (mem == 0) { WMF_ERROR (API,"wmf_[*]alloc: insufficient memory!"); API->err = wmf_E_InsMem; return (0); } MM->list[MM->count] = mem; MM->count++; return (mem); } /** * Allocate memory of specified size and attach to the API's memory manager's internal list. * * @param API the API handle * @param number number or elements * @param size size in bytes of memory required by one element * * With syntax similar to calloc(), wmf_calloc() allocates \p number * \p size bytes of memory and adds a * reference to it in the memory manager's list. To free the memory, use wmf_free(). * * @return Pointer to new memory, or zero on failure. * Sets error state \b wmf_E_InsMem on failure. */ void* wmf_calloc (wmfAPI* API,size_t number,size_t size) { return (wmf_malloc (API,number * size)); } /** * (Re)Allocate memory of specified size and attach to the API's memory manager's internal list. * * @param API the API handle * @param mem pointer to memory previously allocated via the API * @param size new size in bytes of memory required * * With syntax similar to realloc(), wmf_realloc() allocates \p size bytes of memory and adds a reference * to it in the memory manager's list. To free the memory, use wmf_free(). If \p mem is zero, this is * equivalent to a call to wmf_malloc(). If \p size is zero, the memory is released via wmf_free(). * * @return Pointer to new memory, or zero on failure. * Sets error state \b wmf_E_InsMem on failure. */ void* wmf_realloc (wmfAPI* API,void* mem,size_t size) { wmfMemoryManager* MM = (wmfMemoryManager*) API->memory_data; void* more = 0; int i; if (mem == 0) return (wmf_malloc (API,size)); if (size == 0) { WMF_DEBUG (API,"wmf_realloc: attempt to allocate zero-sized memory!"); wmf_free (API,mem); return (0); } for (i = 0; i < MM->count; i++) if (MM->list[i] == mem) { if (MM->realloc) { more = MM->realloc (MM->context,mem,size); } else { more = realloc (mem,size); } if (more == 0) { WMF_ERROR (API,"wmf_[*]alloc: insufficient memory!"); API->err = wmf_E_InsMem; } else { MM->list[i] = more; } break; } return (more); } /** * Frees memory attached to the API's memory manager's internal list. * * @param API the API handle * @param mem pointer to memory previously allocated via the API * * Syntax is similar to free(). */ void wmf_free (wmfAPI* API,void* mem) { wmfMemoryManager* MM = (wmfMemoryManager*) API->memory_data; int i; for (i = 0; i < MM->count; i++) if (MM->list[i] == mem) { if (MM->free) { MM->free (MM->context,mem); } else { free (mem); } MM->count--; MM->list[i] = MM->list[MM->count]; break; } } /** * Detach memory attached to the API's memory manager's internal list. * * @param API the API handle * @param mem pointer to memory previously allocated via the API * * This removes the reference in the API's memory manager's internal list, and the memory will not, * therefore, be released by wmf_api_destroy(). To free subsequently, use free(). */ void wmf_detach (wmfAPI* API,void* mem) { wmfMemoryManager* MM = (wmfMemoryManager*) API->memory_data; int i; for (i = 0; i < MM->count; i++) if (MM->list[i] == mem) { MM->count--; MM->list[i] = MM->list[MM->count]; break; } } /** * Duplicate string and attach to the API's memory manager's internal list. * * @param API the API handle * @param str a string * * With syntax similar to strdup(), wmf_strdup() allocates the necessary memory via wmf_malloc() and copies * the string. Use wmf_free() to free the string. * * @return Pointer to new string, or zero on failure. * Sets error state \b wmf_E_InsMem on failure, or \b wmf_E_Glitch if str is zero. */ char* wmf_strdup (wmfAPI* API,const char* str) { char* cpy = 0; if (str == 0) { if (ERR (API)) return (0); WMF_ERROR (API,"wmf_strdup: attempt to copy non-existent string!"); API->err = wmf_E_Glitch; return (0); } cpy = (char*) wmf_malloc (API,strlen (str) + 1); if (ERR (API)) return (0); strcpy (cpy,str); return (cpy); } /** * Create concatenatation of two strings and attach to the API's memory manager's internal list. * * @param API the API handle * @param pre a string * @param post a string * * wmf_str_append() allocates the necessary memory via wmf_malloc(), copies \p pre into the string and * appends \p post. Use wmf_free() to free the string. * * @return Pointer to new string, or zero on failure. * Sets error state \b wmf_E_InsMem on failure, or \b wmf_E_Glitch if str is zero. */ char* wmf_str_append (wmfAPI* API,char* pre,char* post) { char* cpy = 0; if ((pre == 0) && (post == 0)) return (0); if (pre == 0) return (wmf_strdup (API,post)); if (post == 0) return (wmf_strdup (API,pre)); cpy = (char*) wmf_malloc (API,strlen (pre) + strlen (post) + 1); if (ERR (API)) return (0); strcpy (cpy,pre); strcat (cpy,post); return (cpy); } /** * Substring search. * * @param haystack a string * @param needle a substring to search for in haystack * * With syntax identical to strstr(), wmf_strstr() searches for string \p needle in string \p haystack. * * @return Pointer to substring \p needle found in \p haystack, or zero if not found. */ char* wmf_strstr (const char* haystack,const char* needle) { #ifdef HAVE_STRSTR return (strstr (haystack,needle)); #else /* HAVE_STRSTR */ const char* ptr1; const char* ptr2; const char* ptr3; ptr1 = haystack; while (*ptr1) { ptr2 = ptr1; ptr3 = needle; while ((*ptr3) == (*ptr2)) { if ((*ptr3) == 0) break; ptr2++; ptr3++; } if ((*ptr3) == 0) break; ptr1++; } return ((*ptr1) ? (char*) ptr1 : 0); #endif /* HAVE_STRSTR */ } /* Optional status call-back function * ================================== */ /** * Set a status call-back function. * * @param API the API handle * @param context handle for user data * @param function call-back function * * The metafile player calls the status function after each record. */ void wmf_status_function (wmfAPI* API,void* context,wmfStatus function) { API->status.context = context; API->status.function = function; } /** * Increase the size of the internal string buffer. * * @param API the API handle * * \b libwmf maintains an internal buffer for string operations. wmf_strbuf_grow() increases the size by 64. * * @return Returns the new size of the buffer. * Uses wmf_realloc(), so may set \b wmf_E_InsMem on failure. */ unsigned long wmf_strbuf_grow (wmfAPI* API) { char* more = 0; more = (char*) wmf_realloc (API,API->string_buffer.buffer,(API->string_buffer.length + 64) * sizeof (char)); if (ERR (API)) { WMF_DEBUG (API,"bailing..."); return (0); } API->string_buffer.length += 64; API->string_buffer.buffer = more; return (API->string_buffer.length); }