/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 | } |