1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: Various support code related to the outputFormatObj.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2002, Frank Warmerdam
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies of this Software or works derived from this Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 *****************************************************************************/
29
30 #include <assert.h>
31 #include "mapserver.h"
32 #include "mapows.h"
33
34
35
36 static outputFormatObj *msAllocOutputFormat( mapObj *map, const char *name,
37 const char *driver );
38
39 /*************************************************************************
40
41 NOTES on outputFormatObj:
42
43 typedef struct {
44 char *name;
45 char *mimetype;
46 char *driver;
47 int imagemode; // MS_IMAGEMODE_* value.
48 int transparent;
49 int numformatoptions;
50 char **formatoptions;
51 } outputFormatObj;
52
53 NAME - Associates an internal name with the declaration. The value used
54 has not intrinsic meaning and is just used to associate with the
55 MAP level IMAGETYPE. It is also the "name" used for the format
56 in WMS capabilities documents, and FORMAT= requests. (required)
57
58 MIMETYPE - The mime type to use for this format. If omitted, the value
59 will be derived from the DRIVER or default to the value for
60 untyped binary data will be used. (optional - may be NULL)
61
62 DRIVER - This indicates which internal driver mechanism is to be used.
63 Anything prefixed by "GDAL/" will be handled by the GDAL driver, with
64 the remainder taken as the GDAL format name. (required)
65
66 IMAGEMODE - Has one of "PC256", "RGB", or "RGBA" indicating whether
67 the imaging should be done to a 256 pseudo-colored, 24bit RGB, or
68 32bit RGBA (A=alpha/transparency) result image. Note that the
69 IMAGEMODE actually affects how all the rendering for the map is
70 done, long before it is output to a particular output format.
71 The default value is the traditional "PC256". Some output
72 formats can only support some IMAGEMODE values. (optional)
73
74 Translate as MS_IMAGEMODE_PC256, MS_IMAGEMODE_RGB and
75 MS_IMAGEMODE_RGBA.
76
77 For "GDAL/" drivers, the following extra imagemodes are supported:
78 "BYTE" / MS_IMAGEMODE_BYTE
79 "INT16" / MS_IMAGEMODE_INT16
80 "FLOAT32" / MS_IMAGEMODE_FLOAT32
81
82 Not too sure what this should be set to for output formats like
83 flash and SVG.
84
85 TRANSPARENT - A value of "ON" or "OFF" indicating whether transparency
86 support should be enabled. Same as the old TRANSPARENT flag
87 at the MAP level.
88
89 FORMATOPTION - Contains an argument for the specific driver used. There may
90 be any number of format options for a given OUTPUTFORMAT
91 declaration. FORMATOPTION will be used to encode the older
92 INTERLACE, and QUALITY values.
93
94 Handled as a MapServer style CharArray.
95
96 *************************************************************************/
97
98
99 struct defaultOutputFormatEntry {
100 const char *name;
101 const char *driver;
102 const char *mimetype;
103 } ;
104
105 struct defaultOutputFormatEntry defaultoutputformats[] = {
106 {"png","AGG/PNG","image/png"},
107 {"jpeg","AGG/JPEG","image/jpeg"},
108 {"png8","AGG/PNG8","image/png; mode=8bit"},
109 {"png24","AGG/PNG","image/png; mode=24bit"},
110 {"jpegpng", "AGG/MIXED", "image/vnd.jpeg-png"},
111 {"jpegpng8", "AGG/MIXED", "image/vnd.jpeg-png8"},
112 #ifdef USE_CAIRO
113 {"pdf","CAIRO/PDF","application/x-pdf"},
114 {"svg","CAIRO/SVG","image/svg+xml"},
115 {"cairopng","CAIRO/PNG","image/png"},
116 #endif
117 {"GTiff","GDAL/GTiff","image/tiff"},
118 #ifdef USE_KML
119 {"kml","KML","application/vnd.google-earth.kml+xml"},
120 {"kmz","KMZ","application/vnd.google-earth.kmz"},
121 #endif
122 #ifdef USE_PBF
123 {"mvt","MVT","application/vnd.mapbox-vector-tile"},
124 #endif
125 {"json","UTFGrid","application/json"},
126 {NULL,NULL,NULL}
127 };
128
129 /************************************************************************/
130 /* msPostMapParseOutputFormatSetup() */
131 /************************************************************************/
132
msPostMapParseOutputFormatSetup(mapObj * map)133 int msPostMapParseOutputFormatSetup( mapObj *map )
134
135 {
136 outputFormatObj *format;
137
138 /* If IMAGETYPE not set use the first user defined OUTPUTFORMAT.
139 If none, use the first default format. */
140 if( map->imagetype == NULL && map->numoutputformats > 0 )
141 map->imagetype = msStrdup(map->outputformatlist[0]->name);
142 if( map->imagetype == NULL)
143 map->imagetype = msStrdup(defaultoutputformats[0].name);
144
145 /* select the current outputformat into map->outputformat */
146 format = msSelectOutputFormat( map, map->imagetype );
147 if( format == NULL ) {
148 msSetError(MS_MISCERR,
149 "Unable to select IMAGETYPE `%s'.",
150 "msPostMapParseOutputFormatSetup()",
151 map->imagetype ? map->imagetype : "(null)" );
152 return MS_FAILURE;
153 }
154
155 msApplyOutputFormat( &(map->outputformat), format,
156 map->transparent, map->interlace, map->imagequality );
157
158 return MS_SUCCESS;
159 }
160
161 /************************************************************************/
162 /* msCreateDefaultOutputFormat() */
163 /************************************************************************/
164
msCreateDefaultOutputFormat(mapObj * map,const char * driver,const char * name)165 outputFormatObj *msCreateDefaultOutputFormat( mapObj *map,
166 const char *driver,
167 const char *name )
168
169 {
170
171 outputFormatObj *format = NULL;
172 if( strncasecmp(driver,"GD/",3) == 0 ) {
173 return msCreateDefaultOutputFormat( map, "AGG/PNG8", name );
174 }
175
176 if( strcasecmp(driver,"UTFGRID") == 0 ) {
177 if(!name) name="utfgrid";
178 format = msAllocOutputFormat( map, name, driver );
179 format->mimetype = msStrdup("application/json");
180 format->imagemode = MS_IMAGEMODE_RGB;
181 format->extension = msStrdup("json");
182 format->renderer = MS_RENDER_WITH_UTFGRID;
183 }
184
185 else if( strcasecmp(driver,"AGG/PNG") == 0 ) {
186 if(!name) name="png24";
187 format = msAllocOutputFormat( map, name, driver );
188 format->mimetype = msStrdup("image/png");
189 format->imagemode = MS_IMAGEMODE_RGB;
190 format->extension = msStrdup("png");
191 format->renderer = MS_RENDER_WITH_AGG;
192 }
193
194 else if( strcasecmp(driver,"AGG/PNG8") == 0 ) {
195 if(!name) name="png8";
196 format = msAllocOutputFormat( map, name, driver );
197 format->mimetype = msStrdup("image/png; mode=8bit");
198 format->imagemode = MS_IMAGEMODE_RGB;
199 format->extension = msStrdup("png");
200 format->renderer = MS_RENDER_WITH_AGG;
201 msSetOutputFormatOption( format, "QUANTIZE_FORCE", "on");
202 msSetOutputFormatOption( format, "QUANTIZE_COLORS", "256");
203 }
204
205 else if( strcasecmp(driver,"AGG/JPEG") == 0 ) {
206 if(!name) name="jpeg";
207 format = msAllocOutputFormat( map, name, driver );
208 format->mimetype = msStrdup("image/jpeg");
209 format->imagemode = MS_IMAGEMODE_RGB;
210 format->extension = msStrdup("jpg");
211 format->renderer = MS_RENDER_WITH_AGG;
212 }
213 #if defined(USE_PBF)
214 else if( strcasecmp(driver,"MVT") == 0 ) {
215 if(!name) name="mvt";
216 format = msAllocOutputFormat( map, name, driver );
217 format->mimetype = msStrdup("application/x-protobuf");
218 format->imagemode = MS_IMAGEMODE_FEATURE;
219 format->extension = msStrdup("pbf");
220 format->renderer = MS_RENDER_WITH_MVT;
221 }
222 #endif
223
224 else if( strcasecmp(driver,"AGG/MIXED") == 0 &&
225 name != NULL && strcasecmp(name,"jpegpng") == 0 ) {
226 format = msAllocOutputFormat( map, name, driver );
227 format->mimetype = msStrdup("image/vnd.jpeg-png");
228 format->imagemode = MS_IMAGEMODE_RGBA;
229 format->extension = msStrdup("XXX");
230 format->renderer = MS_RENDER_WITH_AGG;
231 msSetOutputFormatOption( format, "OPAQUE_FORMAT", "jpeg");
232 msSetOutputFormatOption( format, "TRANSPARENT_FORMAT", "png24");
233 }
234
235 else if( strcasecmp(driver,"AGG/MIXED") == 0 &&
236 name != NULL && strcasecmp(name,"jpegpng8") == 0 ) {
237 format = msAllocOutputFormat( map, name, driver );
238 format->mimetype = msStrdup("image/vnd.jpeg-png8");
239 format->imagemode = MS_IMAGEMODE_RGBA;
240 format->extension = msStrdup("XXX");
241 format->renderer = MS_RENDER_WITH_AGG;
242 msSetOutputFormatOption( format, "OPAQUE_FORMAT", "jpeg");
243 msSetOutputFormatOption( format, "TRANSPARENT_FORMAT", "png8");
244 }
245
246 else if( strcasecmp(driver,"AGG/MIXED") == 0 ) {
247 if(!name) name="mixed";
248 format = msAllocOutputFormat( map, name, driver );
249 format->mimetype = msStrdup("image/mixed");
250 format->imagemode = MS_IMAGEMODE_RGBA;
251 format->extension = msStrdup("XXX");
252 format->renderer = MS_RENDER_WITH_AGG;
253 }
254
255 #if defined(USE_CAIRO)
256 else if( strcasecmp(driver,"CAIRO/PNG") == 0 ) {
257 if(!name) name="cairopng";
258 format = msAllocOutputFormat( map, name, driver );
259 format->mimetype = msStrdup("image/png; mode=24bit");
260 format->imagemode = MS_IMAGEMODE_RGB;
261 format->extension = msStrdup("png");
262 format->renderer = MS_RENDER_WITH_CAIRO_RASTER;
263 }
264 else if( strcasecmp(driver,"CAIRO/JPEG") == 0 ) {
265 if(!name) name="cairojpeg";
266 format = msAllocOutputFormat( map, name, driver );
267 format->mimetype = msStrdup("image/jpeg");
268 format->imagemode = MS_IMAGEMODE_RGB;
269 format->extension = msStrdup("jpg");
270 format->renderer = MS_RENDER_WITH_CAIRO_RASTER;
271 }
272 else if( strcasecmp(driver,"CAIRO/PDF") == 0 ) {
273 if(!name) name="pdf";
274 format = msAllocOutputFormat( map, name, driver );
275 format->mimetype = msStrdup("application/x-pdf");
276 format->imagemode = MS_IMAGEMODE_RGB;
277 format->extension = msStrdup("pdf");
278 format->renderer = MS_RENDER_WITH_CAIRO_PDF;
279 }
280 else if( strcasecmp(driver,"CAIRO/SVG") == 0 ) {
281 if(!name) name="svg";
282 format = msAllocOutputFormat( map, name, driver );
283 format->mimetype = msStrdup("image/svg+xml");
284 format->imagemode = MS_IMAGEMODE_RGB;
285 format->extension = msStrdup("svg");
286 format->renderer = MS_RENDER_WITH_CAIRO_SVG;
287 }
288 #ifdef _WIN32
289 else if( strcasecmp(driver,"CAIRO/WINGDI") == 0 ) {
290 if(!name) name="cairowinGDI";
291 format = msAllocOutputFormat( map, name, driver );
292 format->mimetype = msStrdup("");
293 format->imagemode = MS_IMAGEMODE_RGB;
294 format->extension = msStrdup("");
295 format->renderer = MS_RENDER_WITH_CAIRO_RASTER;
296 }
297 else if( strcasecmp(driver,"CAIRO/WINGDIPRINT") == 0 ) {
298 if(!name) name="cairowinGDIPrint";
299 format = msAllocOutputFormat( map, name, driver );
300 format->mimetype = msStrdup("");
301 format->imagemode = MS_IMAGEMODE_RGB;
302 format->extension = msStrdup("");
303 format->renderer = MS_RENDER_WITH_CAIRO_RASTER;
304 }
305 #endif
306 #endif
307
308 #if defined(USE_OGL)
309 else if( strcasecmp(driver,"OGL/PNG") == 0 ) {
310 if(!name) name="oglpng24";
311 format = msAllocOutputFormat( map, name, driver );
312 format->mimetype = msStrdup("image/png; mode=24bit");
313 format->imagemode = MS_IMAGEMODE_RGB;
314 format->extension = msStrdup("png");
315 format->renderer = MS_RENDER_WITH_OGL;
316 }
317 #endif
318
319 #if defined(USE_KML)
320 else if( strcasecmp(driver,"KML") == 0 ) {
321 if(!name) name="kml";
322 format = msAllocOutputFormat( map, name, driver );
323 format->mimetype = msStrdup("application/vnd.google-earth.kml+xml");
324 format->imagemode = MS_IMAGEMODE_RGB;
325 format->extension = msStrdup("kml");
326 format->renderer = MS_RENDER_WITH_KML;
327 msSetOutputFormatOption( format, "ATTACHMENT", "mapserver.kml");
328 }
329 else if( strcasecmp(driver,"KMZ") == 0 ) {
330 if(!name) name="kmz";
331 format = msAllocOutputFormat( map, name, driver );
332 format->mimetype = msStrdup("application/vnd.google-earth.kmz");
333 format->imagemode = MS_IMAGEMODE_RGB;
334 format->extension = msStrdup("kmz");
335 format->renderer = MS_RENDER_WITH_KML;
336 msSetOutputFormatOption( format, "ATTACHMENT", "mapserver.kmz");
337 }
338 #endif
339
340
341
342 else if( strncasecmp(driver,"gdal/",5) == 0 ) {
343 if(!name) name=driver+5;
344 format = msAllocOutputFormat( map, name, driver );
345 if( msInitDefaultGDALOutputFormat( format ) == MS_FAILURE ) {
346 if( map != NULL ) {
347 map->numoutputformats--;
348 map->outputformatlist[map->numoutputformats] = NULL;
349 }
350
351 msFreeOutputFormat( format );
352 format = NULL;
353 }
354 }
355
356 else if( strncasecmp(driver,"ogr/",4) == 0 ) {
357 if(!name) name=driver+4;
358 format = msAllocOutputFormat( map, name, driver );
359 if( msInitDefaultOGROutputFormat( format ) == MS_FAILURE ) {
360 if( map != NULL ) {
361 map->numoutputformats--;
362 map->outputformatlist[map->numoutputformats] = NULL;
363 }
364
365 msFreeOutputFormat( format );
366 format = NULL;
367 }
368 }
369
370 else if( strcasecmp(driver,"imagemap") == 0 ) {
371 if(!name) name="imagemap";
372 format = msAllocOutputFormat( map, name, driver );
373 format->mimetype = msStrdup("text/html; driver=imagemap");
374 format->extension = msStrdup("html");
375 format->imagemode = MS_IMAGEMODE_NULL;
376 format->renderer = MS_RENDER_WITH_IMAGEMAP;
377 }
378
379 else if( strcasecmp(driver,"template") == 0 ) {
380 if(!name) name="template";
381 format = msAllocOutputFormat( map, name, driver );
382 format->mimetype = msStrdup("text/html");
383 format->extension = msStrdup("html");
384 format->imagemode = MS_IMAGEMODE_FEATURE;
385 format->renderer = MS_RENDER_WITH_TEMPLATE;
386 }
387
388 if( format != NULL )
389 format->inmapfile = MS_FALSE;
390
391
392 return format;
393 }
394
395 /************************************************************************/
396 /* msApplyDefaultOutputFormats() */
397 /************************************************************************/
398
399
msApplyDefaultOutputFormats(mapObj * map)400 void msApplyDefaultOutputFormats( mapObj *map )
401 {
402 char *saved_imagetype;
403 struct defaultOutputFormatEntry *defEntry;
404
405 if( map->imagetype == NULL )
406 saved_imagetype = NULL;
407 else
408 saved_imagetype = msStrdup(map->imagetype);
409
410 defEntry = defaultoutputformats;
411 while(defEntry->name) {
412 if( msSelectOutputFormat( map, defEntry->name ) == NULL )
413 msCreateDefaultOutputFormat( map, defEntry->driver, defEntry->name );
414 defEntry++;
415 }
416 if( map->imagetype != NULL )
417 free( map->imagetype );
418 map->imagetype = saved_imagetype;
419 }
420
421 /************************************************************************/
422 /* msFreeOutputFormat() */
423 /************************************************************************/
424
msFreeOutputFormat(outputFormatObj * format)425 void msFreeOutputFormat( outputFormatObj *format )
426
427 {
428 if( format == NULL )
429 return;
430 if( MS_REFCNT_DECR_IS_NOT_ZERO(format) ) {
431 return;
432 }
433
434 if(MS_RENDERER_PLUGIN(format) && format->vtable) {
435 format->vtable->cleanup(MS_RENDERER_CACHE(format->vtable));
436 free( format->vtable );
437 }
438 msFree( format->name );
439 msFree( format->mimetype );
440 msFree( format->driver );
441 msFree( format->extension );
442 msFreeCharArray( format->formatoptions, format->numformatoptions );
443 msFree( format );
444 }
445
446 /************************************************************************/
447 /* msAllocOutputFormat() */
448 /************************************************************************/
449
msAllocOutputFormat(mapObj * map,const char * name,const char * driver)450 static outputFormatObj *msAllocOutputFormat( mapObj *map, const char *name,
451 const char *driver )
452
453 {
454 outputFormatObj *format;
455
456 /* -------------------------------------------------------------------- */
457 /* Allocate the format object. */
458 /* -------------------------------------------------------------------- */
459 format = (outputFormatObj *) calloc(1,sizeof(outputFormatObj));
460 if( format == NULL ) {
461 msSetError( MS_MEMERR, NULL, "msAllocOutputFormat()" );
462 return NULL;
463 }
464
465 /* -------------------------------------------------------------------- */
466 /* Initialize various fields. */
467 /* -------------------------------------------------------------------- */
468 format->bands = 1;
469 format->name = msStrdup(name);
470 format->driver = msStrdup(driver);
471 format->refcount = 0;
472 format->vtable = NULL;
473 format->device = NULL;
474 format->imagemode = MS_IMAGEMODE_RGB;
475
476 /* -------------------------------------------------------------------- */
477 /* Attach to map. */
478 /* -------------------------------------------------------------------- */
479 if( map != NULL ) {
480 map->numoutputformats++;
481 if( map->outputformatlist == NULL )
482 map->outputformatlist = (outputFormatObj **) malloc(sizeof(void*));
483 else
484 map->outputformatlist = (outputFormatObj **)
485 realloc(map->outputformatlist,
486 sizeof(void*) * map->numoutputformats );
487
488 map->outputformatlist[map->numoutputformats-1] = format;
489 format->refcount++;
490 }
491
492 return format;
493 }
494
495 /************************************************************************/
496 /* msAppendOutputFormat() */
497 /* */
498 /* Add an output format . */
499 /* http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=511 */
500 /************************************************************************/
msAppendOutputFormat(mapObj * map,outputFormatObj * format)501 int msAppendOutputFormat(mapObj *map, outputFormatObj *format)
502 {
503 /* -------------------------------------------------------------------- */
504 /* Attach to map. */
505 /* -------------------------------------------------------------------- */
506 assert(map);
507 map->numoutputformats++;
508 if (map->outputformatlist == NULL)
509 map->outputformatlist = (outputFormatObj **) malloc(sizeof(void*));
510 else
511 map->outputformatlist = (outputFormatObj **)
512 realloc(map->outputformatlist,
513 sizeof(void*) * map->numoutputformats );
514
515 map->outputformatlist[map->numoutputformats-1] = format;
516 MS_REFCNT_INCR(format);
517
518 return map->numoutputformats;
519 }
520
521 /************************************************************************/
522 /* msRemoveOutputFormat() */
523 /* */
524 /* Remove an output format (by name). */
525 /* http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=511 */
526 /************************************************************************/
msRemoveOutputFormat(mapObj * map,const char * name)527 int msRemoveOutputFormat(mapObj *map, const char *name)
528 {
529 int i, j;
530 /* -------------------------------------------------------------------- */
531 /* Detach from map. */
532 /* -------------------------------------------------------------------- */
533 if (map != NULL) {
534 if (map->outputformatlist == NULL) {
535 msSetError(MS_CHILDERR, "Can't remove format from empty outputformatlist", "msRemoveOutputFormat()");
536 return MS_FAILURE;
537 } else {
538 i = msGetOutputFormatIndex(map, name);
539 if (i >= 0) {
540 map->numoutputformats--;
541 if(MS_REFCNT_DECR_IS_ZERO(map->outputformatlist[i]))
542 msFreeOutputFormat( map->outputformatlist[i] );
543
544 for (j=i; j<map->numoutputformats-1; j++) {
545 map->outputformatlist[j] = map->outputformatlist[j+1];
546 }
547 }
548 map->outputformatlist = (outputFormatObj **)
549 realloc(map->outputformatlist,
550 sizeof(void*) * (map->numoutputformats) );
551 return MS_SUCCESS;
552 }
553 }
554 return MS_FAILURE;
555 }
556
557 /************************************************************************/
558 /* msGetOutputFormatIndex() */
559 /* */
560 /* Pulled this out of msSelectOutputFormat for use in other cases. */
561 /************************************************************************/
562
msGetOutputFormatIndex(mapObj * map,const char * imagetype)563 int msGetOutputFormatIndex(mapObj *map, const char *imagetype)
564 {
565 int i;
566 /* -------------------------------------------------------------------- */
567 /* Try to find the format in the maps list of formats, first by */
568 /* mime type, and then by output format name. */
569 /* -------------------------------------------------------------------- */
570 for (i = 0; i < map->numoutputformats; i++) {
571 if (map->outputformatlist[i]->mimetype != NULL
572 && strcasecmp(imagetype,
573 map->outputformatlist[i]->mimetype) == 0 )
574 return i;
575 }
576
577 for( i = 0; i < map->numoutputformats; i++ ) {
578 if( strcasecmp(imagetype,map->outputformatlist[i]->name) == 0 )
579 return i;
580 }
581
582 return -1;
583 }
584
585 /************************************************************************/
586 /* msSelectOutputFormat() */
587 /************************************************************************/
588
589
590
msSelectOutputFormat(mapObj * map,const char * imagetype)591 outputFormatObj *msSelectOutputFormat( mapObj *map,
592 const char *imagetype )
593
594 {
595 int index;
596 outputFormatObj *format = NULL;
597
598 if( map == NULL || imagetype == NULL || strlen(imagetype) == 0 )
599 return NULL;
600
601 /* -------------------------------------------------------------------- */
602 /* Try to find the format in the maps list of formats, first by */
603 /* mime type, and then by output format name. */
604 /* -------------------------------------------------------------------- */
605 index = msGetOutputFormatIndex(map, imagetype);
606 if (index >= 0) {
607 format = map->outputformatlist[index];
608 } else {
609 struct defaultOutputFormatEntry *formatEntry = defaultoutputformats;
610 while(formatEntry->name) {
611 if(!strcasecmp(imagetype,formatEntry->name) || !strcasecmp(imagetype,formatEntry->mimetype)) {
612 format = msCreateDefaultOutputFormat( map, formatEntry->driver, formatEntry->name );
613 break;
614 }
615 formatEntry++;
616 }
617
618 }
619
620 if (format) {
621 if (map->imagetype)
622 free(map->imagetype);
623 map->imagetype = msStrdup(format->name);
624 }
625
626 if( format != NULL )
627 msOutputFormatValidate( format, MS_FALSE );
628
629 return format;
630 }
631
632 /************************************************************************/
633 /* msApplyOutputFormat() */
634 /************************************************************************/
635
msApplyOutputFormat(outputFormatObj ** target,outputFormatObj * format,int transparent,int interlaced,int imagequality)636 void msApplyOutputFormat( outputFormatObj **target,
637 outputFormatObj *format,
638 int transparent,
639 int interlaced,
640 int imagequality )
641
642 {
643 int change_needed = MS_FALSE;
644 int old_imagequality, old_interlaced;
645 outputFormatObj *formatToFree = NULL;
646
647 assert( target != NULL );
648
649 if( *target != NULL && MS_REFCNT_DECR_IS_ZERO( (*target) ) ) {
650 formatToFree = *target;
651 *target = NULL;
652 }
653
654 if( format == NULL ) {
655 if( formatToFree )
656 msFreeOutputFormat( formatToFree );
657 *target = NULL;
658 return;
659 }
660
661 msOutputFormatValidate( format, MS_FALSE );
662
663 /* -------------------------------------------------------------------- */
664 /* Do we need to change any values? If not, then just apply */
665 /* and return. */
666 /* -------------------------------------------------------------------- */
667 if( transparent != MS_NOOVERRIDE && !format->transparent != !transparent )
668 change_needed = MS_TRUE;
669
670 old_imagequality = atoi(msGetOutputFormatOption( format, "QUALITY", "75"));
671 if( imagequality != MS_NOOVERRIDE && old_imagequality != imagequality )
672 change_needed = MS_TRUE;
673
674 old_interlaced =
675 strcasecmp(msGetOutputFormatOption( format, "INTERLACE", "ON"),
676 "OFF") != 0;
677 if( interlaced != MS_NOOVERRIDE && !interlaced != !old_interlaced )
678 change_needed = MS_TRUE;
679
680 if( change_needed ) {
681 char new_value[128];
682
683 if( format->refcount > 0 )
684 format = msCloneOutputFormat( format );
685
686 if( transparent != MS_NOOVERRIDE ) {
687 format->transparent = transparent;
688 if( format->imagemode == MS_IMAGEMODE_RGB )
689 format->imagemode = MS_IMAGEMODE_RGBA;
690 }
691
692 if( imagequality != MS_NOOVERRIDE && imagequality != old_imagequality ) {
693 snprintf( new_value, sizeof(new_value), "%d", imagequality );
694 msSetOutputFormatOption( format, "QUALITY", new_value );
695 }
696
697 if( interlaced != MS_NOOVERRIDE && !interlaced != !old_interlaced ) {
698 if( interlaced )
699 msSetOutputFormatOption( format, "INTERLACE", "ON" );
700 else
701 msSetOutputFormatOption( format, "INTERLACE", "OFF" );
702 }
703 }
704
705 *target = format;
706 format->refcount++;
707 if( MS_RENDERER_PLUGIN(format) ) {
708 msInitializeRendererVTable(format);
709 }
710
711
712 if( formatToFree )
713 msFreeOutputFormat( formatToFree );
714 }
715
716 /************************************************************************/
717 /* msCloneOutputFormat() */
718 /************************************************************************/
719
msCloneOutputFormat(outputFormatObj * src)720 outputFormatObj *msCloneOutputFormat( outputFormatObj *src )
721
722 {
723 outputFormatObj *dst;
724 int i;
725
726 dst = msAllocOutputFormat( NULL, src->name, src->driver );
727
728 msFree( dst->mimetype );
729 if( src->mimetype )
730 dst->mimetype = msStrdup( src->mimetype );
731 else
732 dst->mimetype = NULL;
733
734 msFree( dst->extension );
735 if( src->extension )
736 dst->extension = msStrdup( src->extension );
737 else
738 dst->extension = NULL;
739
740 dst->imagemode = src->imagemode;
741 dst->renderer = src->renderer;
742
743 dst->transparent = src->transparent;
744 dst->bands = src->bands;
745
746 dst->numformatoptions = src->numformatoptions;
747 dst->formatoptions = (char **)
748 malloc(sizeof(char *) * src->numformatoptions );
749
750 for( i = 0; i < src->numformatoptions; i++ )
751 dst->formatoptions[i] = msStrdup(src->formatoptions[i]);
752
753 dst->inmapfile = src->inmapfile;
754
755 return dst;
756 }
757
758 /************************************************************************/
759 /* msGetOutputFormatOption() */
760 /* */
761 /* Fetch the value of a particular option. It is assumed the */
762 /* options are in "KEY=VALUE" format. */
763 /************************************************************************/
764
msGetOutputFormatOption(outputFormatObj * format,const char * optionkey,const char * defaultresult)765 const char *msGetOutputFormatOption( outputFormatObj *format,
766 const char *optionkey,
767 const char *defaultresult )
768
769 {
770 int i, len = strlen(optionkey);
771
772 for( i = 0; i < format->numformatoptions; i++ ) {
773 if( strncasecmp(format->formatoptions[i],optionkey,len) == 0
774 && format->formatoptions[i][len] == '=' )
775 return format->formatoptions[i] + len + 1;
776 }
777
778 return defaultresult;
779 }
780
781 /************************************************************************/
782 /* msSetOutputFormatOption() */
783 /************************************************************************/
784
msSetOutputFormatOption(outputFormatObj * format,const char * key,const char * value)785 void msSetOutputFormatOption( outputFormatObj *format,
786 const char *key, const char *value )
787
788 {
789 char *newline;
790 int i, len;
791
792 if( value == NULL )
793 return;
794
795 /* -------------------------------------------------------------------- */
796 /* Format the name=value pair into a newly allocated string. */
797 /* -------------------------------------------------------------------- */
798 newline = (char *) malloc(strlen(key)+strlen(value)+2);
799 if( newline == NULL ) {
800 assert( newline != NULL );
801 return;
802 }
803
804 sprintf( newline, "%s=%s", key, value );
805
806 /* -------------------------------------------------------------------- */
807 /* Does this key already occur? If so replace it. */
808 /* -------------------------------------------------------------------- */
809 len = strlen(key);
810
811 for( i = 0; i < format->numformatoptions; i++ ) {
812 if( strncasecmp(format->formatoptions[i],key,len) == 0
813 && format->formatoptions[i][len] == '=' ) {
814 free( format->formatoptions[i] );
815 format->formatoptions[i] = newline;
816 return;
817 }
818 }
819
820 /* -------------------------------------------------------------------- */
821 /* otherwise, we need to grow the list. */
822 /* -------------------------------------------------------------------- */
823 format->numformatoptions++;
824 format->formatoptions = (char **)
825 realloc( format->formatoptions,
826 sizeof(char*) * format->numformatoptions );
827
828 format->formatoptions[format->numformatoptions-1] = newline;
829
830 /* -------------------------------------------------------------------- */
831 /* Capture generic value(s) we are interested in. */
832 /* -------------------------------------------------------------------- */
833 if( strcasecmp(key,"BAND_COUNT") == 0 )
834 format->bands = atoi(value);
835 }
836
837 /************************************************************************/
838 /* msGetOutputFormatMimeList() */
839 /************************************************************************/
840
msGetOutputFormatMimeList(mapObj * map,char ** mime_list,int max_mime)841 void msGetOutputFormatMimeList( mapObj *map, char **mime_list, int max_mime )
842
843 {
844 int mime_count = 0, i;
845
846 msApplyDefaultOutputFormats(map);
847 for( i = 0; i < map->numoutputformats && mime_count < max_mime; i++ ) {
848 int j;
849
850 if( map->outputformatlist[i]->mimetype == NULL )
851 continue;
852
853 for( j = 0; j < mime_count; j++ ) {
854 if( strcasecmp(mime_list[j],
855 map->outputformatlist[i]->mimetype) == 0 )
856 break;
857 }
858
859 if( j == mime_count )
860 mime_list[mime_count++] = map->outputformatlist[i]->mimetype;
861 }
862
863 if( mime_count < max_mime )
864 mime_list[mime_count] = NULL;
865 }
866
867 /************************************************************************/
868 /* msGetOutputFormatMimeList() */
869 /************************************************************************/
msGetOutputFormatMimeListImg(mapObj * map,char ** mime_list,int max_mime)870 void msGetOutputFormatMimeListImg( mapObj *map, char **mime_list, int max_mime )
871
872 {
873 int mime_count = 0, i,j;
874 const char *format_list = NULL;
875 char **tokens = NULL;
876 int numtokens = 0;
877 outputFormatObj *format;
878
879 msApplyDefaultOutputFormats(map);
880 format_list = msOWSLookupMetadata(&(map->web.metadata), "M","getlegendgraphic_formatlist");
881 if ( format_list && strlen(format_list) > 0)
882 tokens = msStringSplit(format_list, ',', &numtokens);
883
884 if (tokens && numtokens > 0) {
885 for(j = 0; j < numtokens; j++ ) {
886 format = msSelectOutputFormat(map, tokens[j]);
887 if (format != NULL) {
888 mime_list[mime_count++] = format->mimetype;
889 }
890 }
891 } else
892 for( i = 0; i < map->numoutputformats && mime_count < max_mime; i++ ) {
893 int j;
894
895 if( map->outputformatlist[i]->mimetype == NULL )
896 continue;
897
898 for( j = 0; j < mime_count; j++ ) {
899 if( strcasecmp(mime_list[j],
900 map->outputformatlist[i]->mimetype) == 0 )
901 break;
902 }
903
904 if( j == mime_count && map->outputformatlist[i]->driver &&
905 strncasecmp(map->outputformatlist[i]->driver, "AGG/", 4)==0)
906 mime_list[mime_count++] = map->outputformatlist[i]->mimetype;
907 }
908
909 if( mime_count < max_mime )
910 mime_list[mime_count] = NULL;
911 if(tokens)
912 msFreeCharArray(tokens, numtokens);
913 }
914
915 /************************************************************************/
916 /* msGetOutputFormatMimeListWMS() */
917 /************************************************************************/
918
msGetOutputFormatMimeListWMS(mapObj * map,char ** mime_list,int max_mime)919 void msGetOutputFormatMimeListWMS( mapObj *map, char **mime_list, int max_mime )
920 {
921 int mime_count = 0, i,j;
922 const char *format_list = NULL;
923 char **tokens = NULL;
924 int numtokens = 0;
925 outputFormatObj *format;
926 msApplyDefaultOutputFormats(map);
927 format_list = msOWSLookupMetadata(&(map->web.metadata), "M","getmap_formatlist");
928 if ( format_list && strlen(format_list) > 0)
929 tokens = msStringSplit(format_list, ',', &numtokens);
930
931 if (tokens && numtokens > 0) {
932 for(j = 0; j < numtokens; j++ ) {
933 format = msSelectOutputFormat(map, tokens[j]);
934 if (format != NULL) {
935 mime_list[mime_count++] = format->mimetype;
936 }
937 }
938 } else {
939 for( i = 0; i < map->numoutputformats && mime_count < max_mime; i++ ) {
940 int j;
941
942 if( map->outputformatlist[i]->mimetype == NULL )
943 continue;
944
945 for( j = 0; j < mime_count; j++ ) {
946 if( strcasecmp(mime_list[j],
947 map->outputformatlist[i]->mimetype) == 0 )
948 break;
949 }
950
951 if( j == mime_count && map->outputformatlist[i]->driver &&
952 (
953 strncasecmp(map->outputformatlist[i]->driver, "GDAL/", 5)==0 ||
954 strncasecmp(map->outputformatlist[i]->driver, "AGG/", 4)==0 ||
955 strcasecmp(map->outputformatlist[i]->driver, "CAIRO/SVG")==0 ||
956 strcasecmp(map->outputformatlist[i]->driver, "CAIRO/PDF")==0 ||
957 strcasecmp(map->outputformatlist[i]->driver, "kml")==0 ||
958 strcasecmp(map->outputformatlist[i]->driver, "kmz")==0 ||
959 strcasecmp(map->outputformatlist[i]->driver, "mvt")==0 ||
960 strcasecmp(map->outputformatlist[i]->driver, "UTFGRID")==0))
961 mime_list[mime_count++] = map->outputformatlist[i]->mimetype;
962 }
963 }
964 if( mime_count < max_mime )
965 mime_list[mime_count] = NULL;
966 if(tokens)
967 msFreeCharArray(tokens, numtokens);
968 }
969
970
971 /************************************************************************/
972 /* msOutputFormatValidate() */
973 /* */
974 /* Do some validation of the output format, and report to debug */
975 /* output if it doesn't seem valid. Fixup in place as best as */
976 /* possible. */
977 /************************************************************************/
978
msOutputFormatValidate(outputFormatObj * format,int issue_error)979 int msOutputFormatValidate( outputFormatObj *format, int issue_error )
980
981 {
982 int result = MS_TRUE;
983 char *driver_ext;
984
985 format->bands = atoi(msGetOutputFormatOption( format, "BAND_COUNT", "1" ));
986
987 /* Enforce the requirement that JPEG be RGB and TRANSPARENT=OFF */
988 driver_ext = strstr(format->driver,"/");
989 if( driver_ext && ++driver_ext && !strcasecmp(driver_ext,"JPEG")) {
990 if( format->transparent ) {
991 if( issue_error )
992 msSetError( MS_MISCERR,
993 "JPEG OUTPUTFORMAT %s has TRANSPARENT set ON, but this is not supported.\n"
994 "It has been disabled.\n",
995 "msOutputFormatValidate()", format->name );
996 else
997 msDebug( "JPEG OUTPUTFORMAT %s has TRANSPARENT set ON, but this is not supported.\n"
998 "It has been disabled.\n",
999 format->name );
1000
1001 format->transparent = MS_FALSE;
1002 result = MS_FALSE;
1003 }
1004 if( format->imagemode == MS_IMAGEMODE_RGBA ) {
1005 if( issue_error )
1006 msSetError( MS_MISCERR,
1007 "JPEG OUTPUTFORMAT %s has IMAGEMODE RGBA, but this is not supported.\n"
1008 "IMAGEMODE forced to RGB.\n",
1009 "msOutputFormatValidate()", format->name );
1010 else
1011 msDebug( "JPEG OUTPUTFORMAT %s has IMAGEMODE RGBA, but this is not supported.\n"
1012 "IMAGEMODE forced to RGB.\n",
1013 format->name );
1014
1015 format->imagemode = MS_IMAGEMODE_RGB;
1016 result = MS_FALSE;
1017 }
1018 }
1019
1020 if( format->transparent && format->imagemode == MS_IMAGEMODE_RGB ) {
1021 if( issue_error )
1022 msSetError( MS_MISCERR,
1023 "OUTPUTFORMAT %s has TRANSPARENT set ON, but an IMAGEMODE\n"
1024 "of RGB instead of RGBA. Changing imagemode to RGBA.\n",
1025 "msOutputFormatValidate()", format->name );
1026 else
1027 msDebug("OUTPUTFORMAT %s has TRANSPARENT set ON, but an IMAGEMODE\n"
1028 "of RGB instead of RGBA. Changing imagemode to RGBA.\n",
1029 format->name );
1030
1031 format->imagemode = MS_IMAGEMODE_RGBA;
1032 result = MS_FALSE;
1033 }
1034
1035 /* Special checking around RAWMODE image modes. */
1036 if( format->imagemode == MS_IMAGEMODE_INT16
1037 || format->imagemode == MS_IMAGEMODE_FLOAT32
1038 || format->imagemode == MS_IMAGEMODE_BYTE ) {
1039 if( strncmp(format->driver,"GDAL/",5) != 0 ) {
1040 result = MS_FALSE;
1041 if( issue_error )
1042 msSetError( MS_MISCERR,
1043 "OUTPUTFORMAT %s has IMAGEMODE BYTE/INT16/FLOAT32, but this is only supported for GDAL drivers.",
1044 "msOutputFormatValidate()", format->name );
1045 else
1046 msDebug( "OUTPUTFORMAT %s has IMAGEMODE BYTE/INT16/FLOAT32, but this is only supported for GDAL drivers.",
1047 format->name );
1048 }
1049
1050 if( format->renderer != MS_RENDER_WITH_RAWDATA ) /* see bug 724 */
1051 format->renderer = MS_RENDER_WITH_RAWDATA;
1052 }
1053
1054 if( !strcasecmp(format->driver,"AGG/MIXED") )
1055 {
1056 if( !msGetOutputFormatOption(format, "TRANSPARENT_FORMAT", NULL) )
1057 {
1058 result = MS_FALSE;
1059 if( issue_error )
1060 msSetError( MS_MISCERR,
1061 "OUTPUTFORMAT %s lacks a 'TRANSPARENT_FORMAT' FORMATOPTION.",
1062 "msOutputFormatValidate()", format->name );
1063 else
1064 msDebug( "OUTPUTFORMAT %s lacks a 'TRANSPARENT_FORMAT' FORMATOPTION.",
1065 format->name );
1066 }
1067 if( !msGetOutputFormatOption(format, "OPAQUE_FORMAT", NULL) )
1068 {
1069 result = MS_FALSE;
1070 if( issue_error )
1071 msSetError( MS_MISCERR,
1072 "OUTPUTFORMAT %s lacks a 'OPAQUE_FORMAT' FORMATOPTION.",
1073 "msOutputFormatValidate()", format->name );
1074 else
1075 msDebug( "OUTPUTFORMAT %s lacks a 'OPAQUE_FORMAT' FORMATOPTION.",
1076 format->name );
1077 }
1078 }
1079
1080 return result;
1081 }
1082
1083 /************************************************************************/
1084 /* msInitializeRendererVTable() */
1085 /************************************************************************/
1086
msInitializeRendererVTable(outputFormatObj * format)1087 int msInitializeRendererVTable(outputFormatObj *format)
1088 {
1089 assert(format);
1090 if(format->vtable) {
1091 return MS_SUCCESS;
1092 }
1093 format->vtable = (rendererVTableObj*)calloc(1,sizeof(rendererVTableObj));
1094
1095 msInitializeDummyRenderer(format->vtable);
1096
1097 switch(format->renderer) {
1098 case MS_RENDER_WITH_AGG:
1099 return msPopulateRendererVTableAGG(format->vtable);
1100 case MS_RENDER_WITH_UTFGRID:
1101 return msPopulateRendererVTableUTFGrid(format->vtable);
1102 #ifdef USE_PBF
1103 case MS_RENDER_WITH_MVT:
1104 return msPopulateRendererVTableMVT(format->vtable);
1105 #endif
1106 #ifdef USE_CAIRO
1107 case MS_RENDER_WITH_CAIRO_RASTER:
1108 return msPopulateRendererVTableCairoRaster(format->vtable);
1109 case MS_RENDER_WITH_CAIRO_PDF:
1110 return msPopulateRendererVTableCairoPDF(format->vtable);
1111 case MS_RENDER_WITH_CAIRO_SVG:
1112 return msPopulateRendererVTableCairoSVG(format->vtable);
1113 #endif
1114 #ifdef USE_OGL
1115 case MS_RENDER_WITH_OGL:
1116 return msPopulateRendererVTableOGL(format->vtable);
1117 #endif
1118 #ifdef USE_KML
1119 case MS_RENDER_WITH_KML:
1120 return msPopulateRendererVTableKML(format->vtable);
1121 #endif
1122
1123 case MS_RENDER_WITH_OGR:
1124 return msPopulateRendererVTableOGR(format->vtable);
1125
1126 default:
1127 msSetError(MS_MISCERR, "unsupported RendererVtable renderer %d",
1128 "msInitializeRendererVTable()",format->renderer);
1129 return MS_FAILURE;
1130 }
1131 /* this code should never be executed */
1132 return MS_FAILURE;
1133 }
1134
1135 /************************************************************************/
1136 /* msOutputFormatResolveFromImage() */
1137 /************************************************************************/
1138
msOutputFormatResolveFromImage(mapObj * map,imageObj * img)1139 void msOutputFormatResolveFromImage( mapObj *map, imageObj* img )
1140 {
1141 outputFormatObj* format = map->outputformat;
1142 assert( img->format == format );
1143 assert( img->format->refcount >= 2 );
1144
1145 if( format->renderer == MS_RENDER_WITH_AGG &&
1146 strcmp(format->driver, "AGG/MIXED") == 0 &&
1147 (format->imagemode == MS_IMAGEMODE_RGB ||
1148 format->imagemode == MS_IMAGEMODE_RGBA) )
1149 {
1150 outputFormatObj * new_format;
1151 int has_non_opaque_pixels = MS_FALSE;
1152 const char* underlying_driver_type = NULL;
1153 const char* underlying_driver_name = NULL;
1154
1155 // Check if the image has non opaque pixels
1156 if( format->imagemode == MS_IMAGEMODE_RGBA )
1157 {
1158 rasterBufferObj rb;
1159 int ret;
1160
1161 ret = format->vtable->getRasterBufferHandle(img,&rb);
1162 assert( ret == MS_SUCCESS );
1163 (void)ret;
1164 if( rb.data.rgba.a )
1165 {
1166 int row;
1167 for(row=0; row<rb.height && !has_non_opaque_pixels; row++) {
1168 int col;
1169 unsigned char *a;
1170 a=rb.data.rgba.a+row*rb.data.rgba.row_step;
1171 for(col=0; col<rb.width && !has_non_opaque_pixels; col++) {
1172 if(*a < 255) {
1173 has_non_opaque_pixels = MS_TRUE;
1174 }
1175 a+=rb.data.rgba.pixel_step;
1176 }
1177 }
1178 }
1179 }
1180
1181 underlying_driver_type = ( has_non_opaque_pixels ) ?
1182 "TRANSPARENT_FORMAT" : "OPAQUE_FORMAT";
1183 underlying_driver_name = msGetOutputFormatOption(format, underlying_driver_type,
1184 NULL);
1185 if( underlying_driver_name == NULL ) {
1186 msSetError(MS_MISCERR,
1187 "Missing %s format option on %s.",
1188 "msOutputFormatResolveFromImage()",
1189 underlying_driver_type, format->name );
1190 return;
1191 }
1192 new_format = msSelectOutputFormat( map, underlying_driver_name );
1193 if( new_format == NULL ) {
1194 msSetError(MS_MISCERR,
1195 "Cannot find %s output format.",
1196 "msOutputFormatResolveFromImage()",
1197 underlying_driver_name );
1198 return;
1199 }
1200 if( new_format->renderer != MS_RENDER_WITH_AGG )
1201 {
1202 msSetError(MS_MISCERR,
1203 "%s cannot be used as the %s format of %s since it is not AGG based.",
1204 "msOutputFormatResolveFromImage()",
1205 underlying_driver_name, underlying_driver_type, format->name );
1206 return;
1207 }
1208
1209 msApplyOutputFormat( &(map->outputformat),
1210 new_format,
1211 has_non_opaque_pixels,
1212 MS_NOOVERRIDE,
1213 MS_NOOVERRIDE );
1214
1215 msFreeOutputFormat( format );
1216 img->format = map->outputformat;
1217 img->format->refcount ++;
1218 }
1219 }
1220