1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  Labeling Implementation.
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 /*
31 ** maplabel.c: Routines to enable text drawing, BITMAP or TRUETYPE.
32 */
33 
34 #include <float.h>
35 
36 #include "mapserver.h"
37 #include "fontcache.h"
38 
39 #include "cpl_vsi.h"
40 #include "cpl_string.h"
41 
42 
43 
44 
45 
initTextPath(textPathObj * ts)46 void initTextPath(textPathObj *ts) {
47   memset(ts,0,sizeof(*ts));
48 }
49 
50 int WARN_UNUSED msLayoutTextSymbol(mapObj *map, textSymbolObj *ts, textPathObj *tgret);
51 
52 #if defined(USE_EXTENDED_DEBUG) && 0
msDebugTextPath(textSymbolObj * ts)53 static void msDebugTextPath(textSymbolObj *ts) {
54   int i;
55   msDebug("text: %s\n",ts->annotext);
56   if(ts->textpath) {
57     for(i=0;i<ts->textpath->numglyphs; i++) {
58       glyphObj *g = &ts->textpath->glyphs[i];
59       msDebug("glyph %d: pos: %f %f\n",g->glyph->key.codepoint,g->pnt.x,g->pnt.y);
60     }
61   } else {
62     msDebug("no glyphs\n");
63   }
64   msDebug("\n=========================================\n");
65 }
66 #endif
67 
msComputeTextPath(mapObj * map,textSymbolObj * ts)68 int msComputeTextPath(mapObj *map, textSymbolObj *ts) {
69   textPathObj *tgret = msSmallMalloc(sizeof(textPathObj));
70   assert(ts->annotext && *ts->annotext);
71   initTextPath(tgret);
72   ts->textpath = tgret;
73   tgret->absolute = 0;
74   tgret->glyph_size = ts->label->size * ts->scalefactor;
75   tgret->glyph_size = MS_MAX(tgret->glyph_size, ts->label->minsize * ts->resolutionfactor);
76   tgret->glyph_size = MS_NINT(MS_MIN(tgret->glyph_size, ts->label->maxsize * ts->resolutionfactor));
77   tgret->line_height = ceil(tgret->glyph_size * 1.33);
78   return msLayoutTextSymbol(map,ts,tgret);
79 }
80 
initTextSymbol(textSymbolObj * ts)81 void initTextSymbol(textSymbolObj *ts) {
82   memset(ts,0,sizeof(*ts));
83 }
84 
freeTextPath(textPathObj * tp)85 void freeTextPath(textPathObj *tp) {
86   free(tp->glyphs);
87   if(tp->bounds.poly) {
88     free(tp->bounds.poly->point);
89     free(tp->bounds.poly);
90   }
91 }
freeTextSymbol(textSymbolObj * ts)92 void freeTextSymbol(textSymbolObj *ts) {
93   if(ts->textpath) {
94     freeTextPath(ts->textpath);
95     free(ts->textpath);
96   }
97   if(ts->label->numstyles) {
98     if(ts->style_bounds) {
99       int i;
100       for(i=0;i<ts->label->numstyles; i++) {
101         if(ts->style_bounds[i]) {
102           if(ts->style_bounds[i]->poly) {
103             free(ts->style_bounds[i]->poly->point);
104             free(ts->style_bounds[i]->poly);
105           }
106           free(ts->style_bounds[i]);
107         }
108       }
109       free(ts->style_bounds);
110     }
111   }
112   free(ts->annotext);
113   if(freeLabel(ts->label) == MS_SUCCESS) {
114     free(ts->label);
115   }
116 }
117 
msCopyTextPath(textPathObj * dst,textPathObj * src)118 void msCopyTextPath(textPathObj *dst, textPathObj *src) {
119   int i;
120   *dst = *src;
121   if(src->bounds.poly) {
122     dst->bounds.poly = msSmallMalloc(sizeof(lineObj));
123     dst->bounds.poly->numpoints = src->bounds.poly->numpoints;
124     dst->bounds.poly->point = msSmallMalloc(src->bounds.poly->numpoints * sizeof(pointObj));
125     for(i=0; i<src->bounds.poly->numpoints; i++) {
126       dst->bounds.poly->point[i] = src->bounds.poly->point[i];
127     }
128   } else {
129     dst->bounds.poly = NULL;
130   }
131   if(dst->numglyphs > 0) {
132     dst->glyphs = msSmallMalloc(dst->numglyphs * sizeof(glyphObj));
133     for(i=0; i<dst->numglyphs; i++)
134       dst->glyphs[i] = src->glyphs[i];
135   }
136 }
137 
msCopyTextSymbol(textSymbolObj * dst,textSymbolObj * src)138 void msCopyTextSymbol(textSymbolObj *dst, textSymbolObj *src) {
139   *dst = *src;
140   MS_REFCNT_INCR(src->label);
141   dst->annotext = msStrdup(src->annotext);
142   if(dst->textpath) {
143     dst->textpath = msSmallMalloc(sizeof(textPathObj));
144     msCopyTextPath(dst->textpath,src->textpath);
145   }
146   if(dst->style_bounds) {
147     int i;
148     dst->style_bounds = msSmallCalloc(src->label->numstyles, sizeof(label_bounds*));
149     for(i=0; i<src->label->numstyles; i++) {
150       if(src->style_bounds[i]) {
151         dst->style_bounds[i] = msSmallMalloc(sizeof(label_bounds));
152         copyLabelBounds(dst->style_bounds[i], src->style_bounds[i]);
153       }
154     }
155   }
156 }
157 
labelNeedsDeepCopy(labelObj * label)158 static int labelNeedsDeepCopy(labelObj *label) {
159   int i;
160   if(label->numbindings > 0) return MS_TRUE;
161   for(i=0; i<label->numstyles; i++) {
162     if(label->styles[i]->numbindings>0) {
163       return MS_TRUE;
164     }
165   }
166   return MS_FALSE;
167 }
168 
msPopulateTextSymbolForLabelAndString(textSymbolObj * ts,labelObj * l,char * string,double scalefactor,double resolutionfactor,label_cache_mode cache)169 void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char *string, double scalefactor, double resolutionfactor, label_cache_mode cache) {
170   if(cache == duplicate_always) {
171     ts->label = msSmallMalloc(sizeof(labelObj));
172     initLabel(ts->label);
173     msCopyLabel(ts->label,l);
174   } else if(cache == duplicate_never) {
175     ts->label = l;
176     MS_REFCNT_INCR(l);
177   } else if(cache == duplicate_if_needed && labelNeedsDeepCopy(l)) {
178     ts->label = msSmallMalloc(sizeof(labelObj));
179     initLabel(ts->label);
180     msCopyLabel(ts->label,l);
181   } else {
182     ts->label = l;
183     MS_REFCNT_INCR(l);
184   }
185   ts->resolutionfactor = resolutionfactor;
186   ts->scalefactor = scalefactor;
187   ts->annotext = string; /* we take the ownership of the annotation text */
188   ts->rotation = l->angle * MS_DEG_TO_RAD;
189 }
190 
msAddLabelGroup(mapObj * map,imageObj * image,layerObj * layer,int classindex,shapeObj * shape,pointObj * point,double featuresize)191 int msAddLabelGroup(mapObj *map, imageObj *image, layerObj* layer, int classindex, shapeObj *shape, pointObj *point, double featuresize)
192 {
193   int l,s, priority;
194   labelCacheSlotObj *cacheslot;
195 
196   labelCacheMemberObj *cachePtr=NULL;
197   layerObj *layerPtr=NULL;
198   classObj *classPtr=NULL;
199   int numtextsymbols = 0;
200   textSymbolObj **textsymbols, *ts;
201   int layerindex = layer->index;
202 
203   // We cannot use GET_LAYER here because in drawQuery the drawing may happen
204   // on a temp layer only.
205   layerPtr = layer;
206   classPtr = layer->class[classindex];
207 
208   if(classPtr->numlabels == 0) return MS_SUCCESS; /* not an error just nothing to do */
209 
210   /* check that the label intersects the layer mask */
211   if(layerPtr->mask) {
212     int maskLayerIdx = msGetLayerIndex(map,layerPtr->mask);
213     layerObj *maskLayer = GET_LAYER(map,maskLayerIdx);
214     unsigned char *alphapixptr;
215     if(maskLayer->maskimage && MS_IMAGE_RENDERER(maskLayer->maskimage)->supports_pixel_buffer) {
216       rasterBufferObj rb;
217       int x,y;
218       memset(&rb,0,sizeof(rasterBufferObj));
219       if(UNLIKELY(MS_FAILURE == MS_IMAGE_RENDERER(maskLayer->maskimage)->getRasterBufferHandle(maskLayer->maskimage,&rb))) {
220         return MS_FAILURE;
221       }
222       x = MS_NINT(point->x);
223       y = MS_NINT(point->y);
224       /* Using label repeatdistance, we might have a point with x/y below 0. See #4764 */
225       if (x >= 0 && x < rb.width && y >= 0 && y < rb.height) {
226         assert(rb.type == MS_BUFFER_BYTE_RGBA);
227         alphapixptr = rb.data.rgba.a+rb.data.rgba.row_step*y + rb.data.rgba.pixel_step*x;
228         if(!*alphapixptr) {
229           /* label point does not intersect mask */
230           return MS_SUCCESS;
231         }
232       } else {
233         return MS_SUCCESS; /* label point does not intersect image extent, we cannot know if it intersects
234                              mask, so we discard it (#5237)*/
235       }
236     } else {
237       msSetError(MS_MISCERR, "Layer (%s) references references a mask layer, but the selected renderer does not support them", "msAddLabelGroup()", layerPtr->name);
238       return (MS_FAILURE);
239     }
240   }
241 
242   textsymbols = msSmallMalloc(classPtr->numlabels * sizeof(textSymbolObj*));
243 
244   for(l=0; l<classPtr->numlabels; l++) {
245     labelObj *lbl = classPtr->labels[l];
246     char *annotext;
247     if(msGetLabelStatus(map,layerPtr,shape,lbl) == MS_OFF) {
248       continue;
249     }
250     annotext = msShapeGetLabelAnnotation(layerPtr,shape,lbl);
251     if(!annotext) {
252       for(s=0;s<lbl->numstyles;s++) {
253         if(lbl->styles[s]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT)
254           break; /* we have a "symbol only label, so we shouldn't skip this label */
255       }
256       if(s == lbl->numstyles) {
257         continue; /* no anno text, and no label symbols */
258       }
259     }
260     ts = msSmallMalloc(sizeof(textSymbolObj));
261     initTextSymbol(ts);
262     msPopulateTextSymbolForLabelAndString(ts,lbl,annotext,layerPtr->scalefactor,image->resolutionfactor, 1);
263 
264     if(annotext && *annotext && lbl->autominfeaturesize && featuresize > 0) {
265       if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,ts))) {
266         freeTextSymbol(ts);
267         free(ts);
268         return MS_FAILURE;
269       }
270       if(featuresize < (ts->textpath->bounds.bbox.maxx - ts->textpath->bounds.bbox.minx)) {
271         /* feature is too big to be drawn, skip it */
272         freeTextSymbol(ts);
273         free(ts);
274         continue;
275       }
276     }
277     textsymbols[numtextsymbols] = ts;
278     numtextsymbols++;
279   }
280 
281   if(numtextsymbols == 0) {
282     free(textsymbols);
283     return MS_SUCCESS;
284   }
285 
286   /* Validate label priority value and get ref on label cache for it */
287   priority = classPtr->labels[0]->priority; /* take priority from the first label */
288   if (priority < 1)
289     priority = 1;
290   else if (priority > MS_MAX_LABEL_PRIORITY)
291     priority = MS_MAX_LABEL_PRIORITY;
292 
293   cacheslot = &(map->labelcache.slots[priority-1]);
294 
295   if(cacheslot->numlabels == cacheslot->cachesize) { /* just add it to the end */
296     cacheslot->labels = (labelCacheMemberObj *) realloc(cacheslot->labels, sizeof(labelCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT));
297     MS_CHECK_ALLOC(cacheslot->labels, sizeof(labelCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT), MS_FAILURE);
298     cacheslot->cachesize += MS_LABELCACHEINCREMENT;
299   }
300 
301   cachePtr = &(cacheslot->labels[cacheslot->numlabels]);
302 
303   cachePtr->layerindex = layerindex; /* so we can get back to this *raw* data if necessary */
304 
305   cachePtr->classindex = classindex;
306 
307   cachePtr->point = *point; /* the actual label point */
308 
309   cachePtr->leaderline = NULL;
310   cachePtr->leaderbbox = NULL;
311 
312   cachePtr->markerid = -1;
313 
314   cachePtr->status = MS_FALSE;
315 
316   if(layerPtr->type == MS_LAYER_POINT && classPtr->numstyles > 0) {
317     /* cache the marker placement, it's already on the map */
318     /* TO DO: at the moment only checks the bottom style, perhaps should check all of them */
319     /* #2347: after RFC-24 classPtr->styles could be NULL so we check it */
320     double w, h;
321     if(msGetMarkerSize(map, classPtr->styles[0], &w, &h, layerPtr->scalefactor) != MS_SUCCESS)
322       return(MS_FAILURE);
323 
324     if(cacheslot->nummarkers == cacheslot->markercachesize) { /* just add it to the end */
325       cacheslot->markers = (markerCacheMemberObj *) realloc(cacheslot->markers, sizeof(markerCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT));
326       MS_CHECK_ALLOC(cacheslot->markers, sizeof(markerCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT), MS_FAILURE);
327       cacheslot->markercachesize+=MS_LABELCACHEINCREMENT;
328     }
329 
330     cacheslot->markers[cacheslot->nummarkers].bounds.minx = (point->x - .5 * w);
331     cacheslot->markers[cacheslot->nummarkers].bounds.miny = (point->y - .5 * h);
332     cacheslot->markers[cacheslot->nummarkers].bounds.maxx = cacheslot->markers[cacheslot->nummarkers].bounds.minx + (w-1);
333     cacheslot->markers[cacheslot->nummarkers].bounds.maxy = cacheslot->markers[cacheslot->nummarkers].bounds.miny + (h-1);
334     cacheslot->markers[cacheslot->nummarkers].id = cacheslot->numlabels;
335 
336     cachePtr->markerid = cacheslot->nummarkers;
337     cacheslot->nummarkers++;
338   }
339   cachePtr->textsymbols = textsymbols;
340   cachePtr->numtextsymbols = numtextsymbols;
341 
342   cacheslot->numlabels++;
343 
344   return(MS_SUCCESS);
345 }
346 
msAddLabel(mapObj * map,imageObj * image,labelObj * label,int layerindex,int classindex,shapeObj * shape,pointObj * point,double featuresize,textSymbolObj * ts)347 int msAddLabel(mapObj *map, imageObj *image, labelObj *label, int layerindex, int classindex,
348     shapeObj *shape, pointObj *point, double featuresize, textSymbolObj *ts)
349 {
350   int i;
351   labelCacheSlotObj *cacheslot;
352   labelCacheMemberObj *cachePtr=NULL;
353   char *annotext = NULL;
354   layerObj *layerPtr;
355   classObj *classPtr;
356 
357   layerPtr=GET_LAYER(map,layerindex);
358   assert(layerPtr);
359 
360   assert(classindex < layerPtr->numclasses);
361   classPtr = layerPtr->class[classindex];
362 
363   assert(label);
364 
365   if(ts)
366     annotext = ts->annotext;
367   else if(shape)
368     annotext = msShapeGetLabelAnnotation(layerPtr,shape,label);
369 
370   if(!annotext) {
371     /* check if we have a labelpnt style */
372     for(i=0; i<label->numstyles; i++) {
373       if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT)
374         break;
375     }
376     if(i==label->numstyles) {
377       /* label has no text or marker symbols */
378       if(ts) {
379         freeTextSymbol(ts);
380         free(ts);
381       }
382       return MS_SUCCESS;
383     }
384   }
385 
386   if(classPtr->leader) {
387     if(ts && ts->textpath && ts->textpath->absolute) {
388       msSetError(MS_MISCERR, "LEADERs are not supported on ANGLE FOLLOW labels", "msAddLabel()");
389       return MS_FAILURE;
390     }
391   }
392   /* check that the label intersects the layer mask */
393 
394   if (layerPtr->mask) {
395     int maskLayerIdx = msGetLayerIndex(map, layerPtr->mask);
396     layerObj *maskLayer = GET_LAYER(map, maskLayerIdx);
397     unsigned char *alphapixptr;
398     if (maskLayer->maskimage && MS_IMAGE_RENDERER(maskLayer->maskimage)->supports_pixel_buffer) {
399       rasterBufferObj rb;
400       memset(&rb, 0, sizeof (rasterBufferObj));
401       if(UNLIKELY(MS_FAILURE == MS_IMAGE_RENDERER(maskLayer->maskimage)->getRasterBufferHandle(maskLayer->maskimage, &rb))) {
402         return MS_FAILURE;
403       }
404       assert(rb.type == MS_BUFFER_BYTE_RGBA);
405       if (point) {
406         int x = MS_NINT(point->x);
407         int y = MS_NINT(point->y);
408         /* Using label repeatdistance, we might have a point with x/y below 0. See #4764 */
409         if (x >= 0 && x < rb.width && y >= 0 && y < rb.height) {
410           alphapixptr = rb.data.rgba.a+rb.data.rgba.row_step*y + rb.data.rgba.pixel_step*x;
411           if(!*alphapixptr) {
412             /* label point does not intersect mask */
413             if(ts) {
414               freeTextSymbol(ts);
415               free(ts);
416             }
417             return MS_SUCCESS;
418           }
419         } else {
420           return MS_SUCCESS; /* label point does not intersect image extent, we cannot know if it intersects
421                                 mask, so we discard it (#5237)*/
422         }
423       } else if (ts && ts->textpath) {
424         int i = 0;
425         for (i = 0; i < ts->textpath->numglyphs; i++) {
426           int x = MS_NINT(ts->textpath->glyphs[i].pnt.x);
427           int y = MS_NINT(ts->textpath->glyphs[i].pnt.y);
428           if (x >= 0 && x < rb.width && y >= 0 && y < rb.height) {
429             alphapixptr = rb.data.rgba.a + rb.data.rgba.row_step * y + rb.data.rgba.pixel_step*x;
430             if (!*alphapixptr) {
431               freeTextSymbol(ts);
432               free(ts);
433               return MS_SUCCESS;
434             }
435           } else {
436             freeTextSymbol(ts);
437             free(ts);
438             return MS_SUCCESS; /* label point does not intersect image extent, we cannot know if it intersects
439                                   mask, so we discard it (#5237)*/
440           }
441         }
442       }
443     } else {
444       msSetError(MS_MISCERR, "Layer (%s) references references a mask layer, but the selected renderer does not support them", "msAddLabel()", layerPtr->name);
445       return (MS_FAILURE);
446     }
447   }
448 
449   if(!ts) {
450     ts = msSmallMalloc(sizeof(textSymbolObj));
451     initTextSymbol(ts);
452     msPopulateTextSymbolForLabelAndString(ts,label,annotext,layerPtr->scalefactor,image->resolutionfactor, 1);
453   }
454 
455   if(annotext && label->autominfeaturesize && featuresize > 0) {
456     if(!ts->textpath) {
457       if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,ts)))
458         return MS_FAILURE;
459     }
460     if(featuresize > (ts->textpath->bounds.bbox.maxx - ts->textpath->bounds.bbox.minx)) {
461       /* feature is too big to be drawn, skip it */
462       freeTextSymbol(ts);
463       free(ts);
464       return MS_SUCCESS;
465     }
466   }
467 
468 
469 
470   /* Validate label priority value and get ref on label cache for it */
471   if (label->priority < 1)
472     label->priority = 1;
473   else if (label->priority > MS_MAX_LABEL_PRIORITY)
474     label->priority = MS_MAX_LABEL_PRIORITY;
475 
476   cacheslot = &(map->labelcache.slots[label->priority-1]);
477 
478   if(cacheslot->numlabels == cacheslot->cachesize) { /* just add it to the end */
479     cacheslot->labels = (labelCacheMemberObj *) realloc(cacheslot->labels, sizeof(labelCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT));
480     MS_CHECK_ALLOC(cacheslot->labels, sizeof(labelCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT), MS_FAILURE);
481     cacheslot->cachesize += MS_LABELCACHEINCREMENT;
482   }
483 
484   cachePtr = &(cacheslot->labels[cacheslot->numlabels]);
485 
486   cachePtr->layerindex = layerindex; /* so we can get back to this *raw* data if necessary */
487   cachePtr->classindex = classindex;
488 #ifdef include_deprecated
489   if(shape) {
490     cachePtr->shapetype = shape->type;
491   } else {
492     cachePtr->shapetype = MS_SHAPE_POINT;
493   }
494 #endif
495 
496   cachePtr->leaderline = NULL;
497   cachePtr->leaderbbox = NULL;
498 
499   /* Store the label point or the label path (Bug #1620) */
500   if ( point ) {
501     cachePtr->point = *point; /* the actual label point */
502   } else {
503     assert(ts && ts->textpath && ts->textpath->absolute && ts->textpath->numglyphs>0);
504     /* Use the middle point of the labelpath for mindistance calculations */
505     cachePtr->point = ts->textpath->glyphs[ts->textpath->numglyphs/2].pnt;
506   }
507 
508   /* copy the label */
509   cachePtr->numtextsymbols = 1;
510   cachePtr->textsymbols = (textSymbolObj **) msSmallMalloc(sizeof(textSymbolObj*));
511   cachePtr->textsymbols[0] = ts;
512   cachePtr->markerid = -1;
513 
514   cachePtr->status = MS_FALSE;
515 
516   if(layerPtr->type == MS_LAYER_POINT && classPtr->numstyles > 0) { /* cache the marker placement, it's already on the map */
517     double w, h;
518 
519     if(cacheslot->nummarkers == cacheslot->markercachesize) { /* just add it to the end */
520       cacheslot->markers = (markerCacheMemberObj *) realloc(cacheslot->markers, sizeof(markerCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT));
521       MS_CHECK_ALLOC(cacheslot->markers, sizeof(markerCacheMemberObj)*(cacheslot->cachesize+MS_LABELCACHEINCREMENT), MS_FAILURE);
522       cacheslot->markercachesize+=MS_LABELCACHEINCREMENT;
523     }
524 
525     i = cacheslot->nummarkers;
526 
527     /* TO DO: at the moment only checks the bottom style, perhaps should check all of them */
528     /* #2347: after RFC-24 classPtr->styles could be NULL so we check it */
529     if(classPtr->styles != NULL) {
530       if(msGetMarkerSize(map, classPtr->styles[0], &w, &h, layerPtr->scalefactor) != MS_SUCCESS)
531         return(MS_FAILURE);
532       cacheslot->markers[cacheslot->nummarkers].bounds.minx = (point->x - .5 * w);
533       cacheslot->markers[cacheslot->nummarkers].bounds.miny = (point->y - .5 * h);
534       cacheslot->markers[cacheslot->nummarkers].bounds.maxx = cacheslot->markers[cacheslot->nummarkers].bounds.minx + (w-1);
535       cacheslot->markers[cacheslot->nummarkers].bounds.maxy = cacheslot->markers[cacheslot->nummarkers].bounds.miny + (h-1);
536       cacheslot->markers[i].id = cacheslot->numlabels;
537 
538       cachePtr->markerid = i;
539 
540       cacheslot->nummarkers++;
541     }
542   }
543 
544   cacheslot->numlabels++;
545 
546   return(MS_SUCCESS);
547 }
548 
549 /*
550 ** Is a label completely in the image, reserving a gutter (in pixels) inside
551 ** image for no labels (effectively making image larger. The gutter can be
552 ** negative in cases where a label has a buffer around it.
553 */
labelInImage(int width,int height,lineObj * lpoly,rectObj * bounds,int gutter)554 static int labelInImage(int width, int height, lineObj *lpoly, rectObj *bounds, int gutter)
555 {
556   int j;
557 
558   /* do a bbox test first */
559   if(bounds->minx >= gutter &&
560       bounds->miny >= gutter &&
561       bounds->maxx < width-gutter &&
562       bounds->maxy < height-gutter) {
563     return MS_TRUE;
564   }
565 
566   if(lpoly) {
567     for(j=1; j<lpoly->numpoints; j++) {
568       if(lpoly->point[j].x < gutter) return(MS_FALSE);
569       if(lpoly->point[j].x >= width-gutter) return(MS_FALSE);
570       if(lpoly->point[j].y < gutter) return(MS_FALSE);
571       if(lpoly->point[j].y >= height-gutter) return(MS_FALSE);
572     }
573   } else {
574     /* if no poly, then return false as the boundong box intersected */
575     return MS_FALSE;
576   }
577 
578   return(MS_TRUE);
579 }
580 
insertRenderedLabelMember(mapObj * map,labelCacheMemberObj * cachePtr)581 void insertRenderedLabelMember(mapObj *map, labelCacheMemberObj *cachePtr) {
582   if(map->labelcache.num_rendered_members == map->labelcache.num_allocated_rendered_members) {
583     if(map->labelcache.num_rendered_members == 0) {
584       map->labelcache.num_allocated_rendered_members = 50;
585     } else {
586       map->labelcache.num_allocated_rendered_members *= 2;
587     }
588     map->labelcache.rendered_text_symbols = msSmallRealloc(map->labelcache.rendered_text_symbols,
589             map->labelcache.num_allocated_rendered_members * sizeof(labelCacheMemberObj*));
590   }
591   map->labelcache.rendered_text_symbols[map->labelcache.num_rendered_members++] = cachePtr;
592 }
593 
testSegmentLabelBBoxIntersection(const rectObj * leaderbbox,const pointObj * lp1,const pointObj * lp2,const label_bounds * test)594 static inline int testSegmentLabelBBoxIntersection(const rectObj *leaderbbox, const pointObj *lp1,
595     const pointObj *lp2, const label_bounds *test) {
596   if(msRectOverlap(leaderbbox, &test->bbox)) {
597     if(test->poly) {
598       int pp;
599       for(pp=1; pp<test->poly->numpoints; pp++) {
600         if(msIntersectSegments(
601             &(test->poly->point[pp-1]),
602             &(test->poly->point[pp]),
603             lp1,lp2) ==  MS_TRUE) {
604           return(MS_FALSE);
605         }
606       }
607     } else {
608       pointObj tp1,tp2;
609       tp1.x = test->bbox.minx;
610       tp1.y = test->bbox.miny;
611       tp2.x = test->bbox.minx;
612       tp2.y = test->bbox.maxy;
613       if(msIntersectSegments(lp1,lp2,&tp1,&tp2))
614         return MS_FALSE;
615       tp2.x = test->bbox.maxx;
616       tp2.y = test->bbox.miny;
617       if(msIntersectSegments(lp1,lp2,&tp1,&tp2))
618         return MS_FALSE;
619       tp1.x = test->bbox.maxx;
620       tp1.y = test->bbox.maxy;
621       tp2.x = test->bbox.minx;
622       tp2.y = test->bbox.maxy;
623       if(msIntersectSegments(lp1,lp2,&tp1,&tp2))
624         return MS_FALSE;
625       tp2.x = test->bbox.maxx;
626       tp2.y = test->bbox.miny;
627       if(msIntersectSegments(lp1,lp2,&tp1,&tp2))
628         return MS_FALSE;
629     }
630   }
631   return MS_TRUE;
632 }
633 
msTestLabelCacheLeaderCollision(mapObj * map,pointObj * lp1,pointObj * lp2)634 int msTestLabelCacheLeaderCollision(mapObj *map, pointObj *lp1, pointObj *lp2) {
635   int p;
636   rectObj leaderbbox;
637   leaderbbox.minx = MS_MIN(lp1->x,lp2->x);
638   leaderbbox.maxx = MS_MAX(lp1->x,lp2->x);
639   leaderbbox.miny = MS_MIN(lp1->y,lp2->y);
640   leaderbbox.maxy = MS_MAX(lp1->y,lp2->y);
641   for(p=0; p<map->labelcache.num_rendered_members; p++) {
642     labelCacheMemberObj *curCachePtr= map->labelcache.rendered_text_symbols[p];
643     if(msRectOverlap(&leaderbbox, &(curCachePtr->bbox))) {
644     /* leaderbbox interesects with the curCachePtr's global bbox */
645       int t;
646       for(t=0; t<curCachePtr->numtextsymbols; t++) {
647         int s;
648         textSymbolObj *ts = curCachePtr->textsymbols[t];
649         /* check for intersect with textpath */
650         if(ts->textpath && testSegmentLabelBBoxIntersection(&leaderbbox, lp1, lp2, &ts->textpath->bounds) == MS_FALSE) {
651           return MS_FALSE;
652         }
653         /* check for intersect with label's labelpnt styles */
654         if(ts->style_bounds) {
655           for(s=0; s<ts->label->numstyles; s++) {
656             if(ts->label->styles[s]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
657               if(testSegmentLabelBBoxIntersection(&leaderbbox, lp1,lp2, ts->style_bounds[s]) == MS_FALSE) {
658                 return MS_FALSE;
659               }
660             }
661           }
662         }
663       }
664       if(curCachePtr->leaderbbox) {
665         if(msIntersectSegments(lp1,lp2,&(curCachePtr->leaderline->point[0]), &(curCachePtr->leaderline->point[1])) ==  MS_TRUE) {
666           return MS_FALSE;
667         }
668       }
669     }
670   }
671   return MS_TRUE;
672 }
673 
674 /* msTestLabelCacheCollisions()
675 **
676 ** Compares label bounds (in *bounds) against labels already drawn and markers from cache and
677 ** returns MS_FALSE if it collides with another label, or collides with a marker.
678 **
679 ** This function is used by the various msDrawLabelCacheXX() implementations.
680 
681 int msTestLabelCacheCollisions(mapObj *map, labelCacheMemberObj *cachePtr, label_bounds *bounds,
682               int current_priority, int current_label);
683 */
msTestLabelCacheCollisions(mapObj * map,labelCacheMemberObj * cachePtr,label_bounds * lb,int current_priority,int current_label)684 int msTestLabelCacheCollisions(mapObj *map, labelCacheMemberObj *cachePtr, label_bounds *lb,
685         int current_priority, int current_label)
686 {
687   labelCacheObj *labelcache = &(map->labelcache);
688   int i, p, ll;
689 
690   /*
691    * Check against image bounds first
692    */
693   if(!cachePtr->textsymbols[0]->label->partials) {
694     if(labelInImage(map->width, map->height, lb->poly, &lb->bbox, labelcache->gutter) == MS_FALSE) {
695       return MS_FALSE;
696     }
697   }
698 
699   /* Compare against all rendered markers from this priority level and higher.
700   ** Labels can overlap their own marker and markers from lower priority levels
701   */
702   for (p=current_priority; p < MS_MAX_LABEL_PRIORITY; p++) {
703     labelCacheSlotObj *markerslot;
704     markerslot = &(labelcache->slots[p]);
705 
706     for ( ll = 0; ll < markerslot->nummarkers; ll++ ) {
707       if ( !(p == current_priority && current_label == markerslot->markers[ll].id ) ) {  /* labels can overlap their own marker */
708         if ( intersectLabelPolygons(NULL, &markerslot->markers[ll].bounds, lb->poly, &lb->bbox ) == MS_TRUE ) {
709           return MS_FALSE;
710         }
711       }
712     }
713   }
714 
715   for(p=0; p<labelcache->num_rendered_members; p++) {
716     labelCacheMemberObj *curCachePtr= labelcache->rendered_text_symbols[p];
717     if(msRectOverlap(&curCachePtr->bbox,&lb->bbox)) {
718       for(i=0; i<curCachePtr->numtextsymbols; i++) {
719         int j;
720         textSymbolObj *ts = curCachePtr->textsymbols[i];
721         if(ts->textpath && intersectLabelPolygons(ts->textpath->bounds.poly, &ts->textpath->bounds.bbox, lb->poly, &lb->bbox) == MS_TRUE ) {
722           return MS_FALSE;
723         }
724         if(ts->style_bounds) {
725           for(j=0;j<ts->label->numstyles;j++) {
726             if(ts->style_bounds[j] && ts->label->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
727               if(intersectLabelPolygons(ts->style_bounds[j]->poly, &ts->style_bounds[j]->bbox,
728                   lb->poly, &lb->bbox)) {
729                 return MS_FALSE;
730               }
731             }
732           }
733         }
734       }
735     }
736     if(curCachePtr->leaderline) {
737       if(testSegmentLabelBBoxIntersection(curCachePtr->leaderbbox, &curCachePtr->leaderline->point[0],
738           &curCachePtr->leaderline->point[1], lb) == MS_FALSE) {
739         return MS_FALSE;
740       }
741     }
742   }
743   return MS_TRUE;
744 }
745 
746 /* utility function to get the rect of a string outside of a rendering loop, i.e.
747  without going through textpath layouts */
msGetStringSize(mapObj * map,labelObj * label,int size,char * string,rectObj * r)748 int msGetStringSize(mapObj *map, labelObj *label, int size, char *string, rectObj *r) {
749   textSymbolObj ts;
750   double lsize = label->size;
751   initTextSymbol(&ts);
752   label->size = size;
753   msPopulateTextSymbolForLabelAndString(&ts,label,msStrdup(string),1,1,0);
754   if(UNLIKELY(MS_FAILURE == msGetTextSymbolSize(map,&ts,r)))
755     return MS_FAILURE;
756   label->size = lsize;
757   freeTextSymbol(&ts);
758   return MS_SUCCESS;
759 }
760 
msInitFontSet(fontSetObj * fontset)761 int msInitFontSet(fontSetObj *fontset)
762 {
763   fontset->filename = NULL;
764 
765   /* fontset->fonts = NULL; */
766   initHashTable(&(fontset->fonts));
767 
768   fontset->numfonts = 0;
769   fontset->map = NULL;
770   return( 0 );
771 }
772 
msFreeFontSet(fontSetObj * fontset)773 int msFreeFontSet(fontSetObj *fontset)
774 {
775   if (fontset->filename)
776     free(fontset->filename);
777   fontset->filename = NULL;
778   if (&(fontset->fonts))
779     msFreeHashItems(&(fontset->fonts));
780   /* fontset->fonts = NULL; */
781   fontset->numfonts = 0;
782 
783   return( 0 );
784 }
785 
msLoadFontSet(fontSetObj * fontset,mapObj * map)786 int msLoadFontSet(fontSetObj *fontset, mapObj *map)
787 {
788   VSILFILE *stream;
789   const char* line;
790   char alias[64], file1[MS_PATH_LENGTH], file2[MS_PATH_LENGTH];
791   char *path;
792   char szPath[MS_MAXPATHLEN];
793   int i;
794   int bFullPath = 0;
795 
796   if(fontset->numfonts != 0) /* already initialized */
797     return(0);
798 
799   if(!fontset->filename)
800     return(0);
801 
802   fontset->map = (mapObj *)map;
803 
804   path = msGetPath(fontset->filename);
805 
806   /* fontset->fonts = msCreateHashTable(); // create font hash */
807   /* if(!fontset->fonts) { */
808   /* msSetError(MS_HASHERR, "Error initializing font hash.", "msLoadFontSet()"); */
809   /* return(-1); */
810   /* } */
811 
812   stream = VSIFOpenL( msBuildPath(szPath, fontset->map->mappath, fontset->filename), "rb");
813   if(!stream) {
814     msSetError(MS_IOERR, "Error opening fontset %s.", "msLoadFontset()",
815                fontset->filename);
816     return(-1);
817   }
818 
819   i = 0;
820   while( (line = CPLReadLineL(stream)) != NULL ) { /* while there's something to load */
821 
822     if(line[0] == '#' || line[0] == '\n' || line[0] == '\r' || line[0] == ' ')
823       continue; /* skip comments and blank lines */
824 
825     sscanf(line,"%s %s", alias,  file1);
826 
827     if (!(*file1) || !(*alias) || (strlen(file1) <= 0))
828       continue;
829 
830     bFullPath = 0;
831 #if defined(_WIN32) && !defined(__CYGWIN__)
832     if (file1[0] == '\\' || (strlen(file1) > 1 && (file1[1] == ':')))
833       bFullPath = 1;
834 #else
835     if(file1[0] == '/')
836       bFullPath = 1;
837 #endif
838 
839     if(bFullPath) { /* already full path */
840       msInsertHashTable(&(fontset->fonts), alias, file1);
841     } else {
842       snprintf(file2, sizeof(file2), "%s%s", path, file1);
843       /* msInsertHashTable(fontset->fonts, alias, file2); */
844 
845       /*
846       ** msBuildPath is use here, but if we have to save the fontset file
847       ** the msBuildPath must be done everywhere the fonts are used and
848       ** removed here.
849       */
850       msInsertHashTable(&(fontset->fonts), alias,
851                         msBuildPath(szPath, fontset->map->mappath, file2));
852 
853     }
854 
855     i++;
856   }
857 
858   fontset->numfonts = i;
859   VSIFCloseL(stream); /* close the file */
860   free(path);
861 
862   return(0);
863 }
864 
865 
msGetTextSymbolSize(mapObj * map,textSymbolObj * ts,rectObj * r)866 int msGetTextSymbolSize(mapObj *map, textSymbolObj *ts, rectObj *r) {
867   if(!ts->textpath) {
868     if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,ts)))
869       return MS_FAILURE;
870   }
871   *r = ts->textpath->bounds.bbox;
872   return MS_SUCCESS;
873 }
874 /*
875 ** Note: All these routines assume a reference point at the LL corner of the text. GD's
876 ** bitmapped fonts use UL and this is compensated for. Note the rect is relative to the
877 ** LL corner of the text to be rendered, this is first line for TrueType fonts.
878 */
879 
880 #define MARKER_SLOP 2
881 
get_metrics(pointObj * p,int position,textPathObj * tp,int ox,int oy,double rotation,int buffer,label_bounds * bounds)882 pointObj get_metrics(pointObj *p, int position, textPathObj *tp, int ox, int oy, double rotation, int buffer, label_bounds *bounds)
883 {
884   pointObj q = {0,0,0,0}; // initialize
885   double x1=0, y1=0, x2=0, y2=0;
886   double sin_a,cos_a;
887   double w, h, x, y;
888 
889   w = tp->bounds.bbox.maxx - tp->bounds.bbox.minx;
890   h = tp->bounds.bbox.maxy - tp->bounds.bbox.miny;
891 
892   switch(position) {
893     case MS_UL:
894       x1 = -w - ox;
895       y1 = -oy;
896       break;
897     case MS_UC:
898       x1 = -(w/2.0);
899       y1 = -oy - MARKER_SLOP;
900       break;
901     case MS_UR:
902       x1 = ox;
903       y1 = -oy;
904       break;
905     case MS_CL:
906       x1 = -w - ox - MARKER_SLOP;
907       if(oy > 0 && tp->numlines == 1)
908         y1 = oy;
909       else
910         y1 = (h/2.0);
911       break;
912     case MS_CC:
913       x1 = -(w/2.0) + ox;
914       y1 = (h/2.0) + oy;
915       break;
916     case MS_CR:
917       x1 = ox + MARKER_SLOP;
918       if(oy > 0 && tp->numlines == 1)
919         y1 = oy;
920       else
921         y1 = (h/2.0);
922       break;
923     case MS_LL:
924       x1 = -w - ox;
925       y1 = h + oy;
926       break;
927     case MS_LC:
928       x1 = -(w/2.0);
929       y1 = h + oy + MARKER_SLOP;
930       break;
931     case MS_LR:
932       x1 = ox;
933       y1 = h + oy;
934       break;
935   }
936 
937   if(rotation) {
938     sin_a = sin(rotation);
939     cos_a = cos(rotation);
940 
941     x = x1 - tp->bounds.bbox.minx;
942     y = tp->bounds.bbox.maxy - y1;
943     q.x = p->x + (x * cos_a - (y) * sin_a);
944     q.y = p->y - (x * sin_a + (y) * cos_a);
945 
946     if(bounds) {
947       x2 = x1 - buffer; /* ll */
948       y2 = y1 + buffer;
949       bounds->poly->point[0].x = p->x + (x2 * cos_a - (-y2) * sin_a);
950       bounds->poly->point[0].y = p->y - (x2 * sin_a + (-y2) * cos_a);
951 
952       x2 = x1 - buffer; /* ul */
953       y2 = y1 - h - buffer;
954       bounds->poly->point[1].x = p->x + (x2 * cos_a - (-y2) * sin_a);
955       bounds->poly->point[1].y = p->y - (x2 * sin_a + (-y2) * cos_a);
956 
957       x2 = x1 + w + buffer; /* ur */
958       y2 = y1 - h - buffer;
959       bounds->poly->point[2].x = p->x + (x2 * cos_a - (-y2) * sin_a);
960       bounds->poly->point[2].y = p->y - (x2 * sin_a + (-y2) * cos_a);
961 
962       x2 = x1 + w + buffer; /* lr */
963       y2 = y1 + buffer;
964       bounds->poly->point[3].x = p->x + (x2 * cos_a - (-y2) * sin_a);
965       bounds->poly->point[3].y = p->y - (x2 * sin_a + (-y2) * cos_a);
966 
967       bounds->poly->point[4].x = bounds->poly->point[0].x;
968       bounds->poly->point[4].y = bounds->poly->point[0].y;
969       fastComputeBounds(bounds->poly,&bounds->bbox);
970     }
971   }
972   else {
973     q.x = p->x + x1 - tp->bounds.bbox.minx;
974     q.y = p->y + y1 ;
975 
976     if(bounds) {
977       /* no rotation, we only need to return a bbox */
978       bounds->poly = NULL;
979 
980 
981       bounds->bbox.minx = q.x - buffer;
982       bounds->bbox.maxy = q.y + buffer + tp->bounds.bbox.maxy;
983       bounds->bbox.maxx = q.x + w + buffer;
984       bounds->bbox.miny = bounds->bbox.maxy - h - buffer*2;
985     }
986   }
987   return(q);
988 }
989 
intersectTextSymbol(textSymbolObj * ts,label_bounds * lb)990 int intersectTextSymbol(textSymbolObj *ts, label_bounds *lb) {
991   if(ts->textpath && ts->textpath->absolute) {
992     if(intersectLabelPolygons(lb->poly,&lb->bbox,ts->textpath->bounds.poly,&ts->textpath->bounds.bbox))
993       return MS_TRUE;
994   }
995   if(ts->style_bounds) {
996     int s;
997     for(s=0; s<ts->label->numstyles; s++) {
998       if(ts->style_bounds[s] && ts->label->styles[s]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT &&
999           intersectLabelPolygons(lb->poly,&lb->bbox,ts->style_bounds[s]->poly, &ts->style_bounds[s]->bbox))
1000         return MS_TRUE;
1001     }
1002   }
1003   return MS_FALSE;
1004 }
1005 
1006 /*
1007 ** Variation on msIntersectPolygons. Label polygons aren't like shapefile polygons. They
1008 ** have no holes, and often do have overlapping parts (i.e. road symbols).
1009 */
intersectLabelPolygons(lineObj * l1,rectObj * r1,lineObj * l2,rectObj * r2)1010 int intersectLabelPolygons(lineObj *l1, rectObj *r1, lineObj *l2, rectObj *r2)
1011 {
1012   int v1,v2;
1013   pointObj *point;
1014   lineObj *p1,*p2,sp1,sp2;
1015   pointObj pnts1[5],pnts2[5];
1016 
1017 
1018   /* STEP 0: check bounding boxes */
1019   if(!msRectOverlap(r1,r2)) { /* from alans@wunderground.com */
1020     return(MS_FALSE);
1021   }
1022   if(!l1 && !l2)
1023     return MS_TRUE;
1024 
1025   if(!l1) {
1026     p1 = &sp1;
1027     p1->numpoints = 5;
1028     p1->point = pnts1;
1029     pnts1[0].x = pnts1[1].x = pnts1[4].x = r1->minx;
1030     pnts1[2].x = pnts1[3].x = r1->maxx;
1031     pnts1[0].y = pnts1[3].y = pnts1[4].y = r1->miny;
1032     pnts1[1].y = pnts1[2].y = r1->maxy;
1033   } else {
1034     p1 = l1;
1035   }
1036   if(!l2) {
1037     p2 = &sp2;
1038     p2->numpoints = 5;
1039     p2->point = pnts2;
1040     pnts2[0].x = pnts2[1].x = pnts2[4].x = r2->minx;
1041     pnts2[2].x = pnts2[3].x = r2->maxx;
1042     pnts2[0].y = pnts2[3].y = pnts2[4].y = r2->miny;
1043     pnts2[1].y = pnts2[2].y = r2->maxy;
1044   } else {
1045     p2 = l2;
1046   }
1047 
1048   /* STEP 1: look for intersecting line segments */
1049   for(v1=1; v1<p1->numpoints; v1++)
1050     for(v2=1; v2<p2->numpoints; v2++)
1051       if(msIntersectSegments(&(p1->point[v1-1]), &(p1->point[v1]), &(p2->point[v2-1]), &(p2->point[v2])) ==  MS_TRUE) {
1052         return(MS_TRUE);
1053     }
1054 
1055   /* STEP 2: polygon one completely contains two (only need to check one point from each part) */
1056   point = &(p2->point[0]);
1057   if(msPointInPolygon(point, p1) == MS_TRUE) /* ok, the point is in a polygon */
1058     return(MS_TRUE);
1059 
1060   /* STEP 3: polygon two completely contains one (only need to check one point from each part) */
1061   point = &(p1->point[0]);
1062   if(msPointInPolygon(point, p2) == MS_TRUE) /* ok, the point is in a polygon */
1063     return(MS_TRUE);
1064 
1065   return(MS_FALSE);
1066 }
1067 
1068 /* For MapScript, exactly the same the msInsertStyle */
msInsertLabelStyle(labelObj * label,styleObj * style,int nStyleIndex)1069 int msInsertLabelStyle(labelObj *label, styleObj *style, int nStyleIndex)
1070 {
1071   int i;
1072 
1073   if (!style) {
1074     msSetError(MS_CHILDERR, "Can't insert a NULL Style", "msInsertLabelStyle()");
1075     return -1;
1076   }
1077 
1078   /* Ensure there is room for a new style */
1079   if (msGrowLabelStyles(label) == NULL) {
1080     return -1;
1081   }
1082   /* Catch attempt to insert past end of styles array */
1083   else if (nStyleIndex >= label->numstyles) {
1084     msSetError(MS_CHILDERR, "Cannot insert style beyond index %d", "insertLabelStyle()", label->numstyles-1);
1085     return -1;
1086   } else if (nStyleIndex < 0) { /* Insert at the end by default */
1087     label->styles[label->numstyles]=style;
1088     MS_REFCNT_INCR(style);
1089     label->numstyles++;
1090     return label->numstyles-1;
1091   } else if (nStyleIndex >= 0 && nStyleIndex < label->numstyles) {
1092     /* Move styles existing at the specified nStyleIndex or greater */
1093     /* to a higher nStyleIndex */
1094     for (i=label->numstyles-1; i>=nStyleIndex; i--) {
1095       label->styles[i+1] = label->styles[i];
1096     }
1097     label->styles[nStyleIndex]=style;
1098     MS_REFCNT_INCR(style);
1099     label->numstyles++;
1100     return nStyleIndex;
1101   } else {
1102     msSetError(MS_CHILDERR, "Invalid nStyleIndex", "insertLabelStyle()");
1103     return -1;
1104   }
1105 }
1106 
1107 /**
1108  * Move the style up inside the array of styles.
1109  */
msMoveLabelStyleUp(labelObj * label,int nStyleIndex)1110 int msMoveLabelStyleUp(labelObj *label, int nStyleIndex)
1111 {
1112   styleObj *psTmpStyle = NULL;
1113   if (label && nStyleIndex < label->numstyles && nStyleIndex >0) {
1114     psTmpStyle = (styleObj *)malloc(sizeof(styleObj));
1115     initStyle(psTmpStyle);
1116 
1117     msCopyStyle(psTmpStyle, label->styles[nStyleIndex]);
1118 
1119     msCopyStyle(label->styles[nStyleIndex],
1120                 label->styles[nStyleIndex-1]);
1121 
1122     msCopyStyle(label->styles[nStyleIndex-1], psTmpStyle);
1123 
1124     return(MS_SUCCESS);
1125   }
1126   msSetError(MS_CHILDERR, "Invalid index: %d", "msMoveLabelStyleUp()",
1127              nStyleIndex);
1128   return (MS_FAILURE);
1129 }
1130 
1131 
1132 /**
1133  * Move the style down inside the array of styles.
1134  */
msMoveLabelStyleDown(labelObj * label,int nStyleIndex)1135 int msMoveLabelStyleDown(labelObj *label, int nStyleIndex)
1136 {
1137   styleObj *psTmpStyle = NULL;
1138 
1139   if (label && nStyleIndex < label->numstyles-1 && nStyleIndex >=0) {
1140     psTmpStyle = (styleObj *)malloc(sizeof(styleObj));
1141     initStyle(psTmpStyle);
1142 
1143     msCopyStyle(psTmpStyle, label->styles[nStyleIndex]);
1144 
1145     msCopyStyle(label->styles[nStyleIndex],
1146                 label->styles[nStyleIndex+1]);
1147 
1148     msCopyStyle(label->styles[nStyleIndex+1], psTmpStyle);
1149 
1150     return(MS_SUCCESS);
1151   }
1152   msSetError(MS_CHILDERR, "Invalid index: %d", "msMoveLabelStyleDown()",
1153              nStyleIndex);
1154   return (MS_FAILURE);
1155 }
1156 
1157 /**
1158  * Delete the style identified by the index and shift
1159  * styles that follows the deleted style.
1160  */
msDeleteLabelStyle(labelObj * label,int nStyleIndex)1161 int msDeleteLabelStyle(labelObj *label, int nStyleIndex)
1162 {
1163   int i = 0;
1164   if (label && nStyleIndex < label->numstyles && nStyleIndex >=0) {
1165     if (freeStyle(label->styles[nStyleIndex]) == MS_SUCCESS)
1166       msFree(label->styles[nStyleIndex]);
1167     for (i=nStyleIndex; i< label->numstyles-1; i++) {
1168       label->styles[i] = label->styles[i+1];
1169     }
1170     label->styles[label->numstyles-1] = NULL;
1171     label->numstyles--;
1172     return(MS_SUCCESS);
1173   }
1174   msSetError(MS_CHILDERR, "Invalid index: %d", "msDeleteLabelStyle()",
1175              nStyleIndex);
1176   return (MS_FAILURE);
1177 }
1178 
msRemoveLabelStyle(labelObj * label,int nStyleIndex)1179 styleObj *msRemoveLabelStyle(labelObj *label, int nStyleIndex)
1180 {
1181   int i;
1182   styleObj *style;
1183   if (nStyleIndex < 0 || nStyleIndex >= label->numstyles) {
1184     msSetError(MS_CHILDERR, "Cannot remove style, invalid nStyleIndex %d", "removeLabelStyle()", nStyleIndex);
1185     return NULL;
1186   } else {
1187     style=label->styles[nStyleIndex];
1188     for (i=nStyleIndex; i<label->numstyles-1; i++) {
1189       label->styles[i]=label->styles[i+1];
1190     }
1191     label->styles[label->numstyles-1]=NULL;
1192     label->numstyles--;
1193     MS_REFCNT_DECR(style);
1194     return style;
1195   }
1196 }
1197