Coverage Report

Created: 2025-08-28 06:57

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