Annotation of parser3/src/classes/image.C, revision 1.6
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.6 ! paf 8: $Id: image.C,v 1.5 2001/04/11 07:44:12 paf Exp $
1.1 paf 9: */
10:
11: #include "pa_config_includes.h"
12:
1.6 ! paf 13: #include <stdio.h>
! 14:
1.1 paf 15: #ifdef WIN32
16: # include "smtp/smtp.h"
17: #endif
18:
1.6 ! paf 19: #include "gif.h"
! 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.6 ! 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: }
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);
! 331: if(FILE *f=fopen(file_name_cstr, "rb")) {
! 332: gdImagePtr image=gdImageCreateFromGif(f);
! 333: int width=gdImageSX(image);
! 334: int height=gdImageSY(image);
! 335: fclose(f);
! 336:
! 337: static_cast<VImage *>(r.self)->set(&file_name, width, height, image);
! 338: } else
! 339: PTHROW(0, 0,
! 340: &method_name,
! 341: "can not open background image '%s'", file_name_cstr);
! 342: }
! 343:
! 344: inline void v2rgb(int v, int& r, int& g, int& b) {
! 345: r=v>>8*2 & 0xFF;
! 346: g=v>>8*1 & 0xFF;
! 347: b=v & 0xFF;
! 348: }
! 349:
! 350: /// ^image.create[width;height] bgcolor=white
! 351: /// ^image.create[width;height;bgcolor]
! 352: static void _create(Request& r, const String& method_name, Array *params) {
! 353: Pool& pool=r.pool();
! 354:
! 355: int width=(int)r.process(*static_cast<Value *>(params->get(0))).as_double();
! 356: int height=(int)r.process(*static_cast<Value *>(params->get(1))).as_double();
! 357: int bgcolor_value=0xFFFFFF;
! 358: if(params->size()>2)
! 359: bgcolor_value=
! 360: (int)r.process(*static_cast<Value *>(params->get(2))).as_double();
! 361: gdImagePtr image=gdImageCreate(width, height);
! 362: {
! 363: int r,g,b; v2rgb(bgcolor_value, r,g,b);
! 364: gdImageFilledRectangle(image, 0, 0, width-1, height-1,
! 365: gdImageColorExact(image, r,g,b));
! 366: }
! 367: static_cast<VImage *>(r.self)->set(0, width, height, image);
! 368: }
! 369:
! 370: /// ^image.gif[]
! 371: /// ^image.gif[user-file-name]
! 372: /// @test gdImageDestroy make gd pooled!
! 373: static void _gif(Request& r, const String& method_name, Array *params) {
! 374: Pool& pool=r.pool();
! 375:
! 376: gdImagePtr image=static_cast<VImage *>(r.self)->image;
! 377: if(!image)
! 378: PTHROW(0, 0,
! 379: &method_name,
! 380: "does not contain image");
! 381:
! 382: char *file_name_cstr=0;
! 383: if(params) {
! 384: Value& vfile_name=*static_cast<Value *>(params->get(0));
! 385: // forcing [this body type]
! 386: r.fail_if_junction_(true, vfile_name, method_name, "file name must not be code");
! 387:
! 388: file_name_cstr=vfile_name.as_string().cstr(String::UL_FILE_NAME);
! 389: }
! 390: // could _ but don't thing it's wise to use $image.src for vfile.name
! 391:
! 392: VFile& vfile=*new(pool) VFile(pool);
! 393: String& image_gif=*new(pool) String(pool, "image/gif");
! 394: vfile.set(false/*not tainted*/, z, z, file_name_cstr, &image_gif);
! 395:
! 396: r.write_no_lang(vfile);
! 397: }
! 398:
1.1 paf 399: // initialize
400: void initialize_image_class(Pool& pool, VStateless_class& vclass) {
401: // ^image:measure[DATA]
402: vclass.add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
1.3 paf 403:
404: /// ^image.html[]
405: /// ^image.html[hash]
406: vclass.add_native_method("html", Method::CT_DYNAMIC, _html, 0, 1);
1.6 ! paf 407:
! 408: /// ^image.load[background.gif]
! 409: vclass.add_native_method("load", Method::CT_DYNAMIC, _load, 1, 1);
! 410:
! 411: /// ^image.create[width;height] bgcolor=white
! 412: /// ^image.create[width;height;bgcolor]
! 413: vclass.add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);
! 414:
! 415: /// ^image.gif[]
! 416: /// ^image.gif[user-file-name]
! 417: vclass.add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 1);
1.1 paf 418: }
E-mail: