Coverage Report

Created: 2025-11-16 06:25

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