[Mapserver-users] Sending images with mod_perl (solution #1)

Mark Cave-Ayland m.cave-ayland@webbased.co.uk
Sun, 25 May 2003 18:42:38 +0100


This is a multi-part message in MIME format.

------=_NextPart_000_0000_01C322ED.6F0D6C10
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit

Hi everyone,

After a couple of weeks of looking at this problem 'out of hours', I've
eventually come up with a solution that allows perl mapscript to run
under mod_perl and output images directly to the browser without having
to write a temporary file to disk first.

Having looked at the various issues involved, I decided against
redirecting file handles as in Jason's patches, as it seemed some kind
of mystical voodoo occurs when Apache redirects stdout into a socket
with things coming out in the wrong order or not at all. So I decided to
approach it from the angle of getting the image in a variable so that
mod_perl can output it directly.

Looking at mapscript.i, it looked as if someone had already written
something similar; a method called getImageToVar() which worked under
Tcl to set a new variable with the contents of the rendered image. So I
set about adding in equivalent perl methods to achieve the same effect.
However, setting named variables in perl appeared to create them in a
global scope (which is not good for mod_perl) which is why my solution
returns a new scalar from the method.

Please find attached the patch against mapserver 3.6.5 for
comment/testing. I'm afraid the patch removes the existing Tcl function
because I've never used Tcl before so testing it would be hard; however
to add it back in should just be a case of adding a few lines to create
a new typemap.

Once the attached patch has been applied, the mapscript_wrap.c and
mapscript.pm modules need to be recreated with SWiG; I used SWiG 1.3.19
which gave a lot of warnings but the resulting wrappers worked fine. The
modified getImageToVar() method can then be used like this:


#!/usr/bin/perl


# Output the content-type header
use CGI ':cgi';
print header(  # note the use of the filehandle here...
        -type => 'image/png',
);


# The actual mapscript itself goes here
$ENV{MS_ERRORFILE} = "/var/www/perl/mserr.log";

use mapscript;

# Create a new map
my $map = new mapscript::mapObj('/var/www/perl/mstest.map');

# Draw the map
my $mapimg = $map->draw();

# Get the resulting image as a scalar
my $rawimg;
$rawimg = $map->getImageToVar($mapimg);

# Send it to the browser
print $rawimg;


While this may not be the solution originally envisaged, it appears to
be working well here on a development system. Feel free to stress it,
test it, benchmark it and find any memory leaks - but remember this code
is experimental ;)


Cheers,

Mark.

---

Mark Cave-Ayland
Webbased Ltd.
Tamar Science Park
Derriford
Plymouth
PL6 8BX
England

Tel: +44 (0)1752 764445
Fax: +44 (0)1752 764446


This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender. You
should not copy it or use it for any purpose nor disclose or distribute
its contents to any other person.

------=_NextPart_000_0000_01C322ED.6F0D6C10
Content-Type: application/octet-stream;
	name="mapserver-3.6.5-getimage.patch"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="mapserver-3.6.5-getimage.patch"

--- mapserver-3.6.5.vanilla/map.h	Tue Mar 18 16:04:26 2003=0A=
+++ mapserver-3.6.5/map.h	Sun May 25 12:48:17 2003=0A=
@@ -726,6 +726,11 @@=0A=
   webObj web;=0A=
 =0A=
   int *layerorder;=0A=
+=0A=
+#ifndef SWIG=0A=
+	char *imagedata;	/* output image rendered by gd library */=0A=
+	int imagelen;=0A=
+#endif=0A=
    =0A=
 } mapObj;=0A=
 =0A=
--- mapserver-3.6.5.vanilla/mapfile.c	Sat Feb  8 17:16:23 2003=0A=
+++ mapserver-3.6.5/mapfile.c	Sun May 25 12:49:36 2003=0A=
@@ -3000,6 +3004,8 @@=0A=
 =0A=
   map->transparent =3D MS_OFF;=0A=
   map->interlace =3D MS_ON;=0A=
+	map->imagedata =3D NULL;=0A=
+	map->imagelen =3D 0;=0A=
 =0A=
   map->labelcache.labels =3D (labelCacheMemberObj =
*)malloc(sizeof(labelCacheMemberObj)*MS_LABELCACHEINITSIZE);=0A=
   if(map->labelcache.labels =3D=3D NULL) {=0A=
@@ -3094,6 +3100,9 @@=0A=
   if (map->layerorder)=0A=
       free(map->layerorder);=0A=
 =0A=
+	if (map->imagedata)=0A=
+		free(map->imagedata);=0A=
+=0A=
   msFree(map);=0A=
 }=0A=
 =0A=
--- mapserver-3.6.5.vanilla/mapscript/mapscript.i	Mon Jul  8 18:28:16 =
2002=0A=
+++ mapserver-3.6.5/mapscript/mapscript.i	Sun May 25 12:51:47 2003=0A=
@@ -237,92 +237,95 @@=0A=
     return msDrawLabelCache(image->bytes, self);=0A=
   }=0A=
 =0A=
-  int getImageToVar(imageObj *image, char *varname) {=0A=
-    // set a scripting language variable by name with image data=0A=
-    int size =3D 0;=0A=
-    unsigned char *imgbytes;=0A=
-=0A=
-    // Tcl implementation to define needed variables, initialization=0A=
-    #ifdef SWIGTCL8=0A=
-      Tcl_Obj *imgobj;=0A=
-      int flags =3D TCL_LEAVE_ERR_MSG;=0A=
-      /* no other initialization needed */=0A=
-    #endif=0A=
-=0A=
-    // Perl implementation to define needed variables, initialization=0A=
-    #ifdef SWIGPERL=0A=
-    #endif=0A=
-=0A=
-    // Python implementation to define needed variables, initialization=0A=
-    #ifdef SWIGPYTHON=0A=
-    #endif=0A=
-=0A=
-    // generic code to get imgbytes, size=0A=
-    switch (self->imagetype) {=0A=
-      case(MS_GIF):=0A=
-        #ifdef USE_GD_GIF=0A=
-          // GD /w gif doesn't have gdImageGifPtr()=0A=
-          msSetError(MS_MISCERR, "GIF output is not available.",=0A=
-                              "getImageToVar()");=0A=
-          return(MS_FAILURE);=0A=
-        #endif=0A=
-        break;=0A=
-      case(MS_PNG):=0A=
-        #ifdef USE_GD_PNG=0A=
-          imgbytes =3D gdImagePngPtr(image->bytes, &size);=0A=
-        #else=0A=
-          msSetError(MS_MISCERR, "PNG output is not available.",=0A=
-                              "getImageToVar()");=0A=
-          return(MS_FAILURE);=0A=
-        #endif=0A=
-        break;=0A=
-      case(MS_JPEG):=0A=
-        #ifdef USE_GD_JPEG=0A=
-          imgbytes =3D gdImageJpegPtr(image->bytes, &size, =
self->imagequality);=0A=
-        #else=0A=
-          msSetError(MS_MISCERR, "JPEG output is not available.",=0A=
-                              "getImageToVar()");=0A=
-          return(MS_FAILURE);=0A=
-        #endif=0A=
-        break;=0A=
-      case(MS_WBMP):=0A=
-        #ifdef USE_GD_WBMP=0A=
-          imgbytes =3D gdImageWBMPPtr(image->bytes, &size, 1);=0A=
-        #else=0A=
-          msSetError(MS_MISCERR, "WBMP output is not available.",=0A=
-                              "getImageToVar()");=0A=
-          return(MS_FAILURE);=0A=
-        #endif=0A=
-        break;=0A=
-      default:=0A=
-        msSetError(MS_MISCERR, "Unknown output image type.",=0A=
-                              "getImageToVar()");=0A=
-        return(MS_FAILURE);=0A=
-    }=0A=
-=0A=
-=0A=
-    // Tcl implementation to set variable=0A=
-    #ifdef SWIGTCL8=0A=
-      imgobj =3D Tcl_NewByteArrayObj(imgbytes, size);=0A=
-      Tcl_IncrRefCount(imgobj);=0A=
-      Tcl_SetVar2Ex(SWIG_TCL_INTERP, varname, (char *)NULL, imgobj, =
flags);=0A=
-      Tcl_DecrRefCount(imgobj);=0A=
-      gdFree(imgbytes);=0A=
-      return MS_SUCCESS;=0A=
-    #endif=0A=
-=0A=
-    // Perl implementation to set variable=0A=
-    #ifdef SWIGPERL=0A=
-    #endif=0A=
-=0A=
-    // Python implementation to set variable=0A=
-    #ifdef SWIGPYTHON=0A=
-    #endif=0A=
-=0A=
-    // return failure for unsupported swig languages=0A=
-    msSetError(MS_MISCERR, "Unsupported scripting language.",=0A=
-                              "getImageToVar()");=0A=
-    return MS_FAILURE;=0A=
+=0A=
+/* Typemaps to allow function to return binary data */=0A=
+#ifdef SWIGPERL=0A=
+%typemap(in,numinputs=3D0) char *OUTPUT=0A=
+	{=0A=
+	}=0A=
+  =0A=
+	%typemap(argout) char *OUTPUT=0A=
+	{=0A=
+		SV *newscalar;=0A=
+	=0A=
+		newscalar =3D sv_newmortal();=0A=
+		sv_setpvn(newscalar, arg1->imagedata, arg1->imagelen);=0A=
+		ST(argvi++) =3D newscalar;=0A=
+	}=0A=
+#endif=0A=
+=0A=
+=0A=
+  void getImageToVar(imageObj *image, char *OUTPUT) {=0A=
+  =0A=
+	// return the rendered image data back to the script=0A=
+	void *imgtmp;=0A=
+	int imglen;=0A=
+=0A=
+	// Check interlace and transparency parameters=0A=
+	if(self->interlace)=0A=
+		gdImageInterlace(image->bytes, 1);=0A=
+=0A=
+	if(self->transparent)=0A=
+		gdImageColorTransparent(image->bytes, 0);=0A=
+=0A=
+	// Free any existing rendered image data=0A=
+	if (self->imagedata !=3D NULL) free(self->imagedata);=0A=
+=0A=
+=0A=
+	// generic code to get imgbytes, size=0A=
+	switch (self->imagetype) {=0A=
+		case(MS_GIF):=0A=
+		%#ifdef USE_GD_GIF=0A=
+			// GD /w gif doesn't have gdImageGifPtr()=0A=
+			msSetError(MS_MISCERR, "GIF output is not available.", =
"getImageToVar()");=0A=
+			return;=0A=
+		%#endif=0A=
+		break;=0A=
+=0A=
+		case(MS_PNG):=0A=
+		%#ifdef USE_GD_PNG=0A=
+			imgtmp =3D gdImagePngPtr(image->bytes, &imglen);=0A=
+			self->imagedata =3D malloc(imglen);=0A=
+			memcpy(self->imagedata, imgtmp, imglen);=0A=
+			self->imagelen =3D imglen;=0A=
+			gdFree(imgtmp);=0A=
+			return;=0A=
+		%#else=0A=
+		  msSetError(MS_MISCERR, "PNG output is not available.", =
"getImageToVar()");=0A=
+		%#endif=0A=
+		break;=0A=
+=0A=
+		case(MS_JPEG):=0A=
+		%#ifdef USE_GD_JPEG=0A=
+			imgtmp =3D gdImageJpegPtr(image->bytes, &imglen, self->imagequality);=0A=
+			self->imagedata =3D malloc(imglen);=0A=
+			memcpy(self->imagedata, imgtmp, imglen);=0A=
+			self->imagelen =3D imglen;=0A=
+			gdFree(imgtmp);=0A=
+		%#else=0A=
+			msSetError(MS_MISCERR, "JPEG output is not available.", =
"getImageToVar()");=0A=
+			return;=0A=
+		%#endif=0A=
+		break;=0A=
+=0A=
+		case(MS_WBMP):=0A=
+		%#ifdef USE_GD_WBMP=0A=
+			imgtmp =3D gdImageWBMPPtr(image->bytes, &imglen, 1);=0A=
+			self->imagedata =3D malloc(imglen);=0A=
+			memcpy(self->imagedata, imgtmp, imglen);=0A=
+			self->imagelen =3D imglen;=0A=
+			gdFree(imgtmp);=0A=
+		%#else=0A=
+			msSetError(MS_MISCERR, "WBMP output is not available.", =
"getImageToVar()");=0A=
+			return;=0A=
+		%#endif=0A=
+		break;=0A=
+=0A=
+		}=0A=
+=0A=
+		// return failure for unsupported swig languages=0A=
+		msSetError(MS_MISCERR, "Unsupported scripting language.", =
"getImageToVar()");=0A=
+		return;=0A=
   }=0A=
 =0A=
   labelCacheMemberObj *nextLabel() {=0A=

------=_NextPart_000_0000_01C322ED.6F0D6C10--


_______________________________________________
Mapserver-users mailing list
Mapserver-users@lists.gis.umn.edu
http://lists.gis.umn.edu/mailman/listinfo/mapserver-users