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