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