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