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