1 /* *************************************************************************
2                           gdlsvgstream.cpp  -  graphic stream SVG
3                              -------------------
4     begin                : December 26 2008
5     copyright            : (C) 2002 by Sylwester Arabas
6     email                : slayoo@users.sf.net
7  ***************************************************************************/
8 
9 /* *************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 
19 #include "includefirst.hpp"
20 #include "gdlsvgstream.hpp"
21 
22 #ifdef _WIN32
23 #include <io.h>
24 #include <fcntl.h>
25 #include <share.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #else
29 #include <unistd.h>
30 #endif
31 
32 using namespace std;
33 
Init()34 void GDLSVGStream::Init()
35 {
36    plstream::init();
37 }
38 
39 // this lookup table defines the base64 encoding
40 static const string svgBase64Table("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
41 static const char svgfillchar = '=';
42 
encodesvg(unsigned char buf[],unsigned int len)43 	string encodesvg(unsigned char buf[], unsigned int len) {
44 		string             ret;
45 		if(len==0)
46 			return "";
47 		ret.reserve((len-1)/3*4 + 4 + 1);
48 
49 		for (string::size_type i = 0; i < len; ++i)
50 		{
51 			char c;
52 			c = (buf[i] >> 2) & 0x3f;
53 			ret.append(1, svgBase64Table[c]);
54 			c = (buf[i] << 4) & 0x3f;
55 			if (++i < len)
56 				c |= (buf[i] >> 4) & 0x0f;
57 
58 			ret.append(1, svgBase64Table[c]);
59 			if (i < len)
60 			{
61 				c = (buf[i] << 2) & 0x3f;
62 				if (++i < len)
63 					c |= (buf[i] >> 6) & 0x03;
64 
65 				ret.append(1, svgBase64Table[c]);
66 			}
67 			else
68 			{
69 				++i;
70 				ret.append(1, svgfillchar);
71 			}
72 
73 			if (i < len)
74 			{
75 				c = buf[i] & 0x3f;
76 				ret.append(1, svgBase64Table[c]);
77 			}
78 			else
79 			{
80 				ret.append(1, svgfillchar);
81 			}
82 		}
83 
84 		return(ret);
85 	}
86 
87 #ifdef USE_PNGLIB
88 
svg_to_png64(int width,int height,png_byte * image,int bit_depth,int nbpp,int whattype,int * error)89 std::string GDLSVGStream::svg_to_png64(int width,int height,
90 	    png_byte *image, int bit_depth, int nbpp, int whattype, int *error)
91 {
92    static std::string tmpstr;
93    tmpstr.clear();
94    FILE *fp;
95    png_structp png_ptr;
96    png_infop info_ptr;
97    static const int np=pls->ncol0;
98    int k;
99    png_bytep *row_pointers;
100    png_colorp  palette;
101    char line[512];
102    char filename[512];
103    char *in,*out;
104    int lin, lout;
105    int fd;
106    *error = 0;
107    /* open a temporary file */
108    sprintf(filename,"%sgdlsvgpng64.XXXXXX",getenv("IDL_TMPDIR")); //Insecure, check!
109 #ifdef _WIN32
110    static const char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
111    char *XXXXXX = &filename[strlen(filename)-6];
112    srand((unsigned int)time(NULL));
113    for (int i=0; i<6; i++)
114       XXXXXX[i] = letters[rand()%63];
115 
116 /* _sopen replaces _sopen_s(&fd, ...) so that XP can still run without fetching a new MSVCRT. !! */
117 /* Also, sopen_s is not in all win32 compilers' io.h header */
118 
119    fd = _sopen(filename, O_RDWR | O_CREAT | O_EXCL, _SH_DENYNO, _S_IREAD | _S_IWRITE);
120 #else
121    fd=mkstemp(filename);
122 #endif
123 
124    if (fd==-1)
125    {
126      *error=1;
127        cerr<<"unable to create temporary file \""<<filename<<"\" for svg image"<<endl;
128        return NULL;
129    }
130 
131    fp = fdopen(fd,"w+");
132    if (fp == NULL)
133    {
134      *error=1;
135        cerr<<"unable to open temporary file \""<<filename<<"\" for svg image"<<endl;
136        return NULL;
137    }
138 
139    /* Create and initialize the png_struct with the desired error handler
140     * functions.  If you want to use the default stderr and longjump method,
141     * you can supply NULL for the last three parameters.  We also check that
142     * the library version is compatible with the one used at compile time,
143     * in case we are using dynamically linked libraries.  REQUIRED.
144     */
145    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
146 
147    if (png_ptr == NULL)
148    {
149        fclose(fp);
150        unlink(filename);
151      *error=1;
152        return NULL;
153    }
154 
155    /* Allocate/initialize the image information data.  REQUIRED */
156    info_ptr = png_create_info_struct(png_ptr);
157    if (info_ptr == NULL)
158    {
159        fclose(fp);
160        unlink(filename);
161        png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
162      *error=1;
163        return NULL;
164    }
165 
166    /* Set error handling.  REQUIRED if you aren't supplying your own
167     * error hadnling functions in the png_create_write_struct() call.
168     */
169    if (setjmp(png_jmpbuf(png_ptr)))
170    {
171        /* If we get here, we had a problem reading the file */
172        fclose(fp);
173        unlink(filename);
174        png_destroy_write_struct(&png_ptr, &info_ptr);
175      *error=1;
176        return NULL;
177    }
178 
179 
180    /* set up the output control if you are using standard C streams */
181    png_init_io(png_ptr, fp);
182 
183    /* Set the image information here.  Width and height are up to 2^31,
184     * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
185     * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
186     * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
187     * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
188     * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
189     * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
190     */
191    png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, whattype,
192 //either one of:		PNG_COLOR_TYPE_PALETTE,
193 //                      PNG_COLOR_TYPE_GRAY,
194 //                      PNG_COLOR_TYPE_RGB,
195    PNG_INTERLACE_NONE,
196 		PNG_COMPRESSION_TYPE_BASE,
197 		PNG_FILTER_TYPE_BASE);
198 
199    /* set the palette if there is one.  REQUIRED for indexed-color images */
200    bool haspalette=(whattype == PNG_COLOR_TYPE_PALETTE);
201    if (haspalette) {
202      palette = (png_colorp)malloc(np*sizeof(png_color));
203    /* ... set palette colors ... */
204    for (k=0;k<np;k++) {
205      palette[k].red=pls->cmap0[k].r;
206      palette[k].green=pls->cmap0[k].g;
207      palette[k].blue=pls->cmap0[k].b;
208      }
209      png_set_PLTE(png_ptr, info_ptr, palette, np);
210    }
211 
212    /* Write the file header information.  REQUIRED */
213    png_write_info(png_ptr, info_ptr);
214 
215   /* The easiest way to write the image (you may have a different memory
216     * layout, however, so choose what fits your needs best).  You need to
217     * use the first method if you aren't handling interlacing yourself.
218     */
219    row_pointers=(png_bytepp)malloc(height*sizeof(png_bytep));
220    for (k = 0; k < height; ++k) {
221      row_pointers[k] = (png_bytep)(image + k*width*nbpp*sizeof(png_byte));
222    }
223    /* Write it */
224    png_write_image(png_ptr, row_pointers);
225 
226    /* It is REQUIRED to call this to finish writing the rest of the file */
227    png_write_end(png_ptr, info_ptr);
228    fflush(fp); /* just in case we core-dump before finishing... */
229 
230    /* if you malloced the palette, free it here */
231    if (haspalette){
232      free(palette);
233    }
234    free(row_pointers);
235 
236    /* clean up after the write, and free any memory allocated */
237    png_destroy_write_struct(&png_ptr, &info_ptr);
238    /* rewind */
239    rewind(fp);
240    /* count size. Note that string functions do not work since NULL is valid */
241    lout=0;
242    while ((lin=(fread((void *)line, (size_t)1, (size_t)512, fp))) != 0) {
243        lout+=lin;
244    }
245    /* compute in and out length */
246    /* allocate in */
247    lin=lout+1;
248    in=(char*)calloc(lin,sizeof(char));
249    /* allocate out */
250    lout=((lout + 2) / 3 * 4) + 1;
251    /* bufread, convert, back to char */
252    /* rewind */
253    rewind(fp);
254    lin=fread((void *)in, (size_t)1, (size_t)lin, fp);
255    tmpstr = encodesvg((unsigned char*)in, lin);
256    free(in);
257    /* close and destroy fp, return */
258    fclose(fp);
259    unlink(filename);
260    return tmpstr;
261 }
PaintImage(unsigned char * idata,PLINT nx,PLINT ny,DLong * pos,DLong trueColorOrder,DLong channel)262 bool  GDLSVGStream::PaintImage(unsigned char *idata, PLINT nx, PLINT ny, DLong *pos,
263 		   DLong trueColorOrder, DLong channel) {
264   c_plflush();
265   if (channel > 0) {
266     cerr << "TV+SVG device: Value of CHANNEL (use TRUE instead) is out of allowed range. (FIXME!)" << endl;
267     return false;
268   }
269   if (trueColorOrder > 1) {
270     cerr << "TV+SVG device: True Color images must be [3,*,*] only. (FIXME!)" << endl;
271     return false;
272   }
273   pls->bytecnt += fprintf(pls->OutFile,"<image preserveAspectRatio=\"none\" x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" xlink:href=\"data:image/svg;base64,",
274           pos[0],pos[2],pos[1],pos[3]);
275   int error;
276   std::string ret ;
277   // do we need rotation handling?
278 //  if ( pls->diorot == 1.0 ) {
279 //  } else {
280 //  }
281   if ( channel == 0 ) {
282     if ( trueColorOrder == 0 ) { //indexed value 0->255: image
283       ret = GDLSVGStream::svg_to_png64( nx, ny, idata, 8, 1 ,PNG_COLOR_TYPE_PALETTE, &error );
284       if ( error == 0 ) pls->bytecnt += fprintf( pls->OutFile, "%s", ret.c_str( ) );
285      } else {
286       switch ( trueColorOrder ) {
287         case 1:
288           ret = GDLSVGStream::svg_to_png64( nx, ny, idata, 8, 3 ,PNG_COLOR_TYPE_RGB, &error );
289           if ( error == 0 ) pls->bytecnt += fprintf( pls->OutFile, "%s", ret.c_str( ) );
290           break;
291         case 2:
292           break;
293         case 3:
294           break;
295       }
296     }
297 //  } else { //channel = 1 to 3
298   }
299   pls->bytecnt += fprintf(pls->OutFile,"\"/>\n");
300   return true;
301 #undef BUFLEN
302 }
303 #else
PaintImage(unsigned char * idata,PLINT nx,PLINT ny,DLong * pos,DLong trueColorOrder,DLong channel)304 bool  GDLSVGStream::PaintImage(unsigned char *idata, PLINT nx, PLINT ny, DLong *pos,
305 		   DLong trueColorOrder, DLong channel) {return false;}
306 #endif
307