1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  symbolObj related functions.
6  * Author:   Steve Lime and the MapServer team.
7  *
8  ******************************************************************************
9  * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies of this Software or works derived from this Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  *****************************************************************************/
29 
30 #include <stdarg.h> /* variable number of function arguments support */
31 #include <time.h> /* since the parser handles time/date we need this */
32 
33 #include "mapserver.h"
34 #include "mapfile.h"
35 #include "mapcopy.h"
36 #include "mapthread.h"
37 #include "fontcache.h"
38 #include "mapows.h"
39 
40 
41 
42 extern int msyylex(void); /* lexer globals */
43 extern void msyyrestart(FILE *);
44 extern double msyynumber;
45 extern char *msyystring_buffer;
46 extern int msyylineno;
47 extern FILE *msyyin;
48 
49 extern int msyystate;
50 
freeImageCache(struct imageCacheObj * ic)51 void freeImageCache(struct imageCacheObj *ic)
52 {
53   if(ic) {
54     freeImageCache(ic->next); /* free any children */
55     msFreeRasterBuffer(&(ic->img));
56     free(ic);
57   }
58   return;
59 }
60 
61 /*
62 ** msSymbolGetDefaultSize()
63 **
64 ** Return the default size of a symbol.
65 ** Note: The size of a symbol is the height. Everywhere in the code, the width
66 ** is adjusted to the size that becomes the height.
67 ** See mapgd.c // size ~ height in pixels
68 */
msSymbolGetDefaultSize(symbolObj * s)69 double msSymbolGetDefaultSize(symbolObj *s)
70 {
71   double size;
72   if(s == NULL)
73     return 1;
74 
75   switch(s->type) {
76     case(MS_SYMBOL_TRUETYPE):
77       size = 1;
78       break;
79     case(MS_SYMBOL_PIXMAP):
80       assert(s->pixmap_buffer != NULL);
81       if(s->pixmap_buffer == NULL) return 1; /* FIXME */
82       size = (double)s->pixmap_buffer->height;
83       break;
84     case(MS_SYMBOL_SVG):
85       size = 1;
86 #if defined(USE_SVG_CAIRO) || defined (USE_RSVG)
87       assert(s->renderer_cache != NULL);
88       size = s->sizey;
89 #endif
90       break;
91     default: /* vector and ellipses, scalable */
92       size = (s->sizey<=0)?s->sizex:s->sizey;
93       break;
94   }
95 
96   if(size <= 0)
97     return 1;
98 
99   return size;
100 }
101 
initSymbol(symbolObj * s)102 void initSymbol(symbolObj *s)
103 {
104   MS_REFCNT_INIT(s);
105   s->type = MS_SYMBOL_VECTOR;
106   s->transparent = MS_FALSE;
107   s->transparentcolor = 0;
108   s->sizex = 1;
109   s->sizey = 1;
110   s->filled = MS_FALSE;
111   s->numpoints=0;
112   s->renderer=NULL;
113   s->renderer_free_func = NULL;
114   s->renderer_cache = NULL;
115   s->pixmap_buffer=NULL;
116   s->imagepath = NULL;
117   s->name = NULL;
118   s->inmapfile = MS_FALSE;
119   s->font = NULL;
120   s->full_pixmap_path = NULL;
121   s->character = NULL;
122   s->anchorpoint_x = s->anchorpoint_y = 0.5;
123 
124 }
125 
msFreeSymbol(symbolObj * s)126 int msFreeSymbol(symbolObj *s)
127 {
128   if(!s) return MS_FAILURE;
129   if( MS_REFCNT_DECR_IS_NOT_ZERO(s) ) {
130     return MS_FAILURE;
131   }
132 
133   if(s->name) free(s->name);
134   if(s->renderer_free_func) {
135     s->renderer_free_func(s);
136   } else {
137     if(s->renderer!=NULL) {
138       s->renderer->freeSymbol(s);
139     }
140   }
141   if(s->pixmap_buffer) {
142     msFreeRasterBuffer(s->pixmap_buffer);
143     free(s->pixmap_buffer);
144   }
145 
146 
147   if(s->font) free(s->font);
148   msFree(s->full_pixmap_path);
149   if(s->imagepath) free(s->imagepath);
150   if(s->character) free(s->character);
151 
152   return MS_SUCCESS;
153 }
154 
loadSymbol(symbolObj * s,char * symbolpath)155 int loadSymbol(symbolObj *s, char *symbolpath)
156 {
157   int done=MS_FALSE;
158   char szPath[MS_MAXPATHLEN];
159 
160   initSymbol(s);
161 
162   for(;;) {
163     switch(msyylex()) {
164       case(ANCHORPOINT):
165         if(getDouble(&(s->anchorpoint_x)) == -1) return MS_FAILURE;
166         if(getDouble(&(s->anchorpoint_y)) == -1) return MS_FAILURE;
167         if(s->anchorpoint_x<0 || s->anchorpoint_x>1 || s->anchorpoint_y<0 || s->anchorpoint_y>1) {
168           msSetError(MS_SYMERR, "ANCHORPOINT must be between 0 and 1", "loadSymbol()");
169           return(-1);
170         }
171         break;
172       case(ANTIALIAS): /*ignore*/
173         msyylex();
174         break;
175       case(CHARACTER):
176         if(getString(&s->character) == MS_FAILURE) return(-1);
177         break;
178       case(END): /* do some error checking */
179         if((s->type == MS_SYMBOL_SVG) && (s->imagepath == NULL)) {
180           msSetError(MS_SYMERR, "Symbol of type SVG has no file path specified.", "loadSymbol()");
181           return(-1);
182         }
183         if((s->type == MS_SYMBOL_PIXMAP) && (s->full_pixmap_path == NULL)) {
184           msSetError(MS_SYMERR, "Symbol of type PIXMAP has no image data.", "loadSymbol()");
185           return(-1);
186         }
187         if(((s->type == MS_SYMBOL_ELLIPSE) || (s->type == MS_SYMBOL_VECTOR)) && (s->numpoints == 0)) {
188           msSetError(MS_SYMERR, "Symbol of type VECTOR or ELLIPSE has no point data.", "loadSymbol()");
189           return(-1);
190         }
191         if(s->type == MS_SYMBOL_VECTOR) {
192           double minx = s->points[0].x;
193           double miny = s->points[0].y;
194           /* should only negative points be shifted? (#4116)*/
195           int shiftpositive = ((s->anchorpoint_x!=0.5)||(s->anchorpoint_y!=0.5));
196           int i;
197           for(i=1; i<s->numpoints; i++) {
198             if(s->points[i].x != -99 && s->points[i].y != -99) {
199               if(s->points[i].x<minx) minx = s->points[i].x;
200               if(s->points[i].y<miny) miny = s->points[i].y;
201             }
202           }
203           if(minx<0 || miny<0 || (shiftpositive && (minx!=0 || miny!=0))) {
204             for(i=0; i<s->numpoints; i++) {
205               if(s->points[i].x != -99 && s->points[i].y != -99) {
206                 s->points[i].x -= minx;
207                 s->points[i].y -= miny;
208               }
209             }
210             s->sizex -= minx;
211             s->sizey -= miny;
212           }
213         }
214 
215         return(0);
216         break;
217       case(EOF):
218         msSetError(MS_EOFERR, NULL, "loadSymbol()");
219         return(-1);
220         break;
221       case(FILLED):
222         if((s->filled = getSymbol(2,MS_TRUE,MS_FALSE)) == -1)
223           return(-1);
224         break;
225       case(FONT):
226         if(getString(&s->font) == MS_FAILURE) return(-1);
227         break;
228       case(IMAGE):
229         if(msyylex() != MS_STRING) { /* get image location from next token */
230           msSetError(MS_TYPEERR, "Parsing error near (%s):(line %d)", "loadSymbol()", msyystring_buffer, msyylineno);
231           return(-1);
232         }
233         s->full_pixmap_path = msStrdup(msBuildPath(szPath, symbolpath, msyystring_buffer));
234         /* Set imagepath */
235         s->imagepath = msStrdup(msyystring_buffer);
236         break;
237       case(NAME):
238         if(getString(&s->name) == MS_FAILURE) return(-1);
239         break;
240       case(POINTS):
241         done = MS_FALSE;
242         s->sizex = 0;
243         s->sizey = 0;
244         for(;;) {
245           switch(msyylex()) {
246             case(END):
247               done = MS_TRUE;
248               break;
249             case(MS_NUMBER):
250               s->points[s->numpoints].x = atof(msyystring_buffer); /* grab the x */
251               if(getDouble(&(s->points[s->numpoints].y)) == -1) return(-1); /* grab the y */
252               if(s->points[s->numpoints].x!=-99) {
253                 s->sizex = MS_MAX(s->sizex, s->points[s->numpoints].x);
254                 s->sizey = MS_MAX(s->sizey, s->points[s->numpoints].y);
255               }
256               s->numpoints++;
257               break;
258             default:
259               msSetError(MS_TYPEERR, "Parsing error near (%s):(line %d)", "loadSymbol()", msyystring_buffer, msyylineno);
260               return(-1);
261           }
262 
263           if(done == MS_TRUE)
264             break;
265         }
266         break;
267       case(TRANSPARENT):
268         s->transparent = MS_TRUE;
269         if(getInteger(&(s->transparentcolor)) == -1) return(-1);
270         break;
271       case(TYPE):
272         if((s->type = getSymbol(8,MS_SYMBOL_VECTOR,MS_SYMBOL_ELLIPSE,MS_SYMBOL_PIXMAP,MS_SYMBOL_SIMPLE,MS_TRUETYPE,MS_SYMBOL_HATCH,MS_SYMBOL_SVG)) == -1)
273           return(-1);
274         if(s->type == MS_TRUETYPE) /* TrueType keyword is valid several place in map files and symbol files, this simplifies the lexer */
275           s->type = MS_SYMBOL_TRUETYPE;
276         break;
277       default:
278         msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadSymbol()", msyystring_buffer, msyylineno);
279         return(-1);
280     } /* end switch */
281   } /* end for */
282   return done;
283 }
284 
writeSymbol(symbolObj * s,FILE * stream)285 void writeSymbol(symbolObj *s, FILE *stream)
286 {
287   int i;
288 
289   msIO_fprintf(stream, "  SYMBOL\n");
290   if(s->name != NULL) msIO_fprintf(stream, "    NAME \"%s\"\n", s->name);
291 
292   switch (s->type) {
293     case(MS_SYMBOL_HATCH):
294       msIO_fprintf(stream, "    TYPE HATCH\n");
295       break;
296     case(MS_SYMBOL_PIXMAP):
297       msIO_fprintf(stream, "    TYPE PIXMAP\n");
298       if(s->imagepath != NULL) msIO_fprintf(stream, "    IMAGE \"%s\"\n", s->imagepath);
299       if(s->anchorpoint_y!=0.5 || s->anchorpoint_x!=0.5) {
300         msIO_fprintf(stream, "    ANCHORPOINT %g %g\n", s->anchorpoint_x, s->anchorpoint_y);
301       }
302       msIO_fprintf(stream, "    TRANSPARENT %d\n", s->transparentcolor);
303       break;
304     case(MS_SYMBOL_TRUETYPE):
305       msIO_fprintf(stream, "    TYPE TRUETYPE\n");
306       if (s->character != NULL) msIO_fprintf(stream, "    CHARACTER \"%s\"\n", s->character);
307       if (s->font != NULL) msIO_fprintf(stream, "    FONT \"%s\"\n", s->font);
308       if(s->anchorpoint_y!=0.5 || s->anchorpoint_x!=0.5) {
309         msIO_fprintf(stream, "    ANCHORPOINT %g %g\n", s->anchorpoint_x, s->anchorpoint_y);
310       }
311       break;
312     default:
313       if(s->type == MS_SYMBOL_ELLIPSE)
314         msIO_fprintf(stream, "    TYPE ELLIPSE\n");
315       else if(s->type == MS_SYMBOL_VECTOR)
316         msIO_fprintf(stream, "    TYPE VECTOR\n");
317       else if(s->type == MS_SYMBOL_SVG)
318         msIO_fprintf(stream, "    TYPE SVG\n");
319       else
320         msIO_fprintf(stream, "    TYPE SIMPLE\n");
321 
322       if(s->filled == MS_TRUE) msIO_fprintf(stream, "    FILLED TRUE\n");
323       if(s->imagepath != NULL) msIO_fprintf(stream, "    IMAGE \"%s\"\n", s->imagepath);
324       if(s->anchorpoint_y!=0.5 || s->anchorpoint_x!=0.5) {
325         msIO_fprintf(stream, "    ANCHORPOINT %g %g\n", s->anchorpoint_x, s->anchorpoint_y);
326       }
327 
328       /* POINTS */
329       if(s->numpoints != 0) {
330         msIO_fprintf(stream, "    POINTS\n");
331         for(i=0; i<s->numpoints; i++) {
332           msIO_fprintf(stream, "      %g %g\n", s->points[i].x, s->points[i].y);
333         }
334         msIO_fprintf(stream, "    END\n");
335       }
336       break;
337   }
338 
339   msIO_fprintf(stream, "  END\n\n");
340 }
341 
342 
343 /*
344 ** Little helper function to allow us to build symbol files on-the-fly
345 ** from just a file name.
346 **
347 ** Returns the symbol index or -1 if it could not be added.
348 */
msAddImageSymbol(symbolSetObj * symbolset,char * filename)349 int msAddImageSymbol(symbolSetObj *symbolset, char *filename)
350 {
351   char szPath[MS_MAXPATHLEN];
352   symbolObj *symbol=NULL;
353   char *extension=NULL;
354   int symbolidx;
355 
356   if(!symbolset) {
357     msSetError(MS_SYMERR, "Symbol structure unallocated.", "msAddImageSymbol()");
358     return(-1);
359   }
360 
361   if(!filename || strlen(filename) == 0) return(-1);
362 
363   /* Allocate/init memory for new symbol if needed */
364   if (msGrowSymbolSet(symbolset) == NULL)
365     return -1;
366   symbolidx = symbolset->numsymbols;
367   symbolset->numsymbols++;
368   symbol = symbolset->symbol[symbolidx];
369 
370   /* check if svg checking extension otherwise assume it's a pixmap */
371   extension = strrchr(filename, '.');
372   if (extension == NULL)
373     extension = "";
374   if (strncasecmp(extension, ".svg", 4) == 0) {
375     symbol->type = MS_SYMBOL_SVG;
376   } else {
377     symbol->type = MS_SYMBOL_PIXMAP;
378   }
379 
380 #ifdef USE_CURL
381   if (strncasecmp(filename, "http", 4) == 0) {
382     char *tmpfullfilename = NULL;
383     char *tmpfilename = NULL;
384     char *tmppath = NULL;
385     int status = 0;
386     char szPath[MS_MAXPATHLEN];
387     int bCheckLocalCache = MS_TRUE;
388 
389     tmppath = msTmpPath(NULL, NULL, NULL);
390     if (tmppath) {
391       tmpfilename = msEncodeUrl(filename);
392       tmpfullfilename = msBuildPath(szPath, tmppath, tmpfilename);
393       if (tmpfullfilename) {
394         /*use the url for now as a caching mechanism*/
395         if (msHTTPGetFile(filename, tmpfullfilename, &status, -1, bCheckLocalCache, 0, 1024*1024 /* 1 MegaByte */) == MS_SUCCESS) {
396           symbol->imagepath = msStrdup(tmpfullfilename);
397           symbol->full_pixmap_path = msStrdup(tmpfullfilename);
398         } else {
399           unlink(tmpfullfilename);
400           msFree(tmpfilename);
401           msFree(tmppath);
402           return -1;
403         }
404       }
405       msFree(tmpfilename);
406       msFree(tmppath);
407     }
408   }
409 #endif
410   /*if the http did not work, allow it to be treated as a file*/
411   if (!symbol->full_pixmap_path) {
412     if(symbolset->map) {
413       symbol->full_pixmap_path = msStrdup(msBuildPath(szPath, symbolset->map->mappath, filename));
414     } else {
415       symbol->full_pixmap_path = msStrdup(msBuildPath(szPath, NULL, filename));
416     }
417     symbol->imagepath = msStrdup(filename);
418   }
419   symbol->name = msStrdup(filename);
420   return symbolidx;
421 }
422 
msFreeSymbolSet(symbolSetObj * symbolset)423 int msFreeSymbolSet(symbolSetObj *symbolset)
424 {
425   int i;
426 
427   freeImageCache(symbolset->imagecache);
428   for(i=0; i<symbolset->numsymbols; i++) {
429     if (symbolset->symbol[i]!=NULL) {
430       if ( msFreeSymbol((symbolset->symbol[i])) == MS_SUCCESS ) {
431         msFree(symbolset->symbol[i]);
432         symbolset->symbol[i]=NULL;
433       }
434     }
435   }
436   msFree(symbolset->symbol);
437 
438   /* no need to deal with fontset, it's a pointer */
439   return MS_SUCCESS;
440 }
441 
msInitSymbolSet(symbolSetObj * symbolset)442 void msInitSymbolSet(symbolSetObj *symbolset)
443 {
444   symbolset->filename = NULL;
445 
446   symbolset->imagecache = NULL;
447   symbolset->imagecachesize = 0; /* 0 symbols in the cache */
448 
449   symbolset->fontset = NULL;
450   symbolset->map = NULL;
451 
452   symbolset->numsymbols = 0;
453   symbolset->maxsymbols = 0;
454   symbolset->symbol = NULL;
455 
456   /* Alloc symbol[] array and ensure there is at least 1 symbol:
457    * symbol 0 which is the default symbol with all default params.
458    */
459   if (msGrowSymbolSet(symbolset) == NULL)
460     return; /* alloc failed */
461   symbolset->symbol[0]->type = MS_SYMBOL_ELLIPSE;
462   symbolset->symbol[0]->filled = MS_TRUE;
463   symbolset->symbol[0]->numpoints = 1;
464   symbolset->symbol[0]->points[0].x = 1;
465   symbolset->symbol[0]->points[0].y = 1;
466 
467   /* Just increment numsymbols to reserve symbol 0.
468    * initSymbol() has already been called
469    */
470   symbolset->numsymbols = 1;
471 
472 }
473 
474 
475 /*
476 ** Ensure there is at least one free entry in the symbol array of this
477 ** symbolSetObj. Grow the allocated symbol[] array if necessary and
478 ** allocate a new symbol for symbol[numsymbols] if there is not already one
479 ** and call initSymbol() on it.
480 **
481 ** This function is safe to use for the initial allocation of the symbol[]
482 ** array as well (i.e. when maxsymbols==0 and symbol==NULL)
483 **
484 ** Returns a reference to the new symbolObj on success, NULL on error.
485 */
msGrowSymbolSet(symbolSetObj * symbolset)486 symbolObj *msGrowSymbolSet( symbolSetObj *symbolset )
487 {
488   /* Do we need to increase the size of symbol[] by MS_SYMBOL_ALLOCSIZE? */
489   if (symbolset->numsymbols == symbolset->maxsymbols) {
490     int i;
491     if (symbolset->maxsymbols == 0) {
492       /* Initial allocation of array */
493       symbolset->maxsymbols += MS_SYMBOL_ALLOCSIZE;
494       symbolset->numsymbols = 0;
495       symbolset->symbol = (symbolObj**)malloc(symbolset->maxsymbols*sizeof(symbolObj*));
496     } else {
497       /* realloc existing array */
498       symbolset->maxsymbols += MS_SYMBOL_ALLOCSIZE;
499       symbolset->symbol = (symbolObj**)realloc(symbolset->symbol,
500                           symbolset->maxsymbols*sizeof(symbolObj*));
501     }
502 
503     if (symbolset->symbol == NULL) {
504       msSetError(MS_MEMERR, "Failed to allocate memory for symbol array.", "msGrowSymbolSet()");
505       return NULL;
506     }
507 
508     for(i=symbolset->numsymbols; i<symbolset->maxsymbols; i++)
509       symbolset->symbol[i] = NULL;
510   }
511 
512   if (symbolset->symbol[symbolset->numsymbols]==NULL) {
513     symbolset->symbol[symbolset->numsymbols]=(symbolObj*)malloc(sizeof(symbolObj));
514     if (symbolset->symbol[symbolset->numsymbols]==NULL) {
515       msSetError(MS_MEMERR, "Failed to allocate memory for a symbolObj", "msGrowSymbolSet()");
516       return NULL;
517     }
518   }
519 
520   /* Always call initSymbol() even if we didn't allocate a new symbolObj
521    * Since it's possible to dynamically remove/reuse symbols */
522   initSymbol(symbolset->symbol[symbolset->numsymbols]);
523 
524   return symbolset->symbol[symbolset->numsymbols];
525 }
526 
527 
528 
529 /* ---------------------------------------------------------------------------
530    msLoadSymbolSet and loadSymbolSet
531 
532    msLoadSymbolSet wraps calls to loadSymbolSet with mutex acquisition and
533    release.  It should be used everywhere outside the mapfile loading
534    phase of an application.  loadSymbolSet should only be used when a mutex
535    exists.  It does not check for existence of a mutex!
536 
537    See bug 339 for more details -- SG.
538    ------------------------------------------------------------------------ */
539 
msLoadSymbolSet(symbolSetObj * symbolset,mapObj * map)540 int msLoadSymbolSet(symbolSetObj *symbolset, mapObj *map)
541 {
542   int retval = MS_FAILURE;
543 
544   msAcquireLock( TLOCK_PARSER );
545   retval = loadSymbolSet( symbolset, map );
546   msReleaseLock( TLOCK_PARSER );
547 
548   return retval;
549 }
550 
loadSymbolSet(symbolSetObj * symbolset,mapObj * map)551 int loadSymbolSet(symbolSetObj *symbolset, mapObj *map)
552 {
553   int status=1;
554   char szPath[MS_MAXPATHLEN], *pszSymbolPath=NULL;
555 
556   int foundSymbolSetToken=MS_FALSE;
557   int symbolSetLevel=0;
558   int token;
559 
560   if(!symbolset) {
561     msSetError(MS_SYMERR, "Symbol structure unallocated.", "loadSymbolSet()");
562     return(-1);
563   }
564 
565   symbolset->map = (mapObj *)map;
566 
567   if(!symbolset->filename) return(0);
568 
569   /*
570   ** Open the file
571   */
572   if((msyyin = fopen(msBuildPath(szPath, symbolset->map->mappath, symbolset->filename), "r")) == NULL) {
573     msSetError(MS_IOERR, "(%s)", "loadSymbolSet()", symbolset->filename);
574     return(-1);
575   }
576 
577   pszSymbolPath = msGetPath(szPath);
578 
579   msyystate = MS_TOKENIZE_FILE; /* restore lexer state to INITIAL, and do return comments */
580   msyylex(); /* sets things up, but doesn't process any tokens */
581 
582   msyylineno = 0; /* reset line counter */
583   msyyrestart(msyyin); /* flush the scanner - there's a better way but this works for now */
584 
585   /*
586   ** Read the symbol file
587   */
588   for(;;) {
589     token = msyylex();
590 
591     if(!foundSymbolSetToken && token != SYMBOLSET) {
592       msSetError(MS_IDENTERR, "First token must be SYMBOLSET, this doesn't look like a symbol file.", "msLoadSymbolSet()");
593       return(-1);
594     }
595 
596     switch(token) {
597       case(END):
598         if (--symbolSetLevel < 0) {
599           msSetError(MS_IDENTERR, "END token found outside SYMBOLSET context. When nesting multiple SYMBOLSETs, make sure the SYMBOLSET/END pairs match.", "msLoadSymbolSet()");
600           status = -1;
601         }
602         break;
603       case(EOF):
604         status = 0;
605         break;
606       case(SYMBOL):
607         /* Allocate/init memory for new symbol if needed */
608         if (symbolSetLevel == 0) {
609           msSetError(MS_IDENTERR, "SYMBOL token found outside SYMBOLSET context. When nesting multiple SYMBOLSETs, make sure the SYMBOLSET/END pairs match.", "msLoadSymbolSet()");
610           status = -1;
611         } else if (msGrowSymbolSet(symbolset) == NULL) {
612           status = -1;
613         } else if((loadSymbol((symbolset->symbol[symbolset->numsymbols]), pszSymbolPath) == -1))
614           status = -1;
615         else
616           symbolset->numsymbols++;
617         break;
618       case(SYMBOLSET):
619         foundSymbolSetToken = MS_TRUE;
620         symbolSetLevel++;
621         break;
622       default:
623         msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadSymbolSet()", msyystring_buffer, msyylineno);
624         status = -1;
625     } /* end switch */
626 
627     if(status != 1) break;
628   } /* end for */
629 
630   fclose(msyyin);
631   msyyin = NULL;
632   free(pszSymbolPath);
633   return(status);
634 }
635 
msGetCharacterSize(mapObj * map,char * font,int size,char * character,rectObj * r)636 int msGetCharacterSize(mapObj *map, char* font, int size, char *character, rectObj *r) {
637   unsigned int unicode, codepoint;
638   glyph_element *glyph;
639   face_element *face = msGetFontFace(font, &map->fontset);
640   if(UNLIKELY(!face)) return MS_FAILURE;
641   msUTF8ToUniChar(character, &unicode);
642   codepoint = msGetGlyphIndex(face,unicode);
643   glyph = msGetGlyphByIndex(face,size,codepoint);
644   if(UNLIKELY(!glyph)) return MS_FAILURE;
645   if(glyph) {
646     r->minx = glyph->metrics.minx;
647     r->maxx = glyph->metrics.maxx;
648     r->miny = - glyph->metrics.maxy;
649     r->maxy = - glyph->metrics.miny;
650   }
651   return MS_SUCCESS;
652 }
653 
654 /*
655 ** Returns the size, in pixels, of a marker symbol defined by a specific style and scalefactor. Used for annotation
656 ** layer collision avoidance. A marker is made up of a number of styles so the calling code must either do the looping
657 ** itself or call this function for the bottom style which should be the largest.
658 */
msGetMarkerSize(mapObj * map,styleObj * style,double * width,double * height,double scalefactor)659 int msGetMarkerSize(mapObj *map, styleObj *style, double *width, double *height, double scalefactor)
660 {
661   int size;
662   symbolObj *symbol;
663   *width = *height = 0; /* set a starting value */
664 
665   if(style->symbol > map->symbolset.numsymbols || style->symbol < 0) return(MS_FAILURE); /* no such symbol, 0 is OK */
666 
667   if(style->symbol == 0) { /* single point */
668     *width = 1;
669     *height = 1;
670     return(MS_SUCCESS);
671   }
672 
673   symbol = map->symbolset.symbol[style->symbol];
674   if (symbol->type == MS_SYMBOL_PIXMAP && !symbol->pixmap_buffer) {
675     if (MS_SUCCESS != msPreloadImageSymbol(MS_MAP_RENDERER(map), symbol))
676       return MS_FAILURE;
677   }
678   if(symbol->type == MS_SYMBOL_SVG && !symbol->renderer_cache) {
679 #if defined(USE_SVG_CAIRO) || defined (USE_RSVG)
680     if(MS_SUCCESS != msPreloadSVGSymbol(symbol))
681       return MS_FAILURE;
682 #else
683     msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "msGetMarkerSize()");
684     return MS_FAILURE;
685 #endif
686   }
687   if(style->size == -1) {
688     size = ( msSymbolGetDefaultSize(symbol) * scalefactor );
689   } else
690     size = (style->size*scalefactor);
691   size = MS_MAX(size, style->minsize);
692   size = MS_MIN(size, style->maxsize);
693 
694   switch(symbol->type) {
695 
696     case(MS_SYMBOL_TRUETYPE): {
697       rectObj gbounds;
698       if(UNLIKELY(MS_FAILURE == msGetCharacterSize(map,symbol->font,size,symbol->character, &gbounds)))
699         return MS_FAILURE;
700 
701       *width = MS_MAX(*width, (gbounds.maxx-gbounds.minx));
702       *height = MS_MAX(*height, (gbounds.maxy-gbounds.miny));
703     }
704 
705       break;
706 
707     case(MS_SYMBOL_PIXMAP):
708       if(size == 1) {
709         *width = MS_MAX(*width, symbol->pixmap_buffer->width);
710         *height = MS_MAX(*height, symbol->pixmap_buffer->height);
711       } else {
712         *width = MS_MAX(*width, (((double)size/(double)symbol->pixmap_buffer->height) * symbol->pixmap_buffer->width));
713         *height = MS_MAX(*height, size);
714       }
715       break;
716     default: /* vector and ellipses, scalable */
717       if(style->size > 0) {
718         *width = MS_MAX(*width, ((size/symbol->sizey) * symbol->sizex));
719         *height = MS_MAX(*height, size);
720       } else { /* use symbol defaults */
721         *width = MS_MAX(*width, symbol->sizex);
722         *height = MS_MAX(*height, symbol->sizey);
723       }
724       break;
725   }
726 
727   return(MS_SUCCESS);
728 }
729 
730 /*
731  * Add a default new symbol. If the symbol name exists
732  * return the id of the symbol.
733  */
msAddNewSymbol(mapObj * map,char * name)734 int msAddNewSymbol(mapObj *map, char *name)
735 {
736   int i = 0;
737 
738   if (!map || !name)
739     return -1;
740 
741   i = msGetSymbolIndex(&map->symbolset, name, MS_TRUE);
742   if (i >= 0)
743     return i;
744 
745   /* Allocate memory for new symbol if needed */
746   if (msGrowSymbolSet(&(map->symbolset)) == NULL)
747     return -1;
748 
749   i = map->symbolset.numsymbols;
750   map->symbolset.symbol[i]->name = msStrdup(name);
751 
752   map->symbolset.numsymbols++;
753 
754   return i;
755 }
756 
757 /* msAppendSymbol and msRemoveSymbol are part of the work to resolve
758  * MapServer bug 579.
759  * http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=579 */
760 
761 
msAppendSymbol(symbolSetObj * symbolset,symbolObj * symbol)762 int msAppendSymbol(symbolSetObj *symbolset, symbolObj *symbol)
763 {
764   /* Allocate memory for new symbol if needed */
765   if (msGrowSymbolSet(symbolset) == NULL)
766     return -1;
767 
768   /* we need to free the symbolObj that was already allocated as we are
769    going to replace it with the provided symbolObj*. Not the most efficient
770    technique, but this function should be rarely called, and in any case only
771    by mapscript. Another option could be to use msCopySymbol(), in which case
772    the call to MS_REFCNT_INCR(symbol) should be removed.*/
773   if(symbolset->symbol[symbolset->numsymbols]) {
774     msFreeSymbol(symbolset->symbol[symbolset->numsymbols]);
775     msFree(symbolset->symbol[symbolset->numsymbols]);
776   }
777   symbolset->symbol[symbolset->numsymbols]=symbol;
778   MS_REFCNT_INCR(symbol);
779   return symbolset->numsymbols++;
780 }
781 
782 
msRemoveSymbol(symbolSetObj * symbolset,int nSymbolIndex)783 symbolObj *msRemoveSymbol(symbolSetObj *symbolset, int nSymbolIndex)
784 {
785   int i;
786   symbolObj *symbol;
787   if (symbolset->numsymbols == 1) {
788     msSetError(MS_CHILDERR, "Cannot remove a symbolset's sole symbol", "removeSymbol()");
789     return NULL;
790   } else if (nSymbolIndex < 0 || nSymbolIndex >= symbolset->numsymbols) {
791     msSetError(MS_CHILDERR, "Cannot remove symbol, invalid nSymbolIndex %d", "removeSymbol()", nSymbolIndex);
792     return NULL;
793   } else {
794     symbol=symbolset->symbol[nSymbolIndex];
795     for (i=nSymbolIndex+1; i<symbolset->numsymbols; i++) {
796       symbolset->symbol[i-1] = symbolset->symbol[i];
797     }
798     symbolset->symbol[i-1]=NULL;
799     symbolset->numsymbols--;
800     MS_REFCNT_DECR(symbol);
801     /* update symbol references in the map */
802     if (symbolset->map) {
803         int l,c,s,lb;
804         layerObj *layer;
805         classObj *cl;
806         styleObj *style;
807         labelObj *label;
808         for (l = 0; l < symbolset->map->numlayers; l++) {
809             layer = GET_LAYER(symbolset->map, l);
810             for (c = 0; c < layer->numclasses; c++) {
811                 cl = layer->class[c];
812                 for (s = 0; s < cl->numstyles; s++) {
813                     style = cl->styles[s];
814                     if (style->symbol >= nSymbolIndex)
815                         --style->symbol;
816                 }
817                 for (lb = 0; lb < cl->numlabels; lb++) {
818                     label = cl->labels[lb];
819                     for (s = 0; s < label->numstyles; s++) {
820                         style = label->styles[s];
821                         if (style->symbol >= nSymbolIndex)
822                             --style->symbol;
823                     }
824                 }
825             }
826         }
827         /* Update symbol references in labelcache */
828         for(c = 0; c < MS_MAX_LABEL_PRIORITY; c++) {
829             labelCacheSlotObj *cacheslot = &(symbolset->map->labelcache.slots[c]);
830             for(l = 0; l < cacheslot->numlabels; l++) {
831                 labelCacheMemberObj *cachePtr = &(cacheslot->labels[l]);
832                 for(lb = 0; lb < cachePtr->numtextsymbols; lb++) {
833                     label = cachePtr->textsymbols[lb]->label;
834                     for (s = 0; s < label->numstyles; s++) {
835                         style = label->styles[s];
836                         if (style->symbol >= nSymbolIndex)
837                             --style->symbol;
838                     }
839                 }
840             }
841         }
842     }
843     return symbol;
844   }
845 }
846 
msSaveSymbolSetStream(symbolSetObj * symbolset,FILE * stream)847 int msSaveSymbolSetStream(symbolSetObj *symbolset, FILE *stream)
848 {
849   int i;
850   if (!symbolset || !stream) {
851     msSetError(MS_SYMERR, "Cannot save symbolset.", "msSaveSymbolSetStream()");
852     return MS_FAILURE;
853   }
854   /* Don't ever write out the default symbol at index 0 */
855   for (i=1; i<symbolset->numsymbols; i++) {
856     if(!symbolset->symbol[i]->inmapfile) writeSymbol((symbolset->symbol[i]), stream);
857   }
858   return MS_SUCCESS;
859 }
860 
msSaveSymbolSet(symbolSetObj * symbolset,const char * filename)861 int msSaveSymbolSet(symbolSetObj *symbolset, const char *filename)
862 {
863   FILE *stream;
864   int retval;
865   if (!filename || strlen(filename) == 0) {
866     msSetError(MS_SYMERR, "Invalid filename.", "msSaveSymbolSet()");
867     return MS_FAILURE;
868   }
869   stream = fopen(filename, "w");
870   if (stream) {
871     fprintf(stream, "SYMBOLSET\n");
872     retval = msSaveSymbolSetStream(symbolset, stream);
873     fprintf(stream, "END\n");
874     fclose(stream);
875   } else {
876     msSetError(MS_SYMERR, "Could not write to %s", "msSaveSymbolSet()",
877                filename);
878     retval = MS_FAILURE;
879   }
880   return retval;
881 }
882 
msLoadImageSymbol(symbolObj * symbol,const char * filename)883 int msLoadImageSymbol(symbolObj *symbol, const char *filename)
884 {
885   msFree(symbol->full_pixmap_path);
886   symbol->full_pixmap_path = msStrdup(filename);
887   return MS_SUCCESS;
888 }
889 
msPreloadImageSymbol(rendererVTableObj * renderer,symbolObj * symbol)890 int msPreloadImageSymbol(rendererVTableObj *renderer, symbolObj *symbol)
891 {
892   if(symbol->pixmap_buffer && symbol->renderer == renderer)
893     return MS_SUCCESS;
894   if(symbol->pixmap_buffer) { /* other renderer was used, start again */
895     msFreeRasterBuffer(symbol->pixmap_buffer);
896   } else {
897     symbol->pixmap_buffer = (rasterBufferObj*)calloc(1,sizeof(rasterBufferObj));
898   }
899   if(MS_SUCCESS != renderer->loadImageFromFile(symbol->full_pixmap_path, symbol->pixmap_buffer)) {
900     /* Free pixmap_buffer already allocated */
901     free(symbol->pixmap_buffer);
902     symbol->pixmap_buffer = NULL;
903     return MS_FAILURE;
904   }
905   symbol->renderer = renderer;
906   symbol->sizex = symbol->pixmap_buffer->width;
907   symbol->sizey = symbol->pixmap_buffer->height;
908   return MS_SUCCESS;
909 
910 }
911 
912 /***********************************************************************
913  * msCopySymbol()                                                      *
914  *                                                                     *
915  * Copy a symbolObj, using mapfile.c:initSymbol(), msCopyPoint()       *
916  * gdImageCreate(), gdImageCopy()                                      *
917  **********************************************************************/
918 
msCopySymbol(symbolObj * dst,symbolObj * src,mapObj * map)919 int msCopySymbol(symbolObj *dst, symbolObj *src, mapObj *map)
920 {
921   int i;
922 
923   initSymbol(dst);
924 
925   MS_COPYSTRING(dst->name, src->name);
926   MS_COPYSTELEM(type);
927   MS_COPYSTELEM(inmapfile);
928 
929   /* map is a special case */
930   dst->map = map;
931 
932   MS_COPYSTELEM(sizex);
933   MS_COPYSTELEM(sizey);
934   MS_COPYSTELEM(anchorpoint_x);
935   MS_COPYSTELEM(anchorpoint_y);
936 
937   for (i=0; i < src->numpoints; i++) {
938     MS_COPYPOINT(&(dst->points[i]), &(src->points[i]));
939   }
940 
941   MS_COPYSTELEM(numpoints);
942   MS_COPYSTELEM(filled);
943 
944   MS_COPYSTRING(dst->imagepath, src->imagepath);
945   MS_COPYSTELEM(transparent);
946   MS_COPYSTELEM(transparentcolor);
947   MS_COPYSTRING(dst->character, src->character);
948   MS_COPYSTRING(dst->font, src->font);
949   MS_COPYSTRING(dst->full_pixmap_path,src->full_pixmap_path);
950 
951   return(MS_SUCCESS);
952 }
953 
954 /***********************************************************************
955  * msCopySymbolSet()                                                   *
956  *                                                                     *
957  * Copy a symbolSetObj using msCopyFontSet(), msCopySymbol()           *
958  **********************************************************************/
959 
msCopySymbolSet(symbolSetObj * dst,symbolSetObj * src,mapObj * map)960 int msCopySymbolSet(symbolSetObj *dst, symbolSetObj *src, mapObj *map)
961 {
962   int i, return_value;
963 
964   MS_COPYSTRING(dst->filename, src->filename);
965 
966   dst->map = map;
967   dst->fontset = &(map->fontset);
968 
969   /* Copy child symbols */
970   for (i = 0; i < src->numsymbols; i++) {
971     if (msGrowSymbolSet(dst) == NULL)
972       return MS_FAILURE;
973     return_value = msCopySymbol(dst->symbol[i], src->symbol[i], map);
974     if (return_value != MS_SUCCESS) {
975       msSetError(MS_MEMERR,"Failed to copy symbol.","msCopySymbolSet()");
976       return(MS_FAILURE);
977     }
978     dst->numsymbols++;
979   }
980 
981   /* MS_COPYSTELEM(imagecachesize); */
982 
983   /* I have a feeling that the code below is not quite right - Sean */
984   /*copyProperty(&(dst->imagecache), &(src->imagecache),
985                sizeof(struct imageCacheObj));
986    */
987 
988   dst->imagecachesize = 0; /* since we are not copying the cache set the cache  to NUNLL and the size to 0 (bug 1521) */
989   dst->imagecache = NULL;
990 
991   return(MS_SUCCESS);
992 }
993 
get_bbox(pointObj * poiList,int numpoints,double * minx,double * miny,double * maxx,double * maxy)994 static void get_bbox(pointObj *poiList, int numpoints, double *minx, double *miny, double *maxx, double *maxy)
995 {
996   int j;
997 
998   *minx = *maxx = poiList[0].x;
999   *miny = *maxy = poiList[0].y;
1000   for(j=1; j<numpoints; j++) {
1001     if ((poiList[j].x==-99.0) || (poiList[j].y==-99.0)) continue;
1002     *minx = MS_MIN(*minx, poiList[j].x);
1003     *maxx = MS_MAX(*maxx, poiList[j].x);
1004     *miny = MS_MIN(*miny, poiList[j].y);
1005     *maxy = MS_MAX(*maxy, poiList[j].y);
1006   }
1007 
1008   return;
1009 }
1010 
1011 /*
1012  ** msRotateSymbol - Clockwise rotation of a symbol definition. Contributed
1013  ** by MapMedia, with clean up by SDL. Currently only type VECTOR and PIXMAP
1014  ** symbols are handled.
1015  */
msRotateVectorSymbol(symbolObj * symbol,double angle)1016 symbolObj *msRotateVectorSymbol(symbolObj *symbol, double angle)
1017 {
1018   double angle_rad=0.0;
1019   double cos_a, sin_a;
1020   double minx=0.0, miny=0.0, maxx=0.0, maxy=0.0;
1021   symbolObj *newSymbol = NULL;
1022   double dp_x, dp_y, xcor, ycor;
1023   double TOL=0.00000000001;
1024   int i;
1025 
1026   /* assert(symbol->type == MS_SYMBOL_VECTOR); */
1027 
1028   newSymbol = (symbolObj *) malloc(sizeof(symbolObj));
1029   msCopySymbol(newSymbol, symbol, NULL); /* TODO: do we really want to do this for all symbol types? */
1030 
1031   angle_rad = (MS_DEG_TO_RAD*angle);
1032 
1033 
1034   sin_a = sin(angle_rad);
1035   cos_a = cos(angle_rad);
1036 
1037   dp_x = symbol->sizex * .5; /* get the shift vector at 0,0 */
1038   dp_y = symbol->sizey * .5;
1039 
1040   /* center at 0,0 and rotate; then move back */
1041   for( i=0; i < symbol->numpoints; i++) {
1042     /* don't rotate PENUP commands (TODO: should use a constant here) */
1043     if ((symbol->points[i].x == -99.0) && (symbol->points[i].y == -99.0) ) {
1044       newSymbol->points[i].x = -99.0;
1045       newSymbol->points[i].y = -99.0;
1046       continue;
1047     }
1048 
1049     newSymbol->points[i].x = dp_x + ((symbol->points[i].x-dp_x)*cos_a - (symbol->points[i].y-dp_y)*sin_a);
1050     newSymbol->points[i].y = dp_y + ((symbol->points[i].x-dp_x)*sin_a + (symbol->points[i].y-dp_y)*cos_a);
1051   }
1052 
1053   /* get the new bbox of the symbol, because we need it to get the new dimensions of the new symbol */
1054   get_bbox(newSymbol->points, newSymbol->numpoints, &minx, &miny, &maxx, &maxy);
1055   if ( (fabs(minx)>TOL) || (fabs(miny)>TOL) ) {
1056     xcor = minx*-1.0; /* symbols always start at 0,0 so get the shift vector */
1057     ycor = miny*-1.0;
1058     for( i=0; i < newSymbol->numpoints; i++) {
1059       if ((newSymbol->points[i].x == -99.0) && (newSymbol->points[i].y == -99.0))
1060         continue;
1061       newSymbol->points[i].x = newSymbol->points[i].x + xcor;
1062       newSymbol->points[i].y = newSymbol->points[i].y + ycor;
1063     }
1064 
1065     /* update the bbox to get the final dimension values for the symbol */
1066     get_bbox(newSymbol->points, newSymbol->numpoints, &minx, &miny, &maxx, &maxy);
1067   }
1068 
1069   newSymbol->sizex = maxx;
1070   newSymbol->sizey = maxy;
1071   return newSymbol;
1072 }
1073