Annotation of parser3/src/classes/image.C, revision 1.9
1.1 paf 1: /** @file
2: Parser: @b image parser class.
3:
4: Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)
5:
6: Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)
7:
1.9 ! paf 8: $Id: image.C,v 1.8 2001/04/11 17:00:53 paf Exp $
1.1 paf 9: */
10:
11: #include "pa_config_includes.h"
12:
1.6 paf 13: #include <stdio.h>
14:
1.8 paf 15: #ifdef WIN32
16: # include "smtp/smtp.h"
17: #endif
18:
19: #include "gif.h"
1.6 paf 20:
1.1 paf 21: #include "pa_common.h"
22: #include "pa_request.h"
23: #include "pa_vfile.h"
24: #include "pa_vimage.h"
25:
26: // global var
27:
28: VStateless_class *image_class;
29:
30: // helpers
31:
32: class Measure_reader {
33: public:
1.3 paf 34: enum { READ_CHUNK_SIZE=0x400 }; // 1K
1.1 paf 35: typedef size_t (*Func)(void *& buf, size_t limit, void *info);
36:
37: Measure_reader(Func afunc, void *ainfo) :
38: func(afunc), info(ainfo),
39: chunk(0), offset(0), size(0) {
40: }
41:
42: size_t read(unsigned char *& buf, size_t limit) {
1.3 paf 43: if(offset+limit>size) // nothing left
44: if(offset==0 || limit==1) { // only one-byte continuations allowed
45: size=(*func)(chunk, READ_CHUNK_SIZE, info);
46: offset=0;
47: } else
48: return 0; // as if EOF
1.1 paf 49: if(!size) // EOF
50: return 0;
51:
52: // something left
53: size_t read_size=min(offset+limit, size)-offset;
54: buf=((unsigned char *)chunk)+offset;
55: offset+=read_size;
56: return read_size;
57: }
58:
59: private:
60: Func func;
61: void *info;
62:
63: void *chunk;
64: size_t offset;
65: size_t size;
66: };
67:
68: // GIF
69: struct GIF_Header {
70: char type[3]; // 'GIF'
71: char version[3];
72: unsigned char width[2];
73: unsigned char height[2];
74: char dif;
75: char fonColor;
76: char nulls;
77: };
78:
79: // JPEG
80: struct JFIF_Header {
81: char length[2]; // length of JFIF segment marker
82: char identifier[5]; // JFIF identifier
83: char version[2]; // version
84: char units; // units X of Y pixel density
85: char xdensity[2]; // X pixel density
86: char ydensity[2]; // X pixel density
87: char xthumbnails; // width of thumbnails
88: char ythumbnails; // height of thumbnails
89: char reserved; // reserved
90: };
91: struct JPG_Frame {
92: char length[2]; // length of image marker
93: char data; // data precision of bits/sample
94: char height[2]; // image height
95: char width[2]; // image width
96: char numComponents; // number of color components
97: };
98:
99: //
100:
101: inline short bytes_to_int(unsigned char HI, unsigned char LO) {
102: return (short)((HI<<8) + LO);
103: }
104:
105: void measure_gif(Pool& pool, const String *origin_string,
106: Measure_reader& reader, int& width, int& height) {
107:
108: unsigned char *buf;
109: const int head_size=sizeof(GIF_Header);
110: if(reader.read(buf, head_size)<head_size)
111: PTHROW(0, 0,
112: origin_string,
113: "broken GIF header - file size is less then %d bytes", head_size);
114:
115: GIF_Header& screenD=*reinterpret_cast<GIF_Header *>(buf);
116: if(strncmp(screenD.type, "GIF", 3)!=0)
117: PTHROW(0, 0,
118: origin_string,
119: "bad image file - GIF signature not found");
120:
121: width=bytes_to_int(screenD.width[1], screenD.width[0]);
122: height=bytes_to_int(screenD.height[1], screenD.height[0]);
123: }
124:
125: void measure_jpeg(Pool& pool, const String *origin_string,
126: Measure_reader& reader, int& width, int& height) {
1.2 paf 127: // JFIF format markers
128: const unsigned char SOI=0xD8;
129: const unsigned char EOI=0xD9;
130: const unsigned char APP0=0xE0;
131: const unsigned char SOF0=0xC0;
132: const unsigned char SOF2=0xC2;
133: const unsigned char COM=0xFE;
134:
1.1 paf 135: unsigned char *screenD_buf;
1.3 paf 136: unsigned char *h_buf=0;
1.1 paf 137:
138: bool flag=false;
139:
140: unsigned char *prefix;
141: const prefix_size=2;
142: if(reader.read(prefix, prefix_size)<prefix_size)
143: PTHROW(0, 0,
144: origin_string,
145: "broken JPEG file - size is less then %d bytes", prefix_size);
146:
147: if(((unsigned char *)prefix)[1]!=SOI)
148: PTHROW(0, 0,
149: origin_string,
150: "broken JPEG file - second byte of header is not 0x%02X", SOI);
151:
152: unsigned char zero=0;
153: unsigned char *marker=&zero;
154:
155: do {
156: while((*marker)!=0xFF)
157: if(reader.read(marker, sizeof(char))<=0) break;
158: if(reader.read(marker, sizeof(char))<=0) break;
159: switch(*marker) {
160: case EOI:
161: marker=&zero;
162: break;
163: case APP0:
164: if(!flag) {
165: flag=true;
166: if(reader.read(screenD_buf, sizeof(JFIF_Header)) < sizeof(JFIF_Header))
167: break;
168: JFIF_Header& screenD=*reinterpret_cast<JFIF_Header *>(screenD_buf);
169: if((bytes_to_int(screenD.length[0], screenD.length[1]) < 16) ||
1.2 paf 170: strcasecmp(screenD.identifier, "JFIF")) flag=false;
1.1 paf 171: }
172: break;
173: case SOF0:
174: case SOF2:
175: if(reader.read(h_buf, sizeof(JPG_Frame))<sizeof(JPG_Frame))
176: flag=false;
177: break;
178: default: break;
179: }
180: } while(*marker!=EOI);
181:
1.3 paf 182: if(flag && h_buf) {
1.1 paf 183: JPG_Frame& h=*reinterpret_cast<JPG_Frame *>(h_buf);
184: width=bytes_to_int(h.width[0], h.width[1]);
185: height=bytes_to_int(h.height[0], h.height[1]);
186: } else
187: PTHROW(0, 0,
188: origin_string,
189: "broken JPEG file - APP0 frame not found");
190: }
191:
192: // measure center
193:
194: void measure(Pool& pool, const String& file_name,
195: Measure_reader& reader, int& width, int& height) {
196: if(const char *cext=strrchr(file_name.cstr(), '.')) {
197: cext++;
198: if(strcasecmp(cext, "GIF")==0)
199: measure_gif(pool, &file_name, reader, width, height);
200: else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0)
201: measure_jpeg(pool, &file_name, reader, width, height);
202: else
203: PTHROW(0, 0,
204: &file_name,
205: "unhandled image file name extension '%s'", cext);
206: } else
207: PTHROW(0, 0,
208: &file_name,
209: "can not determine image type - no file name extension");
210: }
211:
212: // read from somewhere
213:
214: struct Read_mem_info {
215: unsigned char *ptr;
216: unsigned char *eof;
217: };
218: static size_t read_mem(void*& buf, size_t limit, void *info) {
219: Read_mem_info& rmi=*static_cast<Read_mem_info *>(info);
220: buf=rmi.ptr;
221: size_t read_size=min(limit, (size_t)(rmi.eof-rmi.ptr));
222: rmi.ptr+=read_size;
223: return read_size;
224: }
225:
226: struct Read_disk_info {
227: const String *file_spec;
228: size_t offset;
229: };
230: static size_t read_disk(void*& buf, size_t limit, void *info) {
231: Read_disk_info& rdi=*static_cast<Read_disk_info *>(info);
232: Pool& pool=rdi.file_spec->pool();
233:
234: size_t read_size;
235: file_read(pool, *rdi.file_spec,
236: buf, read_size,
237: false/*as_text*/,
238: true/*fail_on_read_problem*/,
239: rdi.offset, limit);
240:
241: rdi.offset+=read_size;
242: return read_size;
243: }
244:
245: // methods
246:
247: /// ^image:measure[DATA]
248: static void _measure(Request& r, const String& method_name, Array *params) {
249: Pool& pool=r.pool();
250:
251: Value& data=*static_cast<Value *>(params->get(0));
252: // forcing [this body type]
253: r.fail_if_junction_(true, data, method_name, "data must not be code");
254:
255: void *info; Measure_reader::Func read_func;
256: Read_mem_info read_mem_info;
257: Read_disk_info read_disk_info;
258: const String *file_name;
259: if(data.is_string()) {
260: file_name=data.get_string();
261: read_disk_info.file_spec=&r.absolute(*file_name);
262: read_disk_info.offset=0;
263: info=&read_disk_info; read_func=read_disk;
264: } else {
265: const VFile& vfile=*data.as_vfile();
266: file_name=&static_cast<Value *>(vfile.fields().get(*name_name))->as_string();
267: read_mem_info.ptr=(unsigned char *)vfile.value_ptr();
268: read_mem_info.eof=read_mem_info.ptr+vfile.value_size();
269: info=&read_mem_info; read_func=read_mem;
270: }
271:
272: Measure_reader reader(read_func, info);
273: int width, height;
274: measure(pool, *file_name, reader, width, height);
275:
1.6 paf 276: static_cast<VImage *>(r.self)->set(file_name, width, height);
1.1 paf 277: }
278:
1.4 paf 279: struct Attrib_info {
280: String *tag;
281: Hash *skip;
282: };
1.3 paf 283: static void append_attrib_pair(const Hash::Key& key, Hash::Val *val, void *info) {
1.4 paf 284: Attrib_info& ai=*static_cast<Attrib_info *>(info);
285:
286: if(ai.skip && ai.skip->get(key))
287: return;
288:
1.3 paf 289: Value& value=*static_cast<Value *>(val);
290: // src="a.gif" width=123 ismap[=-1]
1.4 paf 291: *ai.tag << " " << key;
1.6 paf 292: if(value.is_string() || value.as_double()>=0)
293: *ai.tag << "=\"" << value.as_string() << "\"";
1.3 paf 294: }
295: /// ^image.html[]
296: /// ^image.html[hash]
297: static void _html(Request& r, const String& method_name, Array *params) {
298: Pool& pool=r.pool();
299:
300: String tag(pool);
301: tag << "<img";
1.4 paf 302:
303: Hash& fields=static_cast<VImage *>(r.self)->fields();
1.5 paf 304: Hash *attribs=0;
1.4 paf 305:
1.8 paf 306: if(params)
1.5 paf 307: if(attribs=static_cast<Value *>(params->get(0))->get_hash()) {
1.4 paf 308: Attrib_info attrib_info={&tag, 0};
1.5 paf 309: attribs->for_each(append_attrib_pair, &attrib_info);
1.4 paf 310: } else
311: PTHROW(0, 0,
312: &method_name,
1.5 paf 313: "attributes must be must be hash");
1.4 paf 314:
1.5 paf 315: Attrib_info attrib_info={&tag, attribs};
1.4 paf 316: fields.for_each(append_attrib_pair, &attrib_info);
1.6 paf 317: tag << " />";
1.3 paf 318: r.write_pass_lang(tag);
319: }
1.8 paf 320:
1.6 paf 321: /// ^image.load[background.gif]
322: static void _load(Request& r, const String& method_name, Array *params) {
323: Pool& pool=r.pool();
324:
325: Value& vfile_name=*static_cast<Value *>(params->get(0));
326: // forcing [this body type]
327: r.fail_if_junction_(true, vfile_name, method_name, "file name must not be code");
328: const String& file_name=vfile_name.as_string();
329:
330: const char *file_name_cstr=r.absolute(file_name).cstr(String::UL_FILE_NAME);
1.9 ! paf 331: gdImage image(pool);
1.6 paf 332: if(FILE *f=fopen(file_name_cstr, "rb")) {
1.9 ! paf 333: image.CreateFromGif(f);
! 334: int width=image.SX();
! 335: int height=image.SY();
1.6 paf 336: fclose(f);
337:
1.9 ! paf 338: static_cast<VImage *>(r.self)->set(&file_name, width, height, &image);
1.6 paf 339: } else
340: PTHROW(0, 0,
341: &method_name,
342: "can not open background image '%s'", file_name_cstr);
343: }
344:
345: /// ^image.create[width;height] bgcolor=white
346: /// ^image.create[width;height;bgcolor]
347: static void _create(Request& r, const String& method_name, Array *params) {
348: Pool& pool=r.pool();
349:
350: int width=(int)r.process(*static_cast<Value *>(params->get(0))).as_double();
351: int height=(int)r.process(*static_cast<Value *>(params->get(1))).as_double();
1.8 paf 352: int bgcolor_value=0xffFFff;
1.6 paf 353: if(params->size()>2)
354: bgcolor_value=
355: (int)r.process(*static_cast<Value *>(params->get(2))).as_double();
1.8 paf 356: gdImage *image=new(pool) gdImage(pool);
357: image->Create(width, height);
358: image->FilledRectangle(0, 0, width-1, height-1, image->Color(bgcolor_value));
359: static_cast<VImage *>(r.self)->set(0, width, height, image);
1.6 paf 360: }
361:
362: /// ^image.gif[]
363: /// ^image.gif[user-file-name]
1.8 paf 364: /// @test gdImageDestroy make gd pooled!
1.6 paf 365: static void _gif(Request& r, const String& method_name, Array *params) {
366: Pool& pool=r.pool();
367:
1.9 ! paf 368: gdImage *image=static_cast<VImage *>(r.self)->image;
1.8 paf 369: if(!image)
1.6 paf 370: PTHROW(0, 0,
371: &method_name,
372: "does not contain image");
373:
374: char *file_name_cstr=0;
1.8 paf 375: if(params) {
1.6 paf 376: Value& vfile_name=*static_cast<Value *>(params->get(0));
377: // forcing [this body type]
378: r.fail_if_junction_(true, vfile_name, method_name, "file name must not be code");
379:
380: file_name_cstr=vfile_name.as_string().cstr(String::UL_FILE_NAME);
381: }
382: // could _ but don't thing it's wise to use $image.src for vfile.name
383:
384: VFile& vfile=*new(pool) VFile(pool);
385: String& image_gif=*new(pool) String(pool, "image/gif");
1.8 paf 386: vfile.set(false/*not tainted*/, z, z, file_name_cstr, &image_gif);
1.6 paf 387:
388: r.write_no_lang(vfile);
389: }
390:
1.1 paf 391: // initialize
392: void initialize_image_class(Pool& pool, VStateless_class& vclass) {
393: // ^image:measure[DATA]
394: vclass.add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
1.3 paf 395:
396: /// ^image.html[]
397: /// ^image.html[hash]
398: vclass.add_native_method("html", Method::CT_DYNAMIC, _html, 0, 1);
1.6 paf 399:
400: /// ^image.load[background.gif]
1.8 paf 401: vclass.add_native_method("load", Method::CT_DYNAMIC, _load, 1, 1);
1.6 paf 402:
403: /// ^image.create[width;height] bgcolor=white
404: /// ^image.create[width;height;bgcolor]
405: vclass.add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);
406:
407: /// ^image.gif[]
408: /// ^image.gif[user-file-name]
409: vclass.add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 1);
1.1 paf 410: }
E-mail: